I've already figured out how to make a simple resource reachable by AbstractRestfulController. Example:
localhost/products -> list
localhost/products/1 -> special product
Is there a way to nest resources? If so, how would you do that? Example:
localhost/products/1/photos -> list all photos of a product
localhost/products/1/photos/3124 -> show special photo of a product
(I have in this presentation as goal mind)
Thanks for your help!
You need to add another route. For instance :
'products' => array(
'type' => 'Literal',
'options' => array(
'route' => '/products',
'defaults' => array(
'controller' => 'Application\Controller\ProductsRest',
'action' => null
)
),
'may_terminate' => true,
'child_routes' => array(
'photos' => array(
'type' => 'Segment',
'options' => array(
'route' => '/:productId/photos'
)
),
)
)
'products' => array(
'type' => 'Segment',
'options' => array(
'route' => '/products/:productId[/photos/:photos]',
'constraints' => array(
'productId' => '[0-9]*',
'photos' => '[0-9]*'
),
'defaults' => array(
'controller' => 'your contrller',
),
),
),
Related
I want this route:
localhost/events/ -> show list of events (listAction)
localhost/events/ultra-music-festival -> show event detail
localhost/events/bob-ross -> show event detail
localhost/events/anyAction -> execute any action in EventsController.php
localhost/events/anyAction/23 -> execute any action, with optional parameter (13), in EventsController.php
My code:
'router' => array(
'routes' => array(
'events' => array(
'type' => 'literal',
'options' => array(
'route' => '/events',
'defaults' => array(
'__NAMESPACE__' => 'Events\Controller',
'module' => 'Events',
'controller' => 'events',
'action' => 'list'
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:action[/:id]]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-z0-9_-]*',
'id' => '[a-zA-z0-9_-]*', // slug
),
),
),
'detail' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:id]',
'defaults' => array(
'action' => 'detail'
),
'constraints' => array(
'id' => '[a-zA-z0-9_-]*', // slug
)
)
),
'paginator' => array(
'type' => 'Segment',
'options' => array(
'route' => '[/:action]/page/[:page]',
'constraints' => array(
'page' => '\d+',
),
)
),
),
)
)
),
This route works perfectly in these situations:
localhost/events/ -> show list of events (listAction)
localhost/events/ultra-music-festival -> show event detail
localhost/events/detail/ultra-music-festival -> show event detail
localhost/events/anyAction/23 -> execute any action, with optional parameter (13), in EventsController.php
This route does NOT work in these situations:
localhost/events/anyAction -> execute any action in EventsController.php
I have not found any solution to this Slug use case, and that's what I need to do.
For me this type of route is (or should be) very used.
For example, in case of a blog:
/blog/postname1
/blog/postname2
/blog/create
/blog/edit/1
/blog/edit/2
Try to change this:
'route' => '[/:action[/:id]]',
to:
'route' => '/:action[/:id]',
The additional brackets are causing the issue.
I am trying to hit the datatable action with the sID and cID params.
/CustomerManager/datatable/123/100
When the above url is hit it goes to the refreshdata see below image.
Working fine when the url is
/CustomerManager/datatable
Can anyone see problem with the config?
'CustomerManager' => array(
'type' => 'Segment',
'options' => array(
'route' => '/CustomerManager[/:action]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'CustomerManager\Controller\CustomerManager',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'datatable' => array(
'type' => 'segment',
'options' => array(
'route' => '/[:sID][/:cID]',
'constraints' => array(
'cono' => '[a-zA-Z0-9_-]+',
'cust' => '[a-zA-Z0-9_-]+',
),
'defaults' => array(
'action' => 'datatable',
),
),
),
'refreshdata' => array(
'type' => 'segment',
'options' => array(
'route' => '/[:showDel][/:sID][/:cID]',
'defaults' => array(
'action' => 'refreshdata'
),
'constraints' => array(
'showDel' => '[a-zA-Z0-9_-]+',
'sID' => '[a-zA-Z0-9_-]+',
'cID' => '[a-zA-Z0-9_-]+',
)
)
),
)
it seems "refreshdata" overwrite "datatable" because you expect a url with at last 3 parameter which all can be letters or numbers
and in request you call a url with two number type parameters which matches with "refreshdata"
also I think
'cono' => '[a-zA-Z0-9_-]+',
'cust' => '[a-zA-Z0-9_-]+',
in this line "cono" and "cust" must change to "sID" and "cID"
After my comment an example on how you declare your routes more structured:
'router' => array(
'CustomerManager' => array(
'CustomerManager' => array(
'type' => 'literal',
'options' => array(
'route' => '/CustomerManager',
'defaults' => array(
'controller' => 'Application\Controller\IndexController',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
// Literal route for data table
'datatable' => array(
'type' => 'literal',
'options' => array(
'route' => '/datatable',
'defaults' => array(
'action' => 'datatable',
),
),
'may_terminate' => true,
'child_routes' => array(
// Child route for table cell
'cell' => array(
'type' => 'segment',
'options' => array(
'route' => '/[:sID][/:cID]',
'constraints' => array(
'sID'=> '[a-zA-Z0-9_-]+',
'cID' => '[a-zA-Z0-9_-]+',
),
),
),
),
),
// Literal route for refreshtable
'refreshtable' => array(
'type' => 'literal',
'options' => array(
'route' => '/refreshtable',
'defaults' => array(
'action' => 'refreshtable',
),
),
'may_terminate' => true,
'child_routes' => array(
// Child route for table cell
'cell' => array(
'type' => 'segment',
'options' => array(
'route' => '/[:sID][/:cID]',
'constraints' => array(
'sID'=> '[a-zA-Z0-9_-]+',
'cID' => '[a-zA-Z0-9_-]+',
),
),
),
),
),
),
),
),
)
This is simply an example you can change it after your specific needs.
EDIT
Understanding the basics of routing is important for building a proper application. You should read the ZF2 Routing documentation thoroughly, all the information you need is in there...
For example:
In the question the sID and cID segments are optional (between square brackets). This will most likely also not work as you expect. A problem might occur when column cID is given but no sID value is given. The router will attach the cID value to the sID parameter name in such case, leading to unexpected route parameter values in your match.
Something like this could be solved by doing something like this with your constraints:
'constraints' => array(
'sID' => 's[a-zA-Z0-9_-]+',
'cID' => 'c[a-zA-Z0-9_-]+'
),
By preceding the regular expression constraints (and the values in the url) with either an s or a c you make sure the correct value is tied to the correct parameter.
I have quite a complex routing file at the moment. I want to be able to have skippable segments (which I'm using a plugin for at the moment - https://github.com/tccltd/TccSkippableSegment) but also skip that segment if it matches one of the child routes. Here's a section of the file:
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => '\Application\Utilities\SkippableSegment',
'options' => array(
'route' => '/college/courses[/:level-name][/]',
'constraints' => array(
'level-name' => '(?i:[a-zA-Z][a-zA-Z0-9_-]*)',
),
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
'level-name' => '',
),
'skippable' => [
'level-name' => true,
]
),
'may_terminate' => true,
'child_routes' => array(
'privacy' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => 'privacy-policy',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'privacy',
),
),
),
),
),
),
),
What I want to achieve is:
/college/courses/AS2 - Works
/college/courses/AS2/privacy - Works
/college/courses/privacy - Does not work because it thinks that
'privacy' is a level
My question is since 'privacy' is already a child node is there a way in constraints to 'ignore' it?
Any help appreciated, feel free to ask for more information if needed.
i have defined two routes, /shoppingcart/ and a child route /shoppingcart/add/ which should only be available for POST requests.
'routes' => array(
'shoppingcart' => array(
'type' => 'literal',
'options' => array(
'route' => '/shoppingcart/',
'defaults' => array(
'controller' => 'ShoppingcartController',
'action' => 'shoppingcart',
),
),
'may_terminate' => true,
'child_routes' => array (
'add-product' => array(
'type' => 'method',
'options' => array(
'verb' => 'post',
'route' => 'add/',
'defaults' => array(
'controller' => 'ShoppingcartController',
'action' => 'addProductToShoppingcart',
),
),
),
)
),
)
The route /shoppingcart/ works fine. The child route /shoppingcart/add/ does not work(404 error with POST and GET).
When i change the type from method to literal and remove the verb key it works.
How can i use Zend\Mvc\Router\Http\Method in the child route?
You need to set may_terminate true for your child route.
Also, you mention route failing for GET, which it will if you only set the verb to post, if you want to allow get too, the verb should be get,post
Edit: after a little experimenting, it turns out my understanding was wrong, the Method type needs to be placed as parent of the route it's protecting....
'routes' => array(
'shoppingcart' => array(
'type' => 'literal',
'options' => array(
'route' => '/shoppingcart/',
'defaults' => array(
'controller' => 'ShoppingcartController',
'action' => 'shoppingcart',
),
),
'may_terminate' => true,
'child_routes' => array (
'add-product' => array(
'type' => 'method',
'options' => array(
'verb' => 'get,post',
),
'child_routes' => array(
// actual route is a child of the method
'form' => array(
'may_terminate' => true,
'type' => 'literal',
'options' => array(
'route' => 'add/',
'defaults' => array(
'controller' => 'ShoppingcartController',
'action' => 'addProductToShoppingcart',
),
),
),
),
),
),
),
),
I am trying to get some basic routing done in ZF2 but I am running into some problems.
The section that is giving me trouble is this:
'parent-categories' => array(
'type' => 'literal',
'options' => array(
'route' => '/kategorier/',
'defaults' => array(
'controller' => 'categories',
'action' => 'parent-categories',
),
),
'may_terminate' => true,
'child_routes' => array(
'child-categories' => array(
'type' => 'segment',
'options' => array(
'route' => '/kategorier[/:slug][/:parentCategoryid]/',
'constraints' => array(
'parentCategoryid' => '[0-9]+',
),
'defaults' => array(
'controller' => 'categories',
'action' => 'child-categories',
)
),
),
),
),
The original 'parent-categories' route works just fine, no problem. The problem though is the child-categories route isn't doing anything. I have the URL:
/kategorier/test-test-test-test-test/1/
but this is never matched to anything. I get the error:
The requested URL could not be matched by routing.
If I take the child-categories route out of the "child_routes" section, it always catches the request, even if the url is only /kategorier/. Can anyone see what I am doing wrong here?
A child route appends to the parent route. I.e. what you're currently matching is
/kategorier//kategorier[/:slug][/:parentCategoryid]/
Do it like this
'parent-categories' => array(
'type' => 'literal',
'options' => array(
'route' => '/kategorier',
'defaults' => array(
'controller' => 'categories',
'action' => 'parent-categories',
),
),
'may_terminate' => true,
'child_routes' => array(
'child-categories' => array(
'type' => 'segment',
'options' => array(
'route' => '[/:slug][/:parentCategoryid]',
'constraints' => array(
'parentCategoryid' => '[0-9]+',
),
'defaults' => array(
'controller' => 'categories',
'action' => 'child-categories',
)
),
),
),
),
And i guess this should be working out alright. It's a good advice to NOT have trailing slashes, as you'd ideally always want to begin new routes with one for better readability ;)