* This article is part of the original Jobeet Tutorial, created by Fabien Potencier, for Symfony 1.4.
URLs
If you click on a job on the Jobeet homepage, the URL looks like this: /job/1/show
. If you have already developed PHP websites, you are probably more accustomed to URLs like /job.php?id=1
. How does Symfony make it work? How does Symfony determine the action to call based on this URL? Why is the id of the job retrieved with the $id
parameter in the action? Here, we will answer all these questions.
You have already seen the following code in the src/Ibw/JobeetBundle/Resources/views/Job/index.html.twig
template:
{{ path('ibw_job_show', { 'id': entity.id }) }}
This uses the path
template helper function to generate the url for the job which has the id 1
. The ibw_job_show
is the name of the route used, defined in the configuration as you will see below.
Routing Configuration
In Symfony2, routing configuration is usually done in the app/config/routing.yml
. This imports specific bundle routing configuration. In our case, the src/Ibw/JobeetBundle/Resources/config/routing.yml
file is imported:
ibw_jobeet: resource: "@IbwJobeetBundle/Resources/config/routing.yml" prefix: /
Now, if you look in the JobeetBundle routing.yml
you will see that it imports another routing file, the one for the Job controller and defines a route called ibw_jobeet_homepage
for the /hello/{name}
URL pattern:
IbwJobeetBundle_job: resource: "@IbwJobeetBundle/Resources/config/routing/job.yml" prefix: /job ibw_jobeet_homepage: pattern: /hello/{name} defaults: { _controller: IbwJobeetBundle:Default:index }
ibw_job: pattern: / defaults: { _controller: "IbwJobeetBundle:Job:index" } ibw_job_show: pattern: /{id}/show defaults: { _controller: "IbwJobeetBundle:Job:show" } ibw_job_new: pattern: /new defaults: { _controller: "IbwJobeetBundle:Job:new" } ibw_job_create: pattern: /create defaults: { _controller: "IbwJobeetBundle:Job:create" } requirements: { _method: post } ibw_job_edit: pattern: /{id}/edit defaults: { _controller: "IbwJobeetBundle:Job:edit" } ibw_job_update: pattern: /{id}/update defaults: { _controller: "IbwJobeetBundle:Job:update" } requirements: { _method: post|put } ibw_job_delete: pattern: /{id}/delete defaults: { _controller: "IbwJobeetBundle:Job:delete" } requirements: { _method: post|delete }
Let’s have a closer look to the ibw_job_show
route. The pattern defined by the ibw_job_show
route acts like /*/show
where the wildcard is given the name id
. For the URL /1/show
, the id
variable gets a value of 1, which is available for you to use in your controller. The _controller
parameter is a special key that tells Symfony which controller/action should be executed when a URL matches this route, in our case it should execute the showAction
from the JobController
in the IbwJobeetBundle
.
The route parameters (e.g. {id}
) are especially important because each is made available as an argument to the controller method.
Routing Configuration in Dev Environment
The dev environment loads the app/config/routing_dev.yml
file that contains the routes used by the Web Debug Toolbar (you already deleted the routes for the AcmeDemoBundle
from /app/config/routing_dev.php
– see Day 1, How to remove the AcmeDemoBundle). This file loads, at the end, the main routing.yml
configuration file.
Route Customizations
For now, when you request the / URL in a browser, you will get a 404 Not Found error. That’s because this URL does not match any routes defined. We have a ibw_jobeet_homepage
route that matches the /hello/jobeet
URL and sends us to the DefaultController
, index
action. Let’s change it to match the /
URL and to call the index action from the JobController
. To make the change, modify it to the following:
# ... ibw_jobeet_homepage: pattern: / defaults: { _controller: IbwJobeetBundle:Job:index }
Now, if you clear the cache and go to http://jobeet.local from your browser, you will see the Job
homepage. We can now change the link of the Jobeet logo in the layout to use the ibw_jobeet_homepage
route:
<!-- ... --> <h1><a href="{{ path('ibw_jobeet_homepage') }}"> <img alt="Jobeet Job Board" src="{{ asset('bundles/ibwjobeet/images/logo.jpg') }}" /> </a></h1> <!-- ... -->
For something a bit more involved, let’s change the job page URL to something more meaningful:
/job/sensio-labs/paris-france/1/web-developer
Without knowing anything about Jobeet, and without looking at the page, you can understand from the URL that Sensio Labs is looking for a Web developer to work in Paris, France.
The following pattern matches such a URL:
/job/{company}/{location}/{id}/{position}
Edit the ibw_job_show
route from the job.yml
file:
# ... ibw_job_show: pattern: /{company}/{location}/{id}/{position} defaults: { _controller: "IbwJobeetBundle:Job:show" }
Now, we need to pass all the parameters for the changed route for it to work:
<!-- ... --> <a href="{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.company, 'location': entity.location, 'position': entity.position }) }}"> {{ entity.position }} </a> <!-- ... -->
If you have a look at generated URLs, they are not quite yet as we want them to be:
|
Job.php
file and add the following methods to the class:// ... use IbwJobeetBundleUtilsJobeet as Jobeet; class Job { // ... public function getCompanySlug() { return Jobeet::slugify($this->getCompany()); } public function getPositionSlug() { return Jobeet::slugify($this->getPosition()); } public function getLocationSlug() { return Jobeet::slugify($this->getLocation()); } }
You must also add the use
statement before the Job
class definition.
After that, create the src/Ibw/JobeetBundle/Utils/Jobeet.php
file and add the slugify
method in it:
namespace IbwJobeetBundleUtils; class Jobeet { static public function slugify($text) { // replace all non letters or digits by - $text = preg_replace('/W+/', '-', $text); // trim and lowercase $text = strtolower(trim($text, '-')); return $text; } }
We have defined three new “virtual” accessors: getCompanySlug()
, getPositionSlug()
, and getLocationSlug()
. They return their corresponding column value after applying it the slugify()
method. Now, you can replace the real column names by these virtual ones in the template:
<!-- ... --> <a href="{{ path('ibw_job_show', { 'id': entity.id, 'company': entity.companyslug, 'location': entity.locationslug, 'position': entity.positionslug}) }}"> {{ entity.position }} </a> <!-- ... -->
Route Requirements
The routing system has a built-in validation feature. Each pattern variable can be validated by a regular expression defined using the requirements entry of a route definition:
# ... ibw_job_show: pattern: /{company}/{location}/{id}/{position} defaults: { _controller: "IbwJobeetBundle:Job:show" } requirements: id: d+ # ...
The above requirements entry forces the id to be a numeric value. If not, the route won’t match.
Route Debugging
While adding and customizing routes, it’s helpful to be able to visualize and get detailed information about your routes. A great way to see every route in your application is via the router:debug
console command. Execute the command by running the following from the root of your project:
php app/console router:debug
The command will print a helpful list of all the configured routes in your application. You can also get very specific information on a single route by including the route name after the command:
php app/console router:debug ibw_job_show
Final Thoughts
That’s all for today! To learn more about the Symfony2 routing system read the Routing chapter form the book.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
1 Comment
Comments are closed.
[…] Related: Routing in Symfony2 […]