Symfony 2 URL Parameters? - php

I am used to Yii's URL management:
site.com/controller/action/var1/value1/var2/value2
That way I can put my variables in any order in the URL or omit one and having a default value asigned:
public function actionFoo( $var1=22, $var2 ) { //Var1 is optional, Var2 must come.
}
Is there such thing in Symfony? I've searched but I found only hard-coded URL positions, like CodeIgniter, and that's something I find very annoying, because if I have a parameter in the middle of the URL I need to necessarily give it a value.
Example:
site.com/controller/action/false/false/value3
Maybe what I am used too is a bad practice, and I am open to learn other way.
Thanks

Symfony doesn't work that way.
If you need to map a value to a variable name then you need to use query strings: ?var1=value1&var2=value2.
Then, in your controller you can do $this->get('request')->query->get('var1', false) (false being the default value if var1 isn't set).
Otherwise you can define a default value in your route but it doesn't work the way you expect it to work.
You could extend the Routing component to mimic Yii's routing system but I wouldn't advise it because it would require some time.
All-in-all query strings will do exactly what you expect.

I have also found it to be a bit annoying as well, but with a little bit of work you can make the URLs work in Symfony by setting multiple routes to the same controller method. The catch is the order does matter.
This example use annotation but you could do the same thing with YAML, XML and PHP.
/**
* #Route("/whatever/{var2}/{var1}", name="whatever_allvars")
* #Route("/whatever/{var2}", name="whatever_onevar")
* #Template()
*/
public function actionFoo( $var1=22, $var2 ) {
//Var1 is optional, Var2 must come.
}
this would allow the following URLs:
site.com/whatever/var2/var1
site.com/whatever/var2
if you really want to make it match the sample URL give you could set it up like this.
/**
* #Route("/controller/action/var1/{var1}/var2/{var2}", name="whatever_allvars")
* #Route("/controller/action/var1/{var1}", name="whatever_onevar")
* #Template()
*/
public function actionFoo( $var1, $var2=22 ) {
//Var2 is optional, Var1 must come.
}
This would allow the following URLs:
site.com/controller/action/var1/value1/var2/value2
site.com/controller/action/var1/value1
Hopefully this gives you an idea what can be done in Symfony.

Related

in Laravel route , why using default and why to struct route like this

Route::get('/atomic/{id}',[ApiController::class,'index'])->defaults('task', 'atomic');
why use defaults here and what is a task & atomic, and Api controller does not have an index function. Please explain this route properly.
I am new to laravel I tried to google for a solution but no result
defaults method helps to pass extra params to controller without passing as route params
As a backend engineer you’ll often be asked to produce URL patterns
that just don’t work with the rest of the site without breaking your
current routing structure. Often you’ll create what’s known as a slug
for your content, a simple hyphen separated string which is unique in
the system. A typical slug would be just generated from the title like
“My Simple Article” becomes as a slug my-simple-article. This way
there’s a unique string in the system for each post.
If you’ve already been implementing routes like this in your system
you’ll likely have urls that look like /post/{slug} but you know now
that’s not going to be good enough. Your company’s marketing team or
SEO wizards want it to be /{slug} and that’s pretty tricky. You can’t
create the pattern /{post-slug} because it’s going to confuse the
system. What is you have an About Us page or a Contact Us page which
equally important urls like /about-us and /contact-us respectively.
The problem here being that the routing system might pick up the
/about-us link and believe it’s meant to be a slug for a Post model.
At this point Laravel will simply not find the model and throw a HTTP
404 error instead. Not good.
This is where the ‘defaults’ method on routes comes into use to save
the day.
if I consider your example then
Route::get('/atomic/{id}',[ApiController::class,'index'])->defaults('task', 'atomic');
while hitting URL http://127.0.0.1:8002/atomic/1 then in the controller,you will get both params $id and $task
public function index($id,$task){
dump($task);
dump($id);
}
the output of the above will be atomic and 1
defaults() method nothing but key-value pair params
/**
* Set a default value for the route.
*
* #param string $key
* #param mixed $value
* #return $this
*/
public function defaults($key, $value)
{
$this->defaults[$key] = $value;
return $this;
}
suppose if you want to pass multiple array params then use setDefaults method like below
Route::get('/atomic/{id}',[ApiController::class,'index'])->setDefaults([
'tasks'=> 'atomics',
'postTitle'=>'post title goes here'
]);
then in controller
public function index($id,$tasks,$postTitle){
dump($tasks);
dump($postTitle);
dump($id);
}
now if you hit URL http://127.0.0.1:8002/atomic/1 then it will print
atomics
post title goes here
1
Ref : The Power of Laravel’s Route ‘defaults’ for making root level SEO pages

Laravel 5.2 Routing with optional params

I am creating a simple product search engine in Laravel 5.2. I can use either get or post, whichever can accomplish what I'm wanting, even if I need to do some backend processing then pass the pretty URL to another method to show the products.
My parameters are
- query
- merchant
- brand
- page
- sort
All of these parameters can be used on their own or separately.
I'm wanting to use pretty URLs if at all possible.
Basically I want the URLs to look something like this:
/shop/query/shoes
/shop/query/shoes/brand/nike
/shop/query/sort/price
/shop/merchant/amazon
There can be many different routes formed by these 5 parameters, but they are all optional. So what is the best solution to making this route work how I'm wanting, without coding for every single possible route.
I'm sure I am overlooking something. I've used Zend Framework before and just use a * after shop and then I can pass anything in regardless.
If you need any other information, let me know. I appreciate any help.
Try something like this
Route::get('shop/{params?}', function(Request $request, $params = '') {
// everything after "shop/" will be in $params
// you need to add custom logic to parse and handle $params string
return $params;
})->where('params', '(([a-zA-Z0-9-_]+)\/?)+');
{params?} is Optional Parameter
Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. Make sure to give the route's corresponding variable a default value
->where('params', ...) is Regular Expression Constraint
You may constrain the format of your route parameters using the where method on a route instance. The where method accepts the name of the parameter and a regular expression defining how the parameter should be constrained
NOTE
Make sure that you tweak (([a-zA-Z0-9-_]+)/?)+ regular expression to cover all of your cases, as this is something that I added to quickly test your examples

How to forward with GET parameters?

How to forward with GET parameters?
I have two actions in same controller. I do some magic in the first action, and if all goes well, I want it to forward to the other one, but with GET parameters created in the first one.
What I have now is this:
return $this->forward('AppBundle:default:tracked', array(), array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
Empty array in there because of a desperate effort to get it to work. Documentation tells that second array is for the query parameters, but nothing really happens, so I must be doing something wrong.
The second action tries to get the parameters like this:
/**
* #Route("/tracked", name="tracked")
*/
public function trackedAction()
{
$request = Request::createFromGlobals();
// If there is the required variables in the GET:
if($request->query->has('tracking_code') && $request->query->has('company'))
But no, variables never get there it seems.
Reason I have this kind of setup, is that user can get into the trackedAction from another place as well.
I think the proper way to get your parameters in your second action would be to do like this :
return $this->forward('AppBundle:default:tracked', array(
'tracking_code' => $tracking_code,
'company' => $tp_company_name
)));
And your second action would be :
/**
* #Route("/tracked/{tracking_code}/{company}", name="tracked")
*/
public function trackedAction($tracking_code=null, $company=null)
{
...
}
I used the $tracking_code = null because you specified this could be accessed from another place, that maybe does not provide these parameters. This way it works for both of them.
Hope this helps.
The way that Symfony controller forwarding works is by duplicating the current the Request with the options that you pass and then re-dispatching it through the HttpKernel component. You can see this in the code. Because it's a sub-request, your second action is creating a Request from globals (i.e. $_GET etc.) which haven't changed.
The solution is to change your second action method signature to:
public function trackedAction(Request $request)
This means that the $request variable will be "local" to your action and will contain the variables you want.
In practice you should always pass the Request in to your actions this way as it makes your controllers a lot more testable and prevents strange issues like this.
Alternatively to all of the above, you could use a redirect instead which would do an HTTP redirect rather than just forwarding within the Symfony request system.

Changing separating character in Zend URLs

Using the Zend Framework and the url method for the view:
$this->url(array('field1' => this, 'field2' => 'is', 'field3' => 'my example'), 'route_name');
Where route_name is the name of the url route and each field# is retrieved from the database.
I noticed that by default it changes spaces in Controller/Action names into plus sign so that what looked like:
www.example.com/this is my example
to
www.example.com/this+is+my+example
I would like to change the separatoer from + to - to have something like
www.example.com/this-is-my-example
I know that another thread: How to change the separation character of Zend Url?
as documented a way to do it which I tried without success.
A thorough explanation on how to do it would be much appreciated.
EDIT2: I know where the problem lies if anyone is interested, it comes from the way the url is assemble, it uses urlencode which converts all non-alphanumeric characters expect - and _ and the spaces as +, there is no way to override that than replace the character create the url manually (as Maxime suggested) or create a custom url function replacing the characters (as suggest by aporat)...
Thanks!
If you really want to do that, you can extend the stock Zend_View_Helper_Url view helper and add your url logic into your view helper.
<?php
namespace Application\View\Helper;
class MyUrl extends \Zend_View_Helper_Url
{
/**
* Generates an url given the name of a route.
*
* #access public
*
* #param array $urlOptions Options passed to the assemble method of the Route object.
* #param mixed $name The name of a Route to use. If null it will use the current Route
* #param bool $reset Whether or not to reset the route defaults with those provided
* #return string Url for the link href attribute.
*/
public function myUrl(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
{
return str_replace('+', '-', parent::url($urlOptions, $name, $reset, $encode));
}
}
and then just load your new view helper and you're good to go:
$helper = new \Application\View\Helper\MyUrl;
$this->view->registerHelper($helper, 'myUrl');
Unfortunately, you can't set anything before calling the url(...) function to achieve what you want to do. The reason is that when the URL is assembled, it uses the php urlencode(...) function.
That said, you still have many options:
1) You simply don't use the url(...) function and create your URLs manually. (Best option)
2) You create a new helper that acts like url(...) but add extra changes to the function to achieve what you want to do.
3) You take the output of the url(...) function and do a str_replace to change + with -. (I DO NOT recommend that option)
Personally, I create all my URLs manually to avoid this kind of problem.

How can I change Zend Framework's routing schema to not use key/value pairs?

Rather than using controller/action/key1/value1/key2/value2 as my URL, I'd like to use controller/action/value1/value2. I think I could do this by defining a custom route in my Bootstrap class, but I want my entire application to behave this way, so adding a custom route for each action is out of the question.
Is this possible? If so, how would I then access valueN? I'd like to be able to define the parameters in my action method's signature. e.x.:
// PostsController.php
public function view($postID) {
echo 'post ID: ' . $postID;
}
I'm using Zend Framework 1.9.3
Thanks!
While I don't think it's possible with the current router to allow N values (a fixed number would work) you could write a custom router that would do it for you.
I would question this approach, however, and suggest that actually listing all of your routes won't take long and will be easier in the long run. A route designed as you've suggested would mean that either your named parameters are always in the same order, i.e.
/controller/action/id/title/colour
or that they are almost anonymous
/controller/action/value1/value2/value3
With code like
$this->getRequest()->getParam('value2'); //fairly meaningless
Does it have to be N or can you say some finite value? For instance can you imagine that you'll never need more than say 5 params? If so you can set up a route:
/:controller/:action/:param0/:param1/:param2/:param3/:param4
Which will work even if you don't specify all 5 params for every action. If you ever need 6 somewhere else you can just add another /:paramN onto the route.
Another solution I've worked with before is to write a plugin which parses the REQUEST_URI and puts all the extra params in the request object in the dispatchLoopStartup() method. I like the first method better as it makes it more obvious where the params are coming from.

Categories