I have two controllers: ArtistsController and RecordsController
I want to order routes logically depending on what the user is doing.
In this case once the user is editing an Artist (/artists/edit/some-artist) he's able to add some records for that artist.
So, I'd like the route to be something like:
"/artists/edit/some/artist/records/add"
And the same thing with the editing function of a record:
"/artists/edit/some-artist/records/edit/some-record"
I've been fighting with it for a while but I've never worked with Routes before on CakePHP and can't find a solution for this. Is this possible? Thanks
In Config/routes.php
Router::connect('/artists/edit/:some_artist', array('controller' => 'artists', 'action' => 'edit'), array('pass' => array('some_artist')));
Router::connect('/artists/edit/:some_artist/:records', array('controller' => 'artists', 'action' => 'edit'), array('pass' => array('some_artist','records')));
and you go on depending how what parameters you want to pass.
rule is simple: is some variable needs to be passed you put colon ":" before it, and add its name in array 'pass'.
I suggest read Routing: Route elements if you want specify type of passing element.
Additionally Artists Controller function should like this
public function edit($some_artist=null,$records = null) {
/**
[...]
*/
}
Related
Simple question for Cakephp 2.0.
I want to set a routing rule such that:
www.abc.com/z/abc123
will resolve to the full URL of (including the URL parameter)
www.abc.com/bookings/bookingref/?ref=abc123
Where bookings is the Controller, and bookingref is the action.
Can someone teach me what I need to write in the routes.php?
Kevin
In routes.php:
Router::connect('/bookingref/', array('controller' => 'bookings', 'action' => 'bookingref'));
In controller:
public function bookingref(){
}
So you should have a view name after your function. i.e. bookingref.ctp
This is how I would implement your solution:
In Config/routes.php add:
Router::connect('/z/:reference',
['controller' => 'bookings', 'action' => 'bookingref'],
[
'pass' => ['reference'],// Passed to corresponding function argument (order matters if 2 or more)
'reference' => '[a-z0-9]+'// RegExp validation if you need it
]
);
In your BookingsController use:
public function bookingref($reference = null)
{
...
}
Unfortunately, Router::redirect() cannot redirect to string based URLs that include variables. The controller based approach Progredi mentioned is your best bet.
I'm trying use only category name to get all products relating to that category from Category controller without mentioning controller name in link
products is my default controller.
Router::connect('/', array('controller' => 'products', 'action' => 'index'));
i want to get product of any category by this link
website.com/category_name
while now i'm able to getting in this way
website.com/categories/find_products/category_name
so kindly suggest me how to use proper mapping/routing to do this.
You router should looks like:
Router::connect('/:category_name', array('controller' => 'categories', 'action' => 'find_products'));
Then in your action you can read your param from:
$this->params['category_name'];
Important thing is that you have to place this route as last in your routes.php file, because it will be catch all requests.
I didn't see a question involving multiple slugs in a route.
I have a tables called "teachers", "courses", and "subjects". Each have a field called "shortcut".
How do I go about routing and pulling the correct Teacher.id, Course.id and Subject.id from a url like the following:
http://www.domain.com/bowlerae/grade-5/math
where "bowlerae" is the Teacher.shortcut, "grade-5" is the Course.shortcut and "math" is the Subject.shortcut.
I'm not sure how to route this initially, how to pull the ID of each 3 elements (would I do this in AppController or each individual controller I need it?) and how to build a link compatible with reverse routing AND pagination. Also, do the fields "shortcut" need to have unique names for each table such as "teacher_slug", "course_slug", "subject_slug"?
Right now, I'm loading each of the 3 models in my routes.php and querying to get the corresponding ID for each (if that portion of the URL exists) and then passing those values to the controller. But as you can imagine, it spends a lot of resources.
Additional question/concern (for AD7six and others)
If I want a URL like this
http://www.domain.com/bowlerae
to point to a teacher's homepage, or similar a URL like this
http://www.domain.com/bowlerae/grade-5
to point to a course's homepage, I would have a route like the following
Router::connect(
'/:teacher',
array('controller' => 'teachers', 'action' => 'view'),
array(
'teacher' => '[a-z0-9]{1}([a-z0-9\-]{2,}[a-z0-9]{1})?',
'pass' => array('teacher')
));
for teachers, and the following for courses
Router::connect(
'/:teacher/:course',
array('controller' => 'courses', 'action' => 'view'),
array(
'teacher' => '[a-z0-9]{1}([a-z0-9\-]{2,}[a-z0-9]{1})?',
'course' => '[a-z0-9]{1}([a-z0-9\-]{2,}[a-z0-9]{1})?',
'pass' => array('teacher', 'course')
));
My concern is that what if someone navigates to URLs like
http://www.domain.com/account where account is a valid controller but not a valid teacher
or
http://www.domain.com/bowlerae/students where students is a valid controller but not a valid course
How would I handle these instances? I tried using the redirect method like so...
TeachersController.php
public function view($tSlug = null) {
$this->Teacher->id = $this->Teacher->findByShortcut($tSlug);
if (!$this->Teacher->exists()) {
$this->Session->setFlash('This teacher does not exist');
$this->redirect(array('controller' => $tSlug));
}
else{
// do this
} // end else if teacher exists
}
I expected it to route the following URL
http://www.domain.com/account to the accounts controller but it routed it to
http://www.domain.com/account/index, correct accounts controller and correct index action but it appended the "/index" to the end of the URL.
With the way my routes are now, it is trying to access the courses controller, so I added the same redirect method to the courses controller like so...
CoursesController.php
public function view($tSlug = null, $cSlug = null) {
$this->Teacher->id = $this->Teacher->findByShortcut($tSlug);
$this->Course->id = $this->Course->findByShortcut($cSlug);
if (!$this->Teacher->exists()) {
$this->Session->setFlash('This teacher does not exist');
$this->redirect(array('controller' => $tSlug, 'action' => $cSlug));
}
elseif (!$this->Course->exists()) {
$this->Session->setFlash('This course does not exist');
$this->redirect(array('controller' => $tSlug, 'action' => $cSlug));
}
else{
// do this
} // end else if everything is fine
}
Unfortunately I get an error saying the redirect will never end.
Also, I HAVE created routes for URLs like that (if no teacher, course or subject are present) but perhaps they are incorrect.
It's a very bad idea to test all requests against a db to see if they match a route.
Put the logic in your controller
One simple way is to create a route ala:
Router::connect('/*', array('controller' => 'x', 'action' => 'y'));
Or slightly more specific:
Router::connect(
'/:teacher/:course/:subject',
array('controller' => 'x', 'action' => 'y'),
array('pass' => array('teacher', 'course', 'subject')
);
And make sure to declare all other routes you use first. Then simply create a controller action like so:
XController extends AppController {
public $uses = array('Teacher');
public function y($tSlug, $cSlug, $sSlug) {
$teacher = $this->Teacher->findBySlug($tSlug);
...
}
}
Restrict route elements
Using route validation you can restrict what matches a given route element, so for example if there are only 5 teachers, you can prevent any other url from matching by making sure it starts with their slug:
Router::connect(
'/:teacher/:course/:subject',
array('controller' => 'x', 'action' => 'y'),
array(
'teacher' => 'rita|sue|bob|larry|mo',
'pass' => array('teacher', 'course', 'subject')
);
If you'd like to read the slugs for teachers out of the db - cache the db query, don't read from the db in your routes file on all requests.
Put something fixed in your route
Using a route with no fixed string in it means any url at all with 3 path segments will match the route - it may be less error prone to put something fixed in the route so that you can be sure that the request is actually for a teacher, course, subject combination
i.e.
Router::connect('/anything/*', array('controller' => 'x', 'action' => 'y'));
This prevents the need for any "does this url match this route?" logic - as it does, or it doesn't.
I'm trying to get to grips with Cake's routing, but I'm having no luck finding a solution to my particular problem.
I want to map www.example.com/slug to www.example.com/venues/view/slug, where slug is the URL friendly name of a record for a particular venue.
I also want to then map www.example.com/slug/events to www.example.com/events/index/venue:slug.
After reading the CakePHP documentation on Routing, a few times over, I'm none the wiser. I understand how I would create these routes for each individual venue, but I'm not sure how to get Cake to generate the routes on the fly.
You want to be careful mapping something to the first path after the domain name. This means that you would be breaking the controller/action/param/param paradigm. You can do this, but it may mean that you need to define every url for your site in your routes file rather than using Cake's routing magic.
An alternative may be to use /v/ for venues and /e/ for events to keep your url short but break up the url for the normal paradigm.
If you still want to do what you requested here, you could do something like the following. It limits the slug to letters, numbers, dashes, and underscores.
Router::connect(
'/:slug',
array(
'controller' => 'venues',
'action' => 'view'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
)
);
Router::connect(
'/:slug/:events',
array(
'controller' => 'events',
'action' => 'index'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
)
);
In your controller, you would then access the slug with the following (using the first route as an example).
function view(){
if(isset($this->params['slug'])){
//Do something with your code here.
}
}
First off, you're not connecting www.example.com/slug to www.example.com/venues/view/slug, you're connecting /slug to a controller action. Like this:
Router::connect('/:slug',
array('controller' => 'venues', 'action' => 'view'),
array('pass' => array('slug'));
To generate the appropriate link, you'd do the same in reverse:
$this->Html->link('Foo',
array('controller' => 'venues', 'action' => 'view', 'slug' => 'bar'))
That should result in the link /bar.
The problem with routing a /:slug URL is that it's a catch-all route. You need to carefully define all other routes you may want to use before this one.
For my site I have a number of Orders each of which contains a number of Quotes. A quote is always tied to an individual order, so in the quotes controller I add a quote with reference to it's order:
function add($orderId) {
// function here
}
And the calling URL looks a bit like
http://www.example.com/quotes/add/1
It occurred to me the URLs would make more sense if they looked a bit more like
http://www.example.com/orders/1/quotes/add
As the quote is being added to order 1.
Is this something it's possible to achieve in CakePHP?
Have a look at the documentation for defining routes.
Something like this should do the trick:
Router::connect(
'/orders/:id/quotes/add',
array('controller' => 'quotes', 'action' => 'add'),
array('id' => '[0-9]+')
);
You will be able to access the ID with $this->params['id'] in QuotesController::add().
Edit:
Also, have a look at the documentation for passing parameters to action.
It is possible to pass the ID in as a parameter of the controller action like so:
Router::connect(
'/orders/:id/quotes/add',
array('controller' => 'quotes', 'action' => 'add'),
array('pass' => array('id'), 'id' => '[0-9]+')
);
You can then access the ID with $id in QuotesController::add($id).