I trying to test my multilanguage app. I have four languages form in my application. I try to test indexAction(), when crawler go through my page I want to check count of title, but title can be in english or in japanese for example. When I pass translation key it does not work. Here is code:
$this->assertEquals(1, $crawler->filter('html:contains("logo_text")')->count());
So the question is, can I pass translation key into tests? Or I need somehow hardcode value?
You can try this solution by Florian Eckerstorfer:
https://florian.ec/articles/use-translation-keys-in-symfony2-functional-tests/
It basically creates a new translator that will return a key instead of a real translation.
class NoTranslator implements TranslatorInterface
{
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
return $id;
}
...
}
And than registering it:
# app/config/config_test.yml
parameters:
translator.class: Acme\DemoBundle\Translation\Translator\NoTranslator
The blog post also describes possibility of using Compiler Passes. A lot more complex solution so you can start with the one above.
Related
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
I would like to create reusable code in controller in "Cakephp way". I would like to replace always one field in few controllers before render website. For example I would like to replace string in field "body". I can do this like this in show method:
public function show($id = null) {
$site = $this->Sites->findById($id)->first();
$new_value = 'test2';
$site['body'] = str_replace('test', $new_value, $site['body']);
}
Is there any better way to do this in cakephp way for example in initalize method or beforeRender? I can't use behavior here.
EDIT:
I know about components, but how to use it to replace all $site['body] (in my code) for all controller methods (so I would like to do this automatic, like behavior for entity)?
Read about Components.
Components are packages of logic that are shared between controllers. CakePHP comes with a fantastic set of core components you can use to aid in various common tasks. You can also create your own components. If you find yourself wanting to copy and paste things between controllers, you should consider creating your own component to contain the functionality. Creating components keeps controller code clean and allows you to reuse code between different controllers.
And see Component Callbacks.
You can use component
https://book.cakephp.org/3.0/en/controllers/components.html
Don't forget to load it in appController or where your need it
After edit :
#nexequ
Maybe if you set the beforeRender in your appController
public function beforeRender()
{
debug($this->request);
}
In $this->request->data array you have your data to replace.
Exemple:
data => array(
'Reunion' => array(
'begin' => '2017-01-13 20:00:00',
'end' => '2017-01-13 20:30:00'
)
If you find the way to get the model ("Reunion" in my example.)
You can do a trick like
replace --> $this->request->data[$model]['body']
I found solution with burzum help, I can use virtual property in src/Model/Entity:
protected function _getBody() {
$new_value = 'test2';
$test = str_replace('test2', $new_value, $this->_properties['body']);
return $test;
}
It will replace for instance 'test2' with $new_value in all controller methods.
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;
}
}
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 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.