Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Anotaciones y Rutas con Comodín

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Es muy sencillo crear una ruta en YAML que apunte a una función del controlador. Pero hay una forma aun más simple de crear rutas... y me encanta. Se llama: anotaciones.

Primero, comenta la ruta en YAML. Básicamente, borrala. Para comprobar que no funciona, refresca la homepage. Asi es! Regresó a la página de bienvenida.

#index:
# path: /
# controller: App\Controller\QuestionController::homepage

Instalación Soporte a Anotaciones

Las anotaciones son un formato especial de configuración y el soporte a anotaciones no es un standard en nuestra pequeña aplicación de Symfony. Y... eso está bien! De hecho, esa es toda la filosofía de Symfony: empieza pequeño y agrega funcionalidades cuando las necesites.

Para agregar soporte a anotaciones, vamos a utilizar Composer para requerir una nueva librería. Si aun no tienes Composer instalado, ve a https://getcomposer.org.

Una vez que lo instales, corre:

composer require annotations

Si estás familiarizado con Composer, el nombre de la librería se te ha de hacer extraño. Y en realidad, instaló una librería totalmente diferente: sensio/framework-extra-bundle. Casi al final del comando, menciona algo sobre dos recetas. Hablaremos sobre ello próximamente: es parte de lo que hace especial a Symfony.

Agregando Rutas con Anotaciones

En fin, ya que el soporte a anotaciones está instalado, podemos agregar de vuelta nuestra ruta usando anotaciones. Que significa eso? Arriba de la función del controlador, escribe /** y presiona enter para crear una sección PHPDoc Luego escribe @Route y autocompleta la del componente Routing. Tal como la otra vez, PhpStorm agregó automáticamente el use statement en la parte de arriba de la clase.

Dentro de los paréntesis, escribe "/".

... lines 1 - 5
use Symfony\Component\Routing\Annotation\Route;
class QuestionController
{
/**
* @Route("/")
*/
public function homepage()
{
... line 15
}
}

Eso es todo! Cuando el usuario vaya a la homepage, se va a ejecutar la función abajo de esto. Me encantan las anotaciones porque son simples de leer y mantienen la ruta y controlador uno junto del otro. Y si... las anotaciones son literalmente configuración dentro de comentarios de PHP. Si no te gustan, siempre puedes utilizar YAML o XML: Symfony es super flexible. Desde el punto de vista del rendimiento, todos los formatos son lo mismo.

Ahora cuando refrescamos la homepage... estamos de vuelta!

Una Segunda Ruta y Controlador

Esta página eventualmente va a listar algunas preguntas recientes. Cuando le das click a una pregunta en específico, necesitará su propia página. Vamos a crear una segunda ruta y controlador para ello. Como? creando un segundo metodo. Que tal: public function show().

... lines 1 - 7
class QuestionController
{
... lines 10 - 20
public function show()
{
... line 23
}
}

Arriba de esto, agrega @Route() y asigna la URL a, que te parece, /questions/how-to-tie-my-shoes-with-magic. Eso seria grandioso!

... lines 1 - 7
class QuestionController
{
... lines 10 - 17
/**
* @Route("/questions/how-to-tie-my-shoes-with-magic")
*/
public function show()
{
... line 23
}
}

Adentro, justo como la última vez, retorna una nueva respuesta: la de HttpFoundation.

La página futura para mostrar preguntas

... lines 1 - 7
class QuestionController
{
... lines 10 - 17
/**
* @Route("/questions/how-to-tie-my-shoes-with-magic")
*/
public function show()
{
return new Response('Future page to show a question!');
}
}

Vamos a probarla! Copia la URL, ve a tu navegador, pega y... funciona! Acabamos de crear una segunda página... en menos de un minuto.

El Controlador Frontal: Trabajando Detrás De Cámaras

Por cierto, no importa a cual URL vayamos - como esta o la homepage - el archivo PHP que nuestro servidor web ejecuta es index.php. Es como si fuéramos a /index.php/questions /how-to-tie-my-shoes-with-magic. La única razón por la que no necesitas escribir index.php en la URL es porque nuestro servidor web local está configurado para ejecutar index.php automáticamente. En producción, tu configuración de Nginx o Apache debe de hacer lo mismo. Revisa la documentación de Symfony para aprender como hacerlo.

Rutas con Comodín

Eventualmente, vamos a tener una base de datos llena de preguntas. Y entonces, no, no vamos a crear manualmente una ruta por cada pregunta. En su lugar, podemos hacer más inteligente esta ruta. Reemplaza la parte how-to-tie-my-shoes-with-magic por {slug}.

Cuando pones algo entre llaves dentro de una ruta, se convierte en comodín. Esta ruta ahora aplica a /questions/LO-QUE-SEA. El nombre {slug} no es importante: pudimos haber puesto lo que sea... por ejemplo {slugulusErectus}! No hace ninguna diferencia.

Pero, como sea que llamemos a este comodín - ejemplo {slug} - ahora nos permite tener un argumento en nuestro controlador con el mismo nombre: $slug... el cual será asignado con el valor de esa parte de la URL.

... lines 1 - 7
class QuestionController
{
... lines 10 - 17
/**
* @Route("/questions/{slug}")
*/
public function show($slug)
{
... lines 23 - 26
}
}

Utilicemoslo para hacer mas elegante a nuestra página! Usemos sprintf(), escribe " la pregunta" y agrega %s como comodín. Pasa $slug como comodín.

... lines 1 - 7
class QuestionController
{
... lines 10 - 17
/**
* @Route("/questions/{slug}")
*/
public function show($slug)
{
return new Response(sprintf(
'Future page to show the question "%s"!',
$slug
));
}
}

Bien! Cambia al navegador, refresca y... me encanta! Cambia la URL a /questions /accidentally-turned-cat-into-furry-shoes y... eso también funciona!

En el futuro, vamos a utilizar el $slug para extraer la pregunta de la base de datos. Pero como aun no llegamos ahí, usaré str_replace() ... y ucwords() solo para hacerlo un poco mas elegante. Aun es pronto, pero la página ya comienza a estar viva!

... lines 1 - 7
class QuestionController
{
... lines 10 - 20
public function show($slug)
{
return new Response(sprintf(
... line 24
ucwords(str_replace('-', ' ', $slug))
));
}
}

A continuación, nuestra aplicación esconde un secreto! Una pequeña línea de comandos ejecutable que está llena de beneficios.

Leave a comment!

38
Login or Register to join the conversation
Robertino V. Avatar
Robertino V. Avatar Robertino V. | posted hace 2 años

@maxii123 I respectfully disagree somehow; for someone starting into developing yes it might be a bit expensive but even for a beginner dev 70 EUR (50 EUR third year) per year is not expensive at all; if one doesn't do this much in a year it is hopeless :-)

3 Reply
Leon Avatar

Is there a VSCode equivalent to the auto-completion phpStorm does?

1 Reply

Hey Mike,

Unfortunately, I personally use PhpStorm, so don't know much about VSCode. I believe that the most powerful autocompletion for Symfony project available in PhpStorm only thanks to Symfony Plugin, but I might be wrong. Probably some users who use VSCode could give you some tips about it.

Cheers!

Reply
Oliver-W Avatar
Oliver-W Avatar Oliver-W | posted hace 2 años

I've got 2 Symfonyprojects with annotations - on the same XAMPP. On one of them the routing works on the other one I get a 404 for a route other than "/". If I call server/index.php/route it works. I can't find the error. Thx for any hints.

Btw: there is no .htaccess on none of the two projects. I work with vhosts.

@maxii123 Me tooooo, I love PHPStorm!

Arrrrghhh: typical I/O-Error (Idiot Operator). There is a .htaccess for the working project. But not in the root of the project but, of corse, in the public folder!!!

1 Reply

Lol Oliver W., it happens :)

By the way, there is a library that helps you with the .htaccess file and here are some good recomendations about how configuring your Apache server https://symfony.com/doc/cur...

I hope it helps. Cheers!

1 Reply
Jitze Avatar

my @Route doesn't work? someone has this problem before

Reply

Hey Jitze,

Sometimes it happens :) Please, double-check the namespace, do you this "use Symfony\Component\Routing\Annotation\Route;" line in the controller? If no - please, add it and try again. If still nothing - let us know, but provide a bit more info please, do you see any errors? How is your PHPDoc looks like exactly?

Cheers!

Reply
Jitze Avatar

The solution for this problems is to install the annotations as package. Cause the routing.yaml has only defined the specific controllers. For this project I must configure the annotations for all controllers. https://symfony.com/doc/cur....

Reply

Hey Jitze

Yes, you need to install the annotations library (but not necessarily as a package), and then, in the routing.yaml file, set the type key to annotation, and all the controllers living under the specified directory will have annotations support

Cheers!

Reply
sadikoff Avatar sadikoff | SFCASTS | posted hace 1 año | edited

Hey @disqus_uUZdE4XYyx

str_replace is not the best solution for it, yeah you can use it in your template when generate the link with {{ path() }} function, but honestly it's better to use slug system for urls

Cheers

Reply
Miky Avatar

Small question.. in Symfony 5.2+ there using Attributes from php8 instead of doctrine Annotations... if your environment (php is >8+)... my question is how to handle the {} in annotations to attributes

`/**
* @Route("/create", name="create_something", methods = {"POST"})
*/

#[Route('/create', name: 'create_something', methods: 'POST')]`

my phpstorm is angry if i want use {} in attributes :( like methods: {'POST'} and still getting warning about Expected parameter of type 'string[]|void', 'string' provided if i let there only methods: 'POST'

Did i attribute in right way or i am missing something? i tried find some official documentations.. but i didn't

Update:
i got now that must be an array and answer is => methods: ['POST']

Reply

Hey Miky

I found this docs about attributes https://wiki.php.net/rfc/na... (scroll down to the attributes part)
It says that the syntax should be like this (in case it was accepted because it seems outdated)
#[Route("/api/posts/{id}", ["methods" => ["GET", "HEAD"]])]

Cheers!

Reply
Sonam K. Avatar
Sonam K. Avatar Sonam K. | posted hace 1 año

I have tried everything still I am getting no route for "GET/.....error everytime

Reply

Hey @Sonam

if you run bin/console debug:router can you see your routes there?

Cheers!

Reply
Sonam K. Avatar

No I can only see _preview_error after running php bin/console debug:router

Reply

Ok, in that case your routes are not being loaded. Are you using annotations for your routes? If that's the case you need to double-check your config/routes/annotations.yaml file has these lines:


controllers:
resource: ../../src/Controller/
type: annotation

kernel:
resource: ../../src/Kernel.php
type: annotation


I'm assuming you're on Symfony 5

Cheers!

Reply
Rizky A. Avatar
Rizky A. Avatar Rizky A. | posted hace 2 años

Why my browser not updated the code? but if i am stop server and start again, code updated

Reply

Hey Rizky,

It depends on what exactly code you're changing. But mostly, clearing the cache should help you, especially if you use any virtualization tool locally. If you don't see changes - the first thing you can try - clear the cache with "bin/console cache:clear". It should help 99%. Btw, make sure you're in dev Symfony environment. If you're in prod env - you will always need to clear the cache manually, Symfony recompile the cache only in dev / test envs.

I hope this helps!

Cheers!

Reply
Jack K. Avatar

I entered /questions/<textarea> in the address bar and it injected the HTML. I assumed the Symfony Response object would wrap it in something like htmlentities. I assumed wrong!

Reply

Hey Jack K.!

You're right about that! But, if you passed that value into Twig and rendered it, then it *would* automatically be escaped. So, in reality, it *is* escaped if you ever try to use that value in a real HTML page :).

Cheers!

Reply
Dmitriy Avatar

Hi.

I need a redirect from path /important-pages/*/*/.... to /important-pages.

I tried writing like this, but it doesn't work. How can i solve this problem?

config/routes.yaml


test:
path: ^/important-pages
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
defaults:
path: /important-pages
permanent: true
Reply

Hey Dmitriy

Pretty common situation, but as I know path is not compatible with regular expressions. So in such case it's better to create some request listener, which will check url and return a redirect instead of response

Cheers!

Reply
Default user avatar
Default user avatar Antonio Gigante Arias | posted hace 2 años

Hello,
i'm trying to install "sensioframework", but when type "composer require annotations" i got this error.

The autoloader expected class "App\Controller\MainController" to be defined
!! in file "C:\xampp\htdocs\SES\vendor\composer/../../src\Controller\MainCont
!! roller.php". The file was found but the class was not in it, the class name
!! or namespace probably has a typo.
Reply

Hey Antonio Gigante Arias

Let's try a couple of things
First, double check that the class name of the controller matches to its file, in other words MainController.php file must contain a class named MainController
Second, clear your cache manually rm -rf var/cache it's rare, but sometimes the cache is bugged.
Third, re-install your vendors. rm -rf vendor && composer install
If nothing works, please let me know

Cheers!

Reply
Default user avatar
Default user avatar Antonio Gigante Arias | MolloKhan | posted hace 2 años

The problem was solved, thank you.

1 Reply
Default user avatar
Default user avatar Antonio Gigante Arias | Antonio Gigante Arias | posted hace 2 años

i'm using symfony 5 on windows

Reply
Default user avatar

Hello,
i just installed the Annotations Plugin but i have no autocompletion. I haven't been able to find why! Maybe you can help me?
Thank you so much for this videos, i love them!

Reply
Ethel H. Avatar

i found why!!!!

Regards

Telou

Reply
Default user avatar
Default user avatar Markus Michalski | posted hace 2 años

Just in case someone is confused why they geh URL not found after adding the second route (like me :)) and you're using Apache (like me :) - actually using a Vagrant maschine) - you have to add an .htaccess to the public folder with rewriting rules.

Reply

Hey Markus Michalski

Yes that can be confused :) And the easiest way to solve it is installing symfony/apache-pack it will bring you cool and fast configuration for apache!

Cheers!

Reply
Default user avatar

Just installing symfony/apache-pack? Anything else? - sorry but I dont really understand the support here. Do I have to follow any of these actions too?

I am using symfony5 with MacOS (without Homebrew).

Reply
Default user avatar

ok, so I did two things now:

1) installing:

"symfony/apache-pack"


2) updating:

"/private/etc/apache2/extra/httpd-vhosts.conf"


<virtualhost *:80="">
DocumentRoot "/xxxxxxxxxxxxxxxx/symfony5/public"
ServerName dev.symfony5.local


DirectoryIndex /index.php


<directory "="" xxxxxxxxxxxxxxxx="" symfony5="" public"="">
AllowOverride All

Require all granted
Allow from All


FallbackResource /index.php
</directory>
</virtualhost>

It is now routing correct, but the landing page has in the URL the "index.php". Like

dev.symfony5.local/index.php

Any idea what I need to change?

Reply

Hey @Tim

do you have mod_rewrite enabled? Also if you are using htaccess than I'd recommend to configure mi]nimal support for you virtual host :

<virtualhost *:80="">
ServerName domain.tld
ServerAlias www.domain.tld

DocumentRoot /var/www/project/public
<directory var="" www="" project="" public="">
AllowOverride All
Order Allow,Deny
Allow from All
</directory>

# uncomment the following lines if you install assets as symlinks
# or run into problems when compiling LESS/Sass/CoffeeScript assets
# <directory var="" www="" project="">
# Options FollowSymlinks
# </directory>

ErrorLog /var/log/apache2/project_error.log
CustomLog /var/log/apache2/project_access.log combined
</virtualhost>

Cheers!

Reply
Default user avatar
Default user avatar Tim K. | sadikoff | posted hace 2 años | edited

Heysadikoff
thanks for the answer. The problem was mod_rewrite. Once enabled in httpd.conf (and restart of apache server) the problem was solved.

Many Thanks!!!
Tim

Reply
Richard Avatar

You really need to stop assumimg PHPStorm. Yes it is great but its also expensive. Show things without PHPStorm smart completion.

Reply

Hey Richard

We usually mention or show what it's PHPStorm doing behind the scenes, I apologize if this confused you.
BTW, we highly recommend (no body is paying us) to develop with PHPStorm any PHP project because it's by far the best IDE in the market

Cheers!

1 Reply

Yea, sorry about this. In this intro course, I really try to show that it's being used. But after awhile, if we scroll up to add the use statement every time manually, I think a whole other group of people will be mad at me for wasting their time :). It's tough to please everyone - but sorry if it confused :).

3 Reply

Hey @maxii123,

You can download and try it with trial period. Or take a look at PhpStorm Early Access Program: https://www.jetbrains.com/r... - a little known fact but it allows you to test latest beta versions of this for free. Feel free to report bugs and help improving it, of course.

I hope this helps!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "easycorp/easy-log-handler": "^1.0.7", // v1.0.9
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "symfony/asset": "5.0.*", // v5.0.11
        "symfony/console": "5.0.*", // v5.0.11
        "symfony/debug-bundle": "5.0.*", // v5.0.11
        "symfony/dotenv": "5.0.*", // v5.0.11
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.0.*", // v5.0.11
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/profiler-pack": "*", // v1.0.5
        "symfony/routing": "5.1.*", // v5.1.11
        "symfony/twig-pack": "^1.0", // v1.0.1
        "symfony/var-dumper": "5.0.*", // v5.0.11
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.0.*" // v5.0.11
    },
    "require-dev": {
        "symfony/profiler-pack": "^1.0" // v1.0.5
    }
}