Adding a custom CakePHP Route - php

How can i configure a route connection to handle...
/users/{nameofuser_as_param}/{action}.json?limit_as_param=20&offset_as_param=20&order_as_param=created_at
in the routes.php file such that it calls my controller action like...
/users/{action}/{nameofuser_as_param}/{limit_as_param}/{offset_as_param}/{order_as_param}.json?
Note: Iam using Cakephp 2.X

to handle...
/users/{nameofuser_as_param}/{action}.json
That's pretty easy, and in the docs.
Assuming there is a call to parseExtensions in the route file, a route along the lines of this is required:
Router::connect(
'/users/:username/:action',
['controller' => 'users'],
[
'pass' => ['username'],
// 'username' => '[a-Z0-9]+' // optional param pattern
]
);
The pass key in the 3rd argument to Router::connect is used to specify which of the route parameters should be passed to the controller action. In this case the username will be passed.
For the rest of the requirements in the question it would make more sense for the action to simply access the get arguments. E.g.:
public function view($user)
{
$defaults = [
'limit_as_param' => 0,
'offset_as_param' => 0,
'order_as_param' => ''
];
$args = array_intersect_key($this->request->query, $defaults) + $defaults;
...
}
It is not possible, without probably significant changes or hacks, to make routes do anything with get arguments since at run time they are only passed the path to determine which is the matching route.

Related

Pass in hard coded params into named Laravel route

I am creating some hard coded routes that will likely be changed again freely in the future. To abstract the ideas a bit:
We have a controller/method BuySubscriptionController#start:
class BuySubscriptionController
function start()
{
$plan = Plan::findBySlug($request->get('plan'));
return view('someView', ['plan' => $plan]);
}
}
We currently have the following route:
Route::get('/buy-subscription/start', 'BuySubscriptionController#start');
This means the sales team would need to advertise the following urls:
site.com/buy-subscription/start?plan=plan-one
site.com/buy-subscription/start?plan=plan-two
Now we have been requested to have a few specialized routes:
site.com/purchase/the-basic-plan (plan-one)
site.com/purchase/the-mega-plan (plan-two)
Now I am trying to add these specialized urls to my routes. I was hoping to do something as follows, but does not work:
Route::get('/purchase/the-basic-plan', [
'uses' => 'BuySubscriptionController#start',
'with' => ['plan' => 'plan-one']
]);
Route::get('/purchase/the-mega-plan', [
'uses' => 'BuySubscriptionController#start',
'with' => ['plan' => 'plan-two']
]);
Is there any way to achieve this, simply, without over engineering some new translation layer? Keep in mind that next week the url might be /buy/the-god-plan meaning plan-one, so being able to simple add a line to my routes seems ideal.
You can define a route that takes the plan as a parameter, and use Regular Expression Constraints so that parameter can only take certain values that you allow. So your route definition can look like this:
Route::get('/purchase/{plan}', 'BuySubscriptionController#start')
->name('purchase-plan')
->where('plan', 'the-basic-plan|the-mega-plan');
Then in your controller action just use the parameter:
class BuySubscriptionController
{
protected $plans = [
'the-basic-plan' => 'plan-one',
'the-mega-plan' => 'plan-two'
];
function start($plan)
{
// You can use an associative array to convert the $plan parameter
// into the value you need for querying the database
$plan = Plan::findBySlug($this->plans[$plan]);
return view('someView', ['plan' => $plan]);
}
}
If you need to generate URLs for the route you can just use the route helper method and pass it the plan name:
route('purchase-plan', 'the-basic-plan');
And you'll get:
site.com/purchase/the-basic-plan
This solution allows you to add any number of plan names by just adding the public plan for the URL in the where constraint of the route, then associating that value with the one you need for the query in your controller's $plans property.

CakePHP 3 best way to add additional data to url

I wanted to ask, about strategy, how can i archive this:
I have url: www.mydomain.com/pages
Now, if some if clausure will return true, i want attach this param to all urls:
www.mydomain.com/pages?id=swa or
www.mydomain.com?id=swa
I have no idea how to start,
Thank You for help.
Your best bet would probably be to use a URL filter, it will affect all URLs that are being generated using the core helpers or the Router class, as long as they are being passed as routing arrays, ie
['controller' => 'abc', 'action' => 'xyz', /* ... */]
URLs passed as strings, like /abc/xyz will not be affected!
\Cake\Routing\Router::addUrlFilter(function ($params, $request) {
$key = 'id';
$value = 'swa';
if (!array_key_exists($key, $params)) {
$params[$key] = $value;
}
return $params;
});
This would add the parameter to all URLs (unless they already have that parameter set). But be careful, in case there is a matching connected route that defines a route element with the same name, it will steal the parameter and use it for the element instead of adding it to the query string!
Also note that this will only affect form actions that do explicitly define an action URL, if you'd wanted it to affect the ones that pick up the current URL too, then you'd also have to modify $request->query
$request->query[$key] = $value;
See also
Cookbook > Routing > Creating Persistent URL Parameters
API > \Core\Routing\Router::addUrlFilter()
If it is a one off you can do that using redirects
return $this->redirect(['controller' => 'Pages', 'action' => 'display', $pageId]);
or via html helper
echo $this->Html->link(
'Page 1',
['controller' => 'Pages', 'action' => 'display', 1]
);

CakePHP 3.0 query string parameters vs passed parameters

In CakePHP 3.0 named parameters have been removed (thank god) in favour of standard query string parameters inline with other application frameworks.
What I'm still struggling to get my head around though is that in other MVC frameworks, for example ASP.NET you would pass the parameters in the ActionResult (same as function):
Edit( int id = null ) {
// do stuff with id
}
And that method would be passed the id as a query string like: /Edit?id=1 and you'd use Routing to make it pretty like: /Edit/1.
In CakePHP however anything passed inside the function parameters like:
function edit( $id = null ) {
// do stuff with $id
}
Must be done as a passed parameter like: /Edit/1 which bypasses the query string idea and also the need for routing to improve the URL.
If I name the params in the link for that edit like:
$this->Html->link('Edit', array('action' => 'edit', 'id' => $post->id));
I then have to do:
public function edit() {
$id = $this->request->query('id');
// do stuff with $id
}
To get at the parameter id passed. Would of thought it would pick it up in the function like in ASP.NET for CakePHP 3.0 but it doesn't.
I prefer to prefix the passed values in the edit link instead of just passing them so I don't have to worry about the ordinal as much on the other end and I know what they are etc.
Has anyone played with either of these ways of passing data to their methods in CakePHP and can shed more light on the correct ways of doing things and how the changes in version 3.0 will improve things in this area...
There are a few types of request params in CakePHP 3.0. Let's review them:
The Query String: are accessed with $this->request->query(), are not passed to controller functions as arguments and in order to make a link you need to do Html->link('My link', ['my_query_param' => $value])
Passed arguments: The special type of argument is the one that is received by the controller function as an argument. They are accessed either as the argument or by inspecting $this->request->params['pass']. You Build links with passed args depending on the route, but for the default route you just add positional params to the link like Html->link('My link', ['action' => view, $id, $secondPassedArg, $thirdPassedArg])
Request Params: Passed arguments are a subtype of this one. A request param is a value that can live in the request out of the information that could be extracted from the route. Params can be converted to other types of params during their lifetime.
Consider this route:
Router::connect('/articles/:year/:month/:day', [
'controller' => 'articles', 'action' => 'archive'
]);
We have effectively created 3 request params with that route: year, month and day and they can be accessed with $this->request->year $this->request->month and $this->request->day. In order to build a link for this we do:
$this->Html->link(
'My Link',
['action' => 'archive', 'year' => $y, 'month' => $m, 'day' => $d]
);
Note that as the route specify those parameters, they are not converted as query string params. Now if we wanted to convert those to passed arguments, we connect this route instead:
Router::connect('/articles/:year/:month/:day',
['controller' => 'articles', 'action' => 'archive'],
['pass' => ['year', 'month', 'day']]
);
Our controller function will now look like:
function archive($year, $month, $day) {
...
}

Append param to current URL with view helper in Zend

In my layout-script I wish to create a link, appending [?|&]lang=en to the current url, using the url view helper. So, I want this to happen:
http://localhost/user/edit/1 => http://localhost/user/edit/1?lang=en
http://localhost/index?page=2 => http://localhost/index?page=2&lang=en
I have tried the solution suggested Zend Framework: Append query strings to current page url, accessing the router directly, but that does work.
My layout-script contains:
English
If the default route is used, it will append /lang/en, but when another route is used, nothing is appended at all.
So, is there any way to do this with in Zend without me having to parse the url?
Edit
Sorry for my faulty explanation. No, I haven't made a custom router. I have just added other routes. My bad. One route that doesn't work is:
$Routes = array(
'user' => array(
'route' => 'admin/user/:mode/:id',
'defaults' => array('controller' => 'admin', 'action' => 'user', 'mode'=>'', 'id' => 0)
)
);
foreach( $Routes as $k=>$v ) {
$route = new Zend_Controller_Router_Route($v['route'], $v['defaults']);
$router->addRoute($k, $route);
}
Upd:
You must add wildcard to your route or define 'lang' parameter explicitly.
'admin/user/:mode/:id/*'
Additionally, according to your comment, you can do something like this:
class controllerplugin extends Zend_Controller_Plugin_Abstract
{
function routeShutdown($request)
{
if($request->getParam('lang', false) {
//store lang
Zend_Controller_Front::getInstance()->getRouter()->setGlobalParam('lang', NULL); //check if this will remove lang from wildcard parameters, have no working zf installation here to check.
}
}
}

Zend Framework Router dynamic routes

I bumped into a problem and I can't seem to find a good solution to make it work. I have to make some dynamic routes into a Zend Framework project. I'll explain shortly what my problem is:
I need to have dynamic custom routes that "extend" the default route (module/controller/action/params). The project I'm working for has several partners and the routes have to work with those.
To store the partners I've made a static class and it looks like this.
<?php
class App_Partner
{
static public $partners = array(
array(
'name' => 'partner1',
'picture' => 'partner1.jpg'
),
array(
'name' => 'partner2',
'picture' => 'partner2.jpg'
),
array(
'name' => 'partner3',
'picture' => 'partner3.jpg'
)
);
static public function routePartners() {
$partners = array();
foreach(self::$partners as $partner) {
array_push($partners, strtolower($partner['name']));
}
$regex = '(' . implode('|', $partners) . ')';
return $regex;
}
}
So App_Partner::routePartners() return me a string like (partner1|partner2|partner3) which I use to create the right routes. My goal is to have the custom routes for each partner for every route I have set in the Bootstrap. So if I have a route add-product.html set I want it to work for each partner as partner1/add-product.html, partner2/add-product.html and partner3/add-product.html.
Also, partner1/, partner2/, partner3 should route to default/index/index.
In fact, I made this thing to work using routes like the one below.
<?php
$routeProposal = new Zend_Controller_Router_Route_Regex(
App_Partner::routePartners() . '?/?proposals.html',
array(
'module' => 'default',
'controller' => 'proposal',
'action' => 'index',
'page' => 1
),
array( 1 => 'partner'),
"%s/proposals.html"
);
$router->addRoute('proposal', $routeProposal);
The problem
The above route works fine if I use a partner in the request URI, but if I don't, I get double slashes like public//proposals.html because of the reverse route set in the route above to be "%s/proposals.html". I can't seem to find a way to avoid this reverse route because I build my urls using the url view helper and if the reverse route isn't set I get an exception stating this.
I also need the routes to work without a partner set, which will be the default way (add-product.html, proposals.html etc).
From your description, it seems like you're looking for a zend router chain, where your partner is an optional chain.
Here's a similar question, but using a hostname route : Zend Framework: get subdomain parameter from route. I adapted it to solve your problem, just put the following in your Bootstrap.php to initialize the routing :
protected function _initRoute()
{
$this->bootstrap('FrontController');
$router = $this->getResource('FrontController')->getRouter();
// Default route
$router->removeDefaultRoutes();
$defaultRoute = new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
)
);
$router->addRoute('default', $defaultRoute);
$partnerRoute = new Zend_Controller_Router_Route(
':partner',
array('partner' => 'none'),
array('partner' => '^(partner1|partner2|partner3)$')
);
$router->addRoute('partner', $partnerRoute->chain($defaultRoute));
}
Change as you see fit. In your controllers you will only get a value for the partner parameter if it was actually specified AND valid (you will get a routing error if the partner doesn't exist)...
I use a similar process to detech lang, in my route (but with a ini file).
You can use a default value for you partners parameter to make the route working without partner, and add a ? to your regex.
But actually, I don't know how to avoid the double //...
Hope that helps.
EDIT: For your information, here is a simplified version of my route with language:
routes.lang.type = "Zend_Controller_Router_Route"
routes.lang.route = "lang/:language/*"
routes.lang.reqs.language = "^(en|fr|nl|de)?$"
routes.lang.defaults.language = none
routes.lang.defaults.module = default
routes.lang.defaults.controller = index
routes.lang.defaults.action = language

Categories