ZF2: using url helper and reuse query parameters - zend-framework2

I'm trying to reuse query params using Url helper in a view. This is my current url:
http://localhost/events/index?__orderby=name&__order=asc
I'm using this code in the view:
$this->url('events/index', array('__page' => '2'), true);
I want to obtain this url:
http://localhost/events/index?__orderby=name&__order=asc&__page=2
But instead i get this:
http://localhost/events/index?controller=Application\Controller\Events&__page=2
This is my route inside module.config.php file:
'events' => array(
'type' => 'segment',
'options' => array(
'route' => '/eventos[/:action]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'Application\Controller\Events',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'index' => array(
'type' => 'Query',
),
),
),
What am i doing wrong? Thanks for your help.

I think what you are looking for is a Query route type to capture the Query string for you as a child route:
'route' => array(
'type' => 'literal',
'options' => array(
'route' => 'page',
'defaults' => array(
),
),
'may_terminate' => true,
'child_routes' => array(
'query' => array(
'type' => 'Query',
'options' => array(
'defaults' => array(
'foo' => 'bar'
)
)
),
),
You can then use the view helper to generate and append the query string for you. If you don't use a Query child route, then the helper will just ignore your query string.
$this->url(
'page/query',
array(
'name'=>'my-test-page',
'format' => 'rss',
'limit' => 10,
)
);
You can then set the third parameter to TRUE and allow the helper to use the current parameters as you are trying to do in your example.
There's examples in the docs:
http://framework.zend.com/manual/2.0/en/modules/zend.mvc.routing.html

You could use something like this, but the query parameters arent reused in my case.
$this->url(
'page/query',
array(),
array(
'query' => array(
'name'=>'my-test-page',
'format' => 'rss',
'limit' => 10,
)
),
true
);
So if you want to reuse query parameters you coul merge them with your new ones and then add all of them to the query array (3 parameter).

Related

ZF2 - Zend Framework 2, understanding routing

I am trying to get my head around Module routing in ZF2.
At the moment I am only able to create a single controller for a single action and am struggling to figure this routing out. I have looked at other modules and plugins and I kind of get it, just need a small push to "get it".
In this example I am trying to route to the two actions: indexAction and cmstoolsAction
Essentially a user navigates to:
/affiliates/overview
/affiliates/cmstools
And the error is:
The requested URL could not be matched by routing.
I think where I am struggling is firstly comprehending how MVC works and getting lost in the detail. There is so much information in the manual that it becomes a little overwhelming.
anyway - would greatly appreciate any input!
Image of the Module structure:
My controller looks like this:
<?php
namespace Affiliates\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class AffiliatesController extends AbstractActionController
{
//Overview page
public function IndexAction()
{
}
public function CmstoolsAction()
{
}
}
And my module config looks like this:
<?php
return array(
'view_manager' => array(
'template_path_stack' => array(
'affiliates' => __DIR__ . '/../view',
),
),
'controllers' => array(
'invokables' => array(
'Affiliates\Controller\Affiliates' =>
'Affiliates\Controller\AffiliatesController'
),
),
'router' => array(
'routes' => array(
'affiliates' => array(
'type' => 'Literal',
'options' => array(
'route' => '/overview',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'cmstools' => array(
'type' => 'Literal',
'options' => array(
'route' => '/cmstools',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'cmstools',
),
),
),
),
),
),
),
);
The routing config is the only important part here. At the moment, you have a route for /overview, which has a child route for /cmstool. This would match the following URLs:
/overview
/overview/cmstool
Not quite what you were after.
There are a few different ways you could configure this. The one closest to what you currently have would be a route for /affiliates, with two child routes, one for each of your actions. The configuration for this would be:
'router' => array(
'routes' => array(
'affiliates' => array(
'type' => 'Literal',
'options' => array(
'route' => '/affiliates',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'index',
),
),
'child_routes' => array(
'overview' => array(
'type' => 'Literal',
'options' => array(
'route' => '/overview',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'index',
),
),
),
'cmstools' => array(
'type' => 'Literal',
'options' => array(
'route' => '/cmstools',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'cmstools',
),
),
),
),
),
),
),
This configuration contains three routes: affiliates, overview and cmstools. The latter two are both child routes of affiliates. Note that I removed the line 'may_terminate' => true, from the affiliates route. This determines whether or not the affiliates route would match on its own (i.e. whether the URL /affiliates would work). Since you didn't list this I'm assuming you don't want it to.
Another way you could configure this would be to simply create two literal routes, once for each URL (not using child routes at all):
'router' => array(
'routes' => array(
'overview' => array(
'type' => 'Literal',
'options' => array(
'route' => '/affiliates/overview',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'index',
),
),
),
'cmstools' => array(
'type' => 'Literal',
'options' => array(
'route' => '/affiliates/cmstools',
'defaults' => array(
'controller' => 'Affiliates',
'action' => 'cmstools',
),
),
),
),
),

Zend Framework 2 Wildcard Route

I have the following route in my module.config.php:
'routes' => array(
'admin' => array(
'type' => 'Segment',
'options' => array(
'route' => '/admin[/:controller[/:action]][/]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]+',
'action' => '[a-zA-Z][a-zA-Z0-9_-]+',
),
'defaults' => array(
'__NAMESPACE__' => 'Admin\Controller',
'module' => 'Admin',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'wildcard' => array(
'type' => 'Wildcard',
)
),
'priority' => 1000
),
),
The reason for the [/] in the end of the route is in the question: Zend Framework 2 Segment Route matching 'test' but not 'test/'
I want this route to be like in ZF1. I want to pass $_GET parameters in it (like /id/1/test/2/).
The problem it this route is actually matching /admin/customer/edit//id/20 but not matching /admin/customer/edit/id/20
Any ideas?
You are on the right track! Use "Wilcard" as a type of child-route to your admin-route.
There are two options available: key_value_delimiter and param_delimiter.
Both's default values are '/' which is equal to the ZF1 default behaviour of route parameters.
'router' => array(
'routes' => array(
'admin' => array(
'type' => 'Segment',
'options' => array(
'route' => '/admin[/:controller[/:action]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]+',
'action' => '[a-zA-Z][a-zA-Z0-9_-]+',
),
),
'defaults' => array(
'__NAMESPACE__' => 'Admin\Controller',
'module' => 'Admin',
'controller' => 'Index',
'action' => 'index',
),
'may_terminate' => true,
'child_routes' => array(
'wildcard' => array(
'type' => 'Wildcard',
'options' => array(
'key_value_delimiter' => '/',
'param_delimiter' => '/'
)
)
)
)
)
)
If you want to address the wildcard route with the help of the url view-helper, you can use it like that:
$this->url('admin/wildcard', array('id' => 1234, 'foo' => 'bar'));
You can add route parameter for id :
'route' => '/admin[/:controller[/:action]][/:id][/]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]+',
'action' => '[a-zA-Z][a-zA-Z0-9_-]+',
'id' => '[0-9]+',
),
this route matchs admin/customer/edit/20/, so you can get id in controller:
$this->params()->fromRoute('id');
If you want to have admin/customer/edit/id/20/ ,try:
'route' => '/admin[/:controller[/:action]][id/:id][/]',
If I am understanding correctly, you are trying to get multiple parameters from the URL, right?
e.g.
Traditional GET: www.domain.com/ctrl/action?key1=abc&key2=efg& ... & keyN=xyz
ZF2 route: www.domain.com/ctrl/action/key1/abc/key2/efg/.../keyN/xyz
If so, this is one way of doing it:
'adminPage' => array(
'type' => 'regex',
'options' => array(
'regex' => '/admin/customer/edit[/](?<keyValuePairs>.*)',
'defaults' => array(
'controller' => 'YourProject\Controller\YourController',
'action' => 'yourAction',
),
'spec' => '/admin/customer/edit/%keyValuePairs%',
)
With this, every character after 'admin/customer/edit/' will be stored in 'keyValuePairs' parameter. In Controller::yourAction, get the 'keyValuePairs' parameter, then split the string back into a meaningful key-value data structure.

ZF2 child routes- ajax doesn't return correct route name

I have weird(in my opinion) problem with Zend Framework 2. After I call my ajax function route name is not correct.
Here is a part of my routing:
'ajax' => array(
'type' => 'Literal',
'options' => array(
'route' => '/ajax',
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'ajax',
),
),
'may_terminate' => true,
'child_routes' => array(
'subcategory' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:name][/:page]',
'constraints' => array(
'name' => '[a-zA-Z][a-zA-Z0-9_-]*',
'page' => '[0-9]+',
),
'defaults' => array(
),
),
),
'category' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:name][/:page]',
'constraints' => array(
'name' => '[a-zA-Z][a-zA-Z0-9_-]*',
'page' => '[0-9]+',
),
'defaults' => array(
),
),
),
),
),
Part of my controller code
public function categoryAction()
{
$route['route_name'] = 'category';
$view = new ViewModel($route);
$view->setTemplate('application/index/category');
return $view;
}
public function subcategoryAction()
{
$route['route_name'] = 'subcategory';
$view = new ViewModel($route);
$view->setTemplate('application/index/category');
return $view;
}
public function ajaxAction()
{
$route = $this->getEvent()->getRouteMatch()->getMatchedRouteName();
var_dump($route); // return always last child route from config
}
category.phtml, there is code of my form, I'll show just url of ajax request
<input type="hidden" name="url" value="<?php echo $this->url('ajax/'.$this->route_name, array('name' => $this->route_param , 'page' =>$this->pages['current'])); ?>">
So, as you can see I pass variable with name of child route from action to view, then my url looks like this:
$this->url('ajax/'.$this->route_name, array(...))
When I am at domain.com/category my ajax url is:
$this->url('ajax/category', array(...))
When I am at domain.com/subcategory my ajax url is:
$this->url('ajax/subcategory', array(...))
Here is come the weird part. As you can see above my ajax action get current route name. It doesn't matter if request is send from domain.com/subcategory or from domain.com/category a value is always a last child of route.
In this example value of
$route = $this->getEvent()->getRouteMatch()->getMatchedRouteName();
is always
string 'ajax/category' (length=13)
Shouldn't I get route name depends on $this->url() parameters? If so, how can I get this?
I have read http://framework.zend.com/manual/2.2/en/modules/zend.mvc.routing.html and I don't see any info about returning last child name of child routes.
category and subcategory have the same pattern, so the router is unable to find the correct route name when fetching requested url and return always the last entry found (Last In First Out), when calling url plugin to build an url, you do not have this issue because you name the route you want to build.
If you want to have the same url to request category and subcategory you have to find another way to differenciate them, when passing name parameter for example.
By the way you can easily overcome this by setting you conf :
'subcategory' => array(
'type' => 'Segment',
'options' => array(
'route' => '/subcategoy/[:name][/:page]',
'constraints' => array(
'name' => '[a-zA-Z][a-zA-Z0-9_-]*',
'page' => '[0-9]+',
),
'defaults' => array(
),
),
),
'category' => array(
'type' => 'Segment',
'options' => array(
'route' => '/category/[:name][/:page]',
'constraints' => array(
'name' => '[a-zA-Z][a-zA-Z0-9_-]*',
'page' => '[0-9]+',
),
'defaults' => array(
),
),
),
In your code current state your url will be generated correctly, and your routes will be correctly detected.

ZF2 - Trying to create a route like ZF1

I actually have 2 modules (Application and Admin) in my ZF2 application and I want a url routing like in ZF1. I currently have the following route:
'router' => array
(
'routes' => array
(
'admin' => array
(
'type' => 'Segment',
'options' => array
(
'route' => 'admin/[:controller[/:action]]',
'constraints' => array
(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array
(
'__NAMESPACE__' => 'Admin\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array
(
'wildcard' => array
(
'type' => 'Wildcard'
)
)
),
),
),
So it will match "/admin", "/admin/controller", "/admin/controller/action" but not "/controller/action".
Now I need a route to the Application module. The problem is that if I simply use a route like that for the module Application, this new route would match "/admin/controller" as controller = "admin" and action = "controller".
I also tried the following regex route in the application:
'application' => array
(
'type' => 'Regex',
'options' => array
(
'regex' => '/(?<controller>^(?!(admin)$)[a-zA-Z][a-zA-Z0-9_-]*)?' .
'(/[a-zA-Z][a-zA-Z0-9_-]*)?',
'spec' => '/%controller%/%action%',
/*'constraints' => array
(
//The controller cannot be "admin"
'controller' => '^(?!(admin)$)[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),*/
'defaults' => array
(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array
(
'wildcard' => array
(
'type' => 'Wildcard'
)
)
),
But it's not getting the variables "controller" and "action".
Does anyone have a suggestion of how to solve that?
mind the route order: routes are handled using a LIFO stack, so what comes last in the array comes first when matching request urls.
this means that you always have to define the most generic routes first to prevent them from matching similar but more specific ones.
using the following order shouldn't need any constraint on the controller parameter, because anything starting with /admin will be matched first
'route1' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/:action]]',
'defaults' => array (
'controller' => 'controller',
'action' => 'index',
),
),
),
'route2' => array(
'type' => 'Segment',
'options' => array(
'route' => '/admin[/:controller[/:action]]',
'defaults' => array (
'controller' => 'admin-controller',
'action' => 'index',
),
),
),
furthermore, you can always specify excplicitly routes priority using the priority property (which should not be defined under the options array, but in the topmost array of the route), so the following code is equivalent to the previous example:
'route2' => array(
'type' => 'Segment',
'options' => array(
'route' => '/admin[/:controller[/:action]]',
'defaults' => array (
'controller' => 'admin-controller',
'action' => 'index',
),
),
),
'route1' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/:action]]',
'defaults' => array (
'controller' => 'controller',
'action' => 'index',
),
),
'priority' => -1,
),

zf2 matching language in url route

how is it possible to match url like www.mydomain.com/en/aboutus to
controller -> index
action -> aboutus
lang -> en
in zf2 module routing config ?
in zf1 we fix that by something like this
$route = new Zend_Controller_Router_Route(
'/contact/:lang',
array(
'module' => 'default',
'controller' => 'contact',
'action' => 'index'
)
);
but the aproeach is something else
we want first determine what the language is in url then look into what controller or action user is requesting
zf2 has hirachy support in routers so you can build your routes like a tree
for your situation you have to create a parent route that match lang in url for example
www.mydomain.com/en or www.mydomain.com/fa or www.mydomain.com/de or ....
then in it children write route for others
for code example :
'langroute' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:lang]',
'defaults' => array(
'lang' => 'en',
),
'constraints' => array(
'lang' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
),
'may_terminate' => true,
'child_routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
// The following is a route to simplify getting started creating
// new controllers and actions without needing to create a new
// module. Simply drop new controllers in, and you can access them
// using the path /application/:controller/:action
'aboutus' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/aboutus',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'aboutus',
),
),
),
),
as you can see the langrout match the en de fa or ... lang text
then the childern route check for inner page
in this example the url www.mydomain.com/en/ match the lang en and the route home
please add this code in your modules module.config.php
return array(
'router' => array(
'routes' => array(
// The following is a route to simplify getting started creating
// new controllers and actions without needing to create a new
// module. Simply drop new controllers in, and you can access them
// using the path /Module_Name/:controller/:lang/:action
'your_route_name_here' => array(
'type' => 'Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'__NAMESPACE__' => 'Module_Name\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller][/:lang][/:action]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'lang' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
'controller' => 'Index',
'lang' => 'en',
'action' => 'index',
),
),
),
),
),
),
),
);

Resources