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
Related
i have this controller to redirect:
$zone='لندن';
$type='خانه';
return redirect()->route('searchResult',['zone' => $request->zone , 'type' => $request->type]);
and here is my route:
Route::get('/estates/{zone}/{type}', 'EstateController#searchResult')->name('searchResult');
When Its Redirect I get a URL like this-
http://localhost:8000/estates/لندن/خانه
Instead of above I would like to have this URL-
http://localhost:8000/estates/خانه/لندن
i don't wanna switch the route parameters!
need help ty!
Edited:
i have this route :
/estates/{zone}
and wanna this route
/estates/{zone}/{type} be a sub-route for base route
but its return me a reverse route and its not going friendly! and its why i dont wanna change the route parameters!
I can't reproduce your exact behavior, but Laravel has something to handle better those kind of Sub Parameters that is Optional Parameters. https://laravel.com/docs/5.7/routing#parameters-optional-parameters
Define a single Route, and put ? after your parameter name to make it optional:
Route::get('/estates/{zone}/{type?}', 'EstateController#searchResult')->name('searchResult');
And in your Action method signature, put type parameter as optional too
<?php
public function searchResult($zone, $type=null)
{
echo $zone.' / '.$type;
/*if(!$type) {
$type = 'commune';
return redirect()->route('searchResult',['zone' => request()->zone , 'type' => $type]);
}*/
}
And in your case I don't really see a reason to pass request()->type as parameter to route, because even if it's null or not, you will remain in the same state. If you have a new $type variable in your code, then pass it as :
return redirect()->route('searchResult',['zone' => request()->zone , 'type' => $type]);
EDIT -------
If your code in Controller is really :
$zone='لندن';
$type='خانه';
return redirect()->route('searchResult',['zone' => $request->zone , 'type' => $request->type]);
Then I think you should use $zone and $type variable instead of request parameters value :
$zone='لندن';
$type='خانه';
return redirect()->route('searchResult',['zone' => $zone , 'type' => $type]);
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.
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.
I have a route as:
((?<directory>\w+)/?)?((?<controller>\w+)/?)?((?<action>\w+)/?)?((?<id>\d+))?
It works fine but it causes my system to have to include the default controller (index) for all routes to the sub routes. For example, if my page URI is /blog/post (where blog is the directory and post would be the action), my actual URI would have to be blog/index/post - I'd like to be able to fall back to just using blog/post instead.
So, I would like it to be routed to:
directory = blog
controller = index
action = post
Obviously this causes issues when the second parameter is actually a controller. For example directory/controller/action would be routed incorrectly.
Is there a routing method to detect that there are three word parameters, possibly followed by a numeric parameter, which can do what I need?
For claification:
param/param/param(?/id) would be: directory/controller/action(/id)
param/param(?/id) would be: directory/default_controller/action(/id)
i'd actually think that you want to alias blog/index/post with blog/post; insert it as a route before the "catch-all" route that you have; the "one big shoe fits all" approach is not always the best. Especially, if you only have 1 such particular use case.
edit:
"kohana's routing system" is daunting; can't make sense of the elephant they're trying to give birth to there... here are some other suggestions:
Take this issue to the manufacturer; this is definetely an FAQ question
Mess around with the regex patterns. Here's a snippet that might be useful (i put it inside a PHP test case, but you could easily decouple it)
public function testRoutePatterns(){
$data = array(
array(
//most specific: word/word/word/id
'~^(?P<directory>\w+)/(?P<controller>\w+)/(?P<action>\w+)/(?P<id>.*)$~i',
'myModule/blog/post/some-id',
array('directory'=>'myModule', 'controller'=>'blog', 'action'=>'post', 'id'=>'some-id'),
true
),
array(
//less specific: word/word/id
'~^(?P<directory>\w+)/(?P<action>\w+)/(?P<id>.*)$~i',
'blog/post/some-id',
array('directory'=>'blog', 'action'=>'post'), //need to inject "index" controller via "defaults()" here i guess
true
),
);
foreach ($data as $d) {
$matches = array();
list($pattern, $subject, $expected, $bool) = $d;
$actual = (bool) preg_match($pattern, $subject, $matches);
$this->assertEquals($bool, $actual); //assert matching
$this->assertEquals(array(), array_diff($expected, $matches)); //$expected contained in $matches
}
}
As explained on this answer, if you have some route like this:
Route::set('route_name', 'directory/controller/action')
->defaults(array(
'directory' => 'biz',
'controller' => 'foo',
'action' => 'bar',
));
You should have the directory structure like this:
/application/classes/controller/biz/foo.php
I have a controller which I use for a login form. In the view, I have a {error} variable which I want to fill in by using the parser lib, when there is an error. I have a function index() in my controller, controlled by array $init which sets some base variables and the error message to '':
function index()
{
$init = array(
'base_url' => base_url(),
'title' => 'Login',
'error' => ''
);
$this->parser->parse('include/header', $init);
$this->parser->parse('login/index', $init);
$this->parser->parse('include/footer', $init);
}
At the end of my login script, I have the following:
if { // query successful }
else
{
$init['error'] = "fail";
$this->parser->parse('login/index', $init);
}
Now, of course this doesn't work. First of all, it only loads the index view, without header and footer, and it fails at setting the original $init['error'] to (in this case) "fail". I was trying to just call $this->index() with perhaps the array as argument, but I can't seem to figure out how I can pass a new $init['error'] which overrides the original one. Actually, while typing this, it seems to impossible to do what I want to do, as the original value will always override anything new.. since I declare it as nothing ('').
So, is there a way to get my error message in there, or not? And if so, how. If not, how would I go about getting my error message in the right spot? (my view: {error}. I've tried stuff with 'global' to bypass the variable scope but alas, this failed. Thanks a lot in advance.
$init musst be modified before generating your view.
To load your header and footer you can include the following command and the footer's equivalent into your view.
<?php $this->load->view('_header'); ?>
to display errors, you can as well use validation_errors()
if you are using the codeigniter form validation.
if you are using the datamapper orm for codeigniter you can write model validations, and if a query fails due to validation rule violation, you get a proper error message in the ->error property of your model.
Code for your model:
var $validation = array(
'user_name' => array(
'rules' => array('required', 'max_length' => 120),
'label' => 'Name'
)
);
You might try this:
function index() {
$init = array(
'base_url' => base_url(),
'title' => 'Login',
'error' => ''
);
$string = $this->parser->parse('include/header', $init, TRUE);
$string .= $this->parser->parse('login/index', $init, TRUE);
$string .= $this->parser->parse('include/footer', $init, TRUE);
$this->parser->parse_string(string);
}
In parse()you can pass TRUE (boolean) to the third parameter, when you want data returned instead of being sent (immediately) to the output class. By the other hand, the method parse_string works exactly like `parse(), only accepts a string as the first parameter in place of a view file, thus it works in conjunction.