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.
Related
I am starting to add multi-language to a site that uses Laravel for frontend.
However, due to lack of resources at the moment, I am translating the strings as I go. It will be a while before whole site is translated.
The only challenge is that if a text is not translated what get displayed is the key. I would like to specify a default/fallback string for such.
e.g.
{{ trans('site.'.$name) }}
If I pass 'Business' as $name and there' no translation for 'Business' in site.php lang file I end up with site.Business on the frontend. This messes up everything. In the worst case, if there's no site.Business, Laravel should output Business.
Even better it should provide an option for default/fallback string.
Is this possible?
On a side note is there a free translation for common words? This will save time having to translate everything myself.
Thanks.
The fallback language is what you should be using. See the docs
You may also configure a "fallback language", which will be used when
the active language does not contain a given language line. Like the
default language, the fallback language is also configured in the
app/config/app.php configuration file:
'fallback_locale' => 'en',
It will surely take you just as much time writing in a fallback inline as it would simply writing in the fallback in a parallel translation file as you write in the translation key. The time spent thinking of an alternate way versus just doing it is going to be negligible in the end.
If you really want an inline fallback, then you need to create a new helper method that does something different. So prepare yourself for some home brewed awesomeness.
Let's create a new function that we can use in any view. I will use the method described by Joseph Sibler. Create a file called helpers.php inside app. Then add this to your composer.json in the autoload object under a files array as "app/helpers.php". Not sure what I mean? See his answer. After adding, run composer dump-autoload.
Now, let's add a trans_fb() method that will take all the parameters of the trans() method, but with a fallback as well. I will define this method such that the first two arguments are required, (the key, and the fallback).
If Laravel cannot find the translation key (it searches in resources/lang/en/auth.php for example auth.failed as a key) it will use the fallback instead, and pass any other optional arguments for the original method.
<?php
if (! function_exists('trans_fb')) {
/**
* Translate the given message with a fallback string if none exists.
*
* #param string $id
* #param string $fallback
* #param array $parameters
* #param string $domain
* #param string $locale
* #return \Symfony\Component\Translation\TranslatorInterface|string
*/
function trans_fb($id, $fallback, $parameters = [], $domain = 'messages', $locale = null)
{
return ($id === ($translation = trans($id, $parameters, $domain, $locale))) ? $fallback : $translation;
}
}
You can then use this in a template like so:
{{ trans_fb("i.love.laravel", "I love Laravel!") }}
I came up with this solution which uses the same parameters as trans() method and when key is not translated, it returns the translated string using the application 'fallback_locale' from app.php.
I still wonder why it's not implemented natively in Laravel and Laravel just returnes untranslated key.
if (!function_exists('trans_fb')) {
function trans_fb(string $key, array $replace = [], ?string $locale = null)
{
$translation = trans($key, $replace, $locale);
if ($key === $translation) {
return trans($key, $replace, config('app.fallback_locale'));
}
return $translation;
}
}
Ok, this is the problem:
I have a search form where I have several input fields to specify a search request. I use Symfony2 forms to benefit from validation, CSRF-protection and all the good jazz.
Now, I want the URLs of the search results to be both SEO friendly and bookmarkable.
e.g.
http://www.example.com/blue-car-in-Berlin-200km/
My current approach after doing some research is building the desired search slug and redirecting to another action like so:
//some form code
if($searchForm->isValid()){
$searchWidget = $searchForm->getData();
if(!empty($searchWidget->getSearch()))
$slug = $searchWidget->getSearch();
if(!empty($searchWidget->getPlace()))
$slug .= '-in-' . $searchWidget->getPlace()->getName());
if(!empty($searchWidget->getDistance()))
$slug .= '-' . $searchWidget->getDistance().'km';
return $this->redirectToRoute('app_search', array('search'=>$slug));
}
With the second controller which is basically supposed to look like this:
public function searchAction(Request $request, $search)
{
//extract actual terms out of the $search - slug
....
//find a way to inject the terms in the current request-Object (dirty)
...
//do all validation again
}
As already stated in the code this feels really inefficient and cumbersome. Isn't there a better way to do all this? Like having a URL representation which is independent of the actual logic?
Furthermore, is there even a clean solution to use all the benefits of symfony form without actual request parameters but with that request slug?.
Thanks
You could define the route for the search action like that (I'm using annotations here):
/**
* #Route("/search/{search}-in-{place}-{distance}km", name="app_search")
* #Method({"GET"})
*/
public function searchAction(Request $request, $search, $place, $distance)
{
// Your code here
}
Of course this is valid if those three search parameters are the only one needed and only if they are all three mandatory; if the search parameters are not mandatory, you have to define more routes to match all the possible combinations (and I think this is not the right way).
A simpler solution is maybe to create a dynamic slug (it can have one or more values) with some sort of fixed formatting from which you can easily extract all the desired search values. But at this point I would like to ask: why don't you use some simple GET parameters (something like /search?search=blue+car&place=berlin&distance=200)?
Update
Expanding the idea of a flexible search string, you could try something like that:
/**
* #Route("/search/{searchString}", name="app_search")
* #Method({"GET"})
*/
public function searchAction(Request $request, $searchString)
{
// Your code here
}
searchString can be something like that ("<...>" is a placeholder for the real data like "city-berlin"):
city-<...>/distance-<...>/place-<...>
You just have to explode by '/' and then, for each piece, explode by '-' and use the first piece as ID for what to search. The first explode should return something like that:
[
'city-<...>',
'distance-<...>',
'place-<...>'
]
The second explode on each of those elements should return
[ 'city', '<...>' ]
This method is the most flexible because the order of the search parameters doesn't matter and no parameters is mandatory for the analysis.
I think you need SEO bundle for this.
This is a follow up question for my first question from Hiding or removing controller name in url using routes for seo purpose = codeigniter
I need to hide or remove the controller name from the url. So I followed the answer given to me by Nucleo 1985, this works perfectly fine for static pages. I know that my question is somewhat different so I got different solution.
I am using one controller.
I have a function in my controller that has a switch case on it. Every case contains url. example (http://www.sample.com/my_controller/my_function/my_case_url). The /my_case_url is the dynamic.
I created individual routes for every function and it's quite hustle and not applicable to my function that has a switch case url.
My question is.
How can I achieve a url like http://www.sample.com/my_function/ and http://www.sample.com/my_function/my_case_url/? (The function name must be removed or hide when the link is clicked and redirect to the page)
I need this for SEO purposes.
Thank you!
this will work same as your first question
// it will go to my_controller index
$route['my_function'] = 'my_controller';
// you can set specific controller method remove method will go to index.
$route['chomy_functione/(:any)'] = 'my_controller/my_function';
This should be done from project_name/application/config/routes.php this file.Add a line
$route['url_first/url_second'] = "any_controller_name/function_name";
You can add a method _remap() in your controller
https://ellislab.com/codeigniter/user-guide/general/controllers.html#remapping
Put the following code inside your controller
/**
* Intercept all calls to this class.
*
* #access private
* #param string
* #param array
* #return boolean
*/
function _remap($method, $params)
{
// If method exists, call that method.
if (method_exists($this, $method) !== false) return call_user_func_array(array($this, $method), $params);
// If method is actually an existing permalink, show permalink's content
if ($this->SomeModel->exists(array('permalink' => $method))) return $this->view($method);
// Non-existing method
show_404();
}
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.
Using the standard MVC set up in Zend Framework, I want to be able to display pages that have anchors throughout. Right now I'm just adding a meaningless parameter with the '#anchor' that I want inside the .phtml file.
<?= $this->url(array(
'controller'=>'my.controller',
'action'=>'my.action',
'anchor'=>'#myanchor'
));
This sets the URL to look like /my.controller/my.action/anchor/#myanchor
Is there a better way to accomplish this? After navigation to the anchor link, the extra item parameter gets set in the user's URL which is something I would rather not happen.
one of possibilities is to override url helper, or to create a new one.
class My_View_Helper_Url extends Zend_View_Helper_Url
{
public function url(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
{
if (isset($urlOptions['anchor']) && !empty($urlOptions['anchor']))
{
$anchor = $urlOptions['anchor'];
unset($urlOptions['anchor']);
}
else
{
$anchor = '';
}
return parent::url($urlOptions, $name, $reset, $encode).$anchor;
}
}
this helper override url helper, problem is, that you can't use parameter called 'anchor', because it will be changed into anchor in url.
you will call it as in your's example
<?= $this->url(array(
'controller'=>'my.controller',
'action'=>'my.action',
'anchor'=>'#myanchor'
));
I hope it helps
There are multiple ways you could go about implementing a fragment id into your URLs. Below are some options, along with some pros and cons for each.
Direct Add
You could simply add the "#$fragment_id" after your url() call. Inelegant, but simple. If you don't use page anchors much (i.e. One or two pages only), this is the way to go.
Write a custom url() helper
You could write a custom version of url() appending an optional 5th argument for the fragment id:
class My_View_Helper_Url extends Zend_View_Helper_Url
{
public function url(array $urlOptions = array(), $name = null,
$reset = false, $encode = true,
$fragment_id = null)
{
$uri = parent::url($urlOptions, $name, $reset, $encode);
if(!is_null($fragment_id)) {
$uri .= "#$fragment_id";
}
return $uri;
}
}
This way, anchor (and anchor/fragment id) information is kept strictly withing the realm of the View. This is good for general use, but can get a little unwieldy for the default route. Also, this is still a little too hard-coded for some uses.
Write a custom Route class (Extreme)
As a third option, you could write a custom version of the Zend_Controller_Router_Route class(es), specifically the assemble($data, $reset, $encode) method (the match($path) method ignores fragment ids by default).
Using this method can be quite tricky, but very useful, especially if use is only limited to specific routes (this method can be used to base the fragment id off of any variable).
Caveat
Certain considerations must be taken into account when using fragment ids. For example, query strings have to precede the fragment id in the uri, otherwise, the query string ignored by PHP. However, most ZF applications tend to avoid use of query strings, so it may not be an issue.
The url view helper accepts a 'fragment' key for the third option:
url('[route]',array([params]),array('fragment'=>'anchor'));
this will automatically end the url with #anchor.
-Thanks to Exlord
I think the Extreme method of writing a custom route class is better because other helper will have the same behavior (like the redirector action helper).