Suppose I have page for searching cars, page takes 3 optional parameters, brand, year and color
Simplified route example:
Route::get('/cars/{brand?}/{year?}/{color?}', function ($brand = NULL, $year = NULL, $color = NULL) {
echo "brand is:".$brand."<br>";
echo "year is:".$year."<br>";
echo "color is:".$color."<br>";
});
I don't realise how to pass for example only year parameter?
Works if passed all of 3 parameters, for example: /cars/_/2010/_ but this is very inelegant solution.
What is proper way for this ?
I don't know if this is possible since you may end up passing only 2 parameters and Laravel wouldn't be able to understand if this is brand, color or year.
I will leave my two cents regarding on my method of URL parameters that I use:
public function getCars(Request $request){
Validator::validate($request->all(), [
'brand' => 'nullable|string',
'year' => 'nullable|integer',
'color' => 'nullable|string'
]);
$cars = Car::select('id', '...');
if($request->has('brand')){
// get cars with that brand
$cars->where('brand', $request->brand);
}
// ... and so on with the other parameters
$cars = $cars->paginate(10); // or $cars->get()
}
This is a fairly simple example so you will have to customize to your needs. Hope that helps.
As the official documentation says, Route parameters are injected into route callbacks / controllers based on their order. In this specific case, the only way Laravel has to know which is each parameter is like you suggest (see https://laravel.com/docs/5.6/routing#route-parameters).
Anyway, if 3 parameters are required to perform a search, you could probably think of changing the request verb from GET to POST, and pass all of them as POST request data instead of in the query string itself.
Related
Hi I got trouble in retrieve URL segment CAkephp3 in view. I want to get the ID from current URL.
Lets say my URL is http://localhost/admin/financial_agreements/edit/50
and I want redirect to http://localhost/admin/financial_agreements/do_print/50
simply :
var urlPrint = "<?=$this->Url->build(['controller' => 'financial_agreements', 'action' => 'do_print', 'I NEED ID FROM CURRENT'], true)?>";
I try debug
<?=debug($this->Url->build()); die();?>
But its produce : admin/financial_agreements/edit/50
whats called in 50 ? I need that 50 inside my "url->build" urlPrint
sorry for bad english.
Anyhelp will appreciate.
thanks.
You can use the Request object to get request data (including url parameters) within views.
Try this in your view:
$this->request->getParam('pass') //CakePHP 3.4+
$this->request->params['pass'] // CakePHP 3.3
That will return an array of all non-named parameters that were passed after the action's name in the URL. Example: /mycontroller/myaction/param1/param2. So in this example, $this->request->getParam('pass') will produce an array like: [0 => 'param1', 1 => 'param2'].
Bonus answer: you can also 'name' parameters in the URL, like: /mycontroller/myaction/some_name:some_value. To retrieve this kind of named parameters, you would do the same trick but using: $this->request->getParam('named') (Use the argument 'named' instead of 'pass').
More info:
https://book.cakephp.org/3.0/en/controllers/request-response.html
https://book.cakephp.org/3.0/en/development/routing.html#passed-arguments
Assuming that your edit function follows standard practices, you'll have something like this:
public function edit($id) {
$financialAgreement = $this->FinancialAgreements->get($id);
...
$this->set(compact('financialAgreement'));
}
Then in edit.ctp, you can get the id of the current record very simply as $financialAgreement->id, so your URL will be generated with
$this->Url->build(['controller' => 'financial_agreements', 'action' => 'do_print', $financialAgreement->id], true)
The following is the route
Route::get('{value1}/{optvalue1?}/{optvalue2?}/{value2}/{value3}/',
[
'uses' => 'Controller#control',
'as' => 'path_route'
]
);
My controller is setup as follows
function redirectSearchRequest(){
return redirect()->route('path_route', [
$value1,
isset($optvalue1) ? $optvalue1 : '',
isset($optvalue2) ? $optvalue2 : '',
$value2,
$value3
]);
}
public function control($value1, $iptvalue1 = null, $optvalue2 = null, $value2, $value3)
{
//process accordingly
}
Now the problem with this is
if I had a url which look like http://example.com/value1/optvalue1/optvalue2/value2/value3. It works without any errors but the url can be sometimes without optvlaue1 and optvalue2 and the route returns http://example.com/value1////value2/value3 as expected laravel throws NotFoundHttpException.
Further more to this problem Option variable are not always present but when they are they should be exactly like how the route is set. I cannot change the order around :(.
Hopefully I am clear enough.
Cheers for you help.
Optional variables work better when they're at the end, to avoid 404 errors like you are seeing. There are several workaround you can try:
Option 1:
Account for every possible variation of the route:
Route::get('{value1}/{optvalue1?}/{optvalue2?}/{value2}/{value3}/', Controller#control );
Route::get('{value1}/{optvalue1?}/{value2}/{value3}/', Controller#control );
Route::get('{value1}/{optvalue2?}/{value2}/{value3}/', Controller#control );
Route::get('{value1}/{value2}/{value3}/', Controller#control );
Option 2:
Use the optional parameters in query string
Route::get('{value1}/{value2}/{value3}/', Controller#control );
And just add ?optvalue1=something&optvalue2=something-else
Otherwise, it gets very complicated in identifying which parameter is which.
Option 3:
Another solution could be to default the values of optvalue1 and optvalue2 to something. E.g.
http://example.com/value1/null/null/value2/value3
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) {
...
}
I need to create urls for category switching, switching category should reset page to 1st and change cat name but keep rest of url params.
Example of url:
http://example.com/cat/fruits/page/3/q/testquery/s/date/t/kcal/mine/26/maxe/844/minb/9/mint/4/maxt/93/minw/7/minbl/6/maxbl/96
Urls can contain many different params but category name and page should be always first, category without page should work too and show 1st page.
Currently I have defined two routes and I'm using Zend's url helper with named route but it resets params and as end resuls I have /cat/cat_name/page/1 url.
$category_url = $view->url(array('category'=>$list_item['url'],'page'=>1),'category',FALSE)
resources.router.routes.category_main.route = "/cat/:category/*"
resources.router.routes.category_main.defaults.module = "ilewazy"
resources.router.routes.category_main.defaults.controller = "index"
resources.router.routes.category_main.defaults.action = "results"
resources.router.routes.category_main.defaults.category =
resources.router.routes.category_main.defaults.page = 1
resources.router.routes.category.route = "/cat/:category/page/:page/*"
resources.router.routes.category.defaults.module = "ilewazy"
resources.router.routes.category.defaults.controller = "index"
resources.router.routes.category.defaults.action = "results"
resources.router.routes.category.defaults.category =
resources.router.routes.category.defaults.page = 1
Do I need to create custom method for assembling url in this case?
Things I would try:
Add module, controller and action params in $urlOptions (it's the
first array you pass to the view helper)
instead of 'category' route name try null and see what that does for
you.
Try removing this line
"resources.router.routes.category.defaults.category = "
Please specify what version are of zf are you using.
Solution is very simple, one just have to pass all current request params to url helper
(and optionaly overwrite/add some of them, page and category in my case), last variable is set to FALSE to prevent route resetting.
$view->url(
array_merge(
$params,
array('category' => $list_item['url'],
'page' => 1)
),
'category_main',
FALSE
);
i have url like this :
http://quickstart.local/public/category1/product2
and in url (category1/product2) numbers are id , categorys and products fetched from database attention to the id
id is unique
i need to the sensitive url like zend framework url. for example :http://stackoverflow.com/questions/621380/seo-url-structure
how i can convert that url to the new url like this
is there any way?!!
You'll need to store a unique value in your database with a field name such as 'url' or something similar. Every time you generate a new product you will have to create this unique url and store it with the product information. A common way to do this is to take the name of the product and make it url friendly:
public function generateUrl($name)
{
$alias = str_replace(' ', '-', strtolower(trim($name)));
return preg_replace('/[^A-Za-z0-9-]/', '', $alias);
}
Calling this method:
$url = $this->generateUrl("My amazing product!");
echo $url;
will output:
my-amazing-product
You'll need to check that the output from this function does not already exist in the database as you will use this value to query on instead of the id.
If you apply this logic to the categories as well, you can have easily readable and descriptive urls like the one below. You may need to tweak your routing before this works correctly though.
http://quickstart.local/public/awesome-stuff/my-amazing-product
You could use ZF's Zend_Controller_Router_Route. For example, to make similar url to those used by SO, one could define a custom route in an application.ini as follows (assuming you have controller and action called questions and show respectively):
resources.router.routes.questions.route = '/questions/:id/:title'
resources.router.routes.questions.type = "Zend_Controller_Router_Route"
resources.router.routes.questions.defaults.module = default
resources.router.routes.questions.defaults.controller = questions
resources.router.routes.questions.defaults.action = show
resources.router.routes.questions.defaults.id =
resources.router.routes.questions.defaults.title =
resources.router.routes.questions.reqs.id = "\d+"
Having such a route, in your views you could generate an url as follows:
<?php echo $this->url(array('id'=>621380,'title' => 'seo url structure'),'questions');
// results in: /myapp/public/questions/621380/seo+url+structure
//OR if you really want to have dashes in your title:
<?php echo $this->url(array('id'=>621380,'title' => preg_replace('/\s+/','-','seo url structure'),'questions');
// results in: /myapp/public/questions/621380/seo-url-structure
Note that /myapp/public/ is in the url generated because I don't have virtual hosts setup on my localhost nor any modifications of .htaccess made. Also note that you don't need to have unique :title, because your real id is in :id variable.
As a side note, if you wanted to make it slightly more user friendly, it would be better to have your url as /question/621380/see-url-structure rather than /questions/621380/see-url-structure. This is because under this url you would have only one question, not many questions. This could be simply done by changing the route to the following resources.router.routes.questions.route = '/question/:id/:title'.
EDIT:
And what to do with categories and products that you have in your question? So, I would define a custom route, but this time using Zend_Controller_Router_Route_Regex:
resources.router.routes.questions.route = '/questions/(\d+)-(d+)/(\w*)'
resources.router.routes.questions.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.questions.defaults.module = default
resources.router.routes.questions.defaults.controller = questions
resources.router.routes.questions.defaults.action = show
resources.router.routes.questions.map.1 = category
resources.router.routes.questions.map.2 = product
resources.router.routes.questions.map.3 = title
resources.router.routes.questions.reverse = "questions/%d-%d/%s"
The url for this route would be then generated:
<?php echo $this->url(array('category' => 6213,'product' => 80,'title' => preg_replace('/\s+/', '-', 'seo url structure')),'questions' ); ?>
// results in: /myapp/public/questions/6213-80/seo-url-structure
Hope this will help or at least point you in the right direction.