Buy

Routing Wildcards

Routing is packed with cool little features, but the most common thing you'll see is the addition of wildcards. Add a /{count} to the end of the route's path:

7 lines modules/dino_roar/dino_roar.routing.yml
dino_says:
path: /the/dino/says/{count}
... lines 3 - 7

Because this is surrounded with curly-braces, the route will now match /the/dino/says/*anything*,

As soon as you have a routing wildcard called count, you can suddenly have a $count argument in your controller function:

15 lines modules/dino_roar/src/Controller/RoarController.php
... lines 1 - 6
class RoarController
{
public function roar($count)
{
... lines 11 - 12
}
}

The value for for the {count} part in the URL is passed to the $count argument. Both the wildcard and the argument must have the same name.

Use $count to change our scary greeting: $roar = 'R'.str_repeat('0', $count).'AR!'. Pass this to the Response:

15 lines modules/dino_roar/src/Controller/RoarController.php
... lines 1 - 8
public function roar($count)
{
$roar = 'R'.str_repeat('O', $count).'AR!';
return new Response($roar);
}
... lines 14 - 15

We just changed the route configuration, so we need to rebuild the routing cache:

drupal router:rebuild

Once I do, the /the/dino/says page returns a 404! Ah! As soon as you add /{count} to the route path, the route only matches when something is passed there. We need /the/dino/says/*something*, anything but blank. There are ways to make the wildcard optional - check out the Symfony routing docs.

Add 10 to the end of the URL:

http://localhost:8000/the/dino/says/10

Rooooooooooar! Make it 50 and the rooooooooooo....oooooooar gets longer.

Woah! You now know half of the new stuff in Drupal 8. It's this routing/controller layer. Make a route, then a controller and make that controller return a Response. Seriously, can we go on vacation now?

Leave a comment!

  • 2017-03-01 JLChafardet

    I'll take a read, I'm not too well documented on howto use 2 types of routers or a custom one or a dymanic one, but thanks for the links, I'll take a read about them and I'll get here eventually

  • 2017-02-28 Victor Bocharsky

    Hey JLChafardet,

    Yes, I see... Hm, I don't know the way to specify a controller to be checked the last, but actually, you need to specify the only one route ("/{something}") in YAML and make it the last - I think it will be enough. I'm not sure, but maybe a custom route loader could help you with your issue too: https://symfony.com/doc/cur... . And take a look at DynamicRouter which is used in Symfony CMF, I suppose it solves your problem in a some way: https://symfony.com/doc/cur...

    Cheers!

  • 2017-02-28 JLChafardet

    now, here's the issue, how about different controllers ?

    I don't want to have a controller or a method for each {something} which is why I'm using the {something} wildcard.

    unless .... I switch to yaml routing .... gota consider that, if I do that, I could force the {something} to be the last route to be checked... I'll read about that.

    Or is there a way to tell the router component to check a specific controller last? maybe?

  • 2017-02-27 Victor Bocharsky

    Hey JLChafardet ,

    Yes, the easiest workaround for this is make your `@route("/{something}")` route the latest route in your system, then it will be matched *only* when the others didn't.

    Cheers!

  • 2017-02-25 JLChafardet

    weaverryan I just (been taking a break after a long project) found myself facing an issue here with wildcarded routing.

    lets assume the following example.

    @route("/{something}")

    on root /

    but if someone wants to go to /admin or /manage or /whatever-else-not-related-to-wildcard yet still having root / as parent.

    is there a way to "avoid" being caught on that wildcard on some urls? (without using a ridiculous switch/case forward method to parse the urls. as hardcoding that would be unmaitainable)

  • 2017-01-25 Victor Bocharsky

    Great! It works in most cases ;)

    Cheers!

  • 2017-01-25 Michael Stratford

    Clearing the cache did the trick. Thank you for your quick and detailed response!

  • 2017-01-25 Victor Bocharsky

    Hey Michael,

    No, it doesn't. Please, ensure you don't have a route prefix for the contoller, i.e. extra @Route() annotation about GenusController class. Btw, you can do $ bin/console debug:router | grep /genus/. Do you really get exactly the /genus/{genusName} path as a result of this command? And of course, don't forget to clear the cache. Keep in mind, that prod cache doesn't regenerate by itself, you should do `bin/console cache:clear -e prod` each time you change annotations, configuration, etc.

    Let me know if it helps.

    Cheers!

  • 2017-01-24 Michael Stratford

    I'm a little confused. I successfully completed the last chapter.

    Moving onto this one, my code is identical to yours.

    The route now looks like: @Route("/genus/{genusName}")
    The public function showAction contains the parameter $genusName
    and the return response is ('The genus: ' . $genusName)

    and yet I'm getting a 404.

    Has anything changed in 3.2.2 that would make this happen?

  • 2016-12-04 Brian Baquiran

    Haha, I guess that's my initial contribution to the platform :D

    Thanks weaverryan!

  • 2016-12-03 weaverryan

    Hey Brian!

    Wow, so this blew my mind a little bit :). And after some digging, you seem to have uncovered a bug recently introduced into Symfony. I've created an issue about it here - https://github.com/symfony/... - we'll find out if this is an intended change or not. But, the most important thing is that you are NOT crazy, in fact, you did a great job of following through on this very weird behavior.

    So, keep going on the tutorial, and don't let this nag you anymore. When I first tried your code, I thought I was losing my mind!

    Cheers!

  • 2016-12-02 Brian Baquiran

    Hey weaverryan,
    You can check it here: https://github.com/brianbaq...

    I did a dump($genusName) and Symfony displays it as null.

    I am using bin/console server:run.

    In the Symfony Profiler's Request/Response screen, _route_params shows "genusName2" => "octopus"

    It's not stopped me from working through the rest of the course, but it bothers me that it's not producing an error when it should.

  • 2016-12-02 weaverryan

    Hey Brian!

    Yea, good find on changing the annotation use statement for Route. It's one of those situation where there are multiple "Route" classes, so PhpStorm just suggests them all :).

    So, 2 things:

    A) Can you post your full controller code here?
    B) Are you using the built-in PHP web server (e.g. bin/console server:run)? If not, and you're using your own web-server, make sure to prefix the URL with app_dev.php. So, instead of going to http://mysite.local/genus/octopus, go to http://mysite.local/app_dev.php/genus/octopus. This activates the "dev" environment (which is activated automatically if you use the built-in web server). If that's the problem - we have more on that in the next screencast :) http://knpuniversity.com/sc...

    Cheers!

  • 2016-12-02 Brian Baquiran

    Hi! Yes, the code is exactly what's in the video and script. I'm using annotations, as directed in the tutorial. I haven't set a default value.

    I did notice that the first time I tried it, the @Route annotation was autocompleted as Symfony\Component\Routing\Annotation\Route and not the Sensio\Bundle\FrameworkExtraBundle\Configuration\Route in the tutorial. It also worked, and I didn't notice the difference until trying to figure out why it didn't produce the error. I've set it to use the Sensio one, but still no joy.

    Anything else I can check?

  • 2016-12-02 weaverryan

    Hey Brian Baquiran !

    Hmm... Does your code look exactly like mine, or do you have some differences? For example, if you have {genusName2} in the route, but $genusName as your argument, did you possibly give it a default value (e.g. $genusName = null)? Are you also using annotations routes (or are you possibly using YAML)?

    Lemme know - there's gotta be some tiny detail that's mucking things up :).

    Cheers!

  • 2016-12-02 Brian Baquiran

    I'm not getting the error message when the wildcard and variable name don't match, just "The genus: "

  • 2016-11-12 weaverryan

    Hey @Jaroslav!

    Thanks! :). You didn't miss the assets:install - we never ran it - but I believe that the installer handles this for you (it's also run automatically when you run "composer install"). But, if something went wrong, it's possible you need to run this.

    But, if you're getting a white screen (white screen of death! I call it) - then that's a different problem. The most likely cause is that you're not in the *dev* environment in Symfony. If you're using the built-in PHP web server that we use (i.e. bin/console server:run) then you will get this automatically. But if you're using your own web server, like Apache or Nginx, then you'll need to modify your URL, e.g. if you setup a host name called mydomain.local, then you would go to http://mydomain.local/app_dev.php for the homepage, or http://mydomain.local/app_dev.php/genus for the /genus page. The extra app_dev.php activates the "dev" environment. We talk about this is the second course of the Symfony series :) http://knpuniversity.com/sc...

    Let me know if that's the problem, or if it's something else!

    Cheers!

  • 2016-11-12 Jaroslav

    Hi! Very nice tutorial, just missed the information that I should probably run assets:install in some of previous steps (to get as nice debug page as you get) and now I am in trouble with renaming genusName to genusName2. I dont get any error, just white page with "Genus name: " string. Why I do not get an error page, plase?

  • 2016-06-26 weaverryan

    Hey Enkhbilguun E.!

    Good question :). If you're using annotation routes (like in this course), give you argument a default value:


    public function showAction($genusName = 'foo')

    When you do that, when you go to http://domain/genus, it will *now* match your route and use this default.

    Cheers!

  • 2016-06-26 Enkhbilguun E.

    Hi,
    How can we set a default value for the $genusName variable if http://domain/genus/{genusName} is empty?

  • 2016-05-18 weaverryan

    Hi Konrad!

    Apologies for my slow reply on your comments :). But now I've answered this question over on another comment: https://knpuniversity.com/s...

    Cheers!

  • 2016-05-14 Konrad ZajÄ…c

    Hi,about this use argument/
    I heve these two use statements:

    use Symfony\Component\BrowserKit\Response

    use Symfony\Component\HttpFoundation\Response;
    when I use symfonys autocompletion - I get the first one.
    But the second one is the one that works, could somone explain?
    -----edit-----
    sometimes it's the other way:
    use Symfony\Component\HttpKernel\Tests\Controller;
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    symfony gave me the first, but the second one works