Changing locale with symfony 2.1 - php

Having some issue changing the locale on a symfony 2.1 website.
I can't find a way to be able to change the lang without using the _locale on every routes. I know this is against the fundamental rule, but this content will for example not be indexed by engine as it is member only.
Typically, I would like a simple method to be able to change the locale on the request (BC from version 2.1), or on the session, but can't figure out how to do that smoothly. I also would like to avoid the use of a Listener for that.
config.yml file :
framework:
translator: { fallback: %locale% }
session:
routing.yml file :
route_change_lang:
pattern: /changelang/{newlang}
defaults: { _controller: AcmeCoreBundle:Default:switchLanguage, newlang: en }
requirements:
newlang: en|fr|de
Simple action to update the locale of the router :
public function switchLanguageAction($newlang)
{
$request = $this->getRequest();
$request->setLocale($newlang);
$referer_url = $this->get('request')->headers->get('referer');
if ($referer_url != null) {
return $this->redirect($referer_url);
} else {
return $this->redirect($this->generateUrl('route_home'));
}
}
What is the problem? I guess it is related to the default_locale set in the main config.yml file, but documentation is not really clear, any help/hint appreciated

I've come across the same problem, since we cant' use locales in our urls (seo-issues). Also we use locales like en_US and those are stored in a config outside the direct framework access.
What I did is registering an event listener and hooking into the onKernelRequest event. There I check if locale is set in session, if not, I add it to both, request and session.
This way, the framework keeps on behaving like it did before 2.1
If you need more info on how to do this, comment and I'll edit some exaples in here :-)

Restore the old behavior as explain in https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md#httpfoundation-1
And use the piece of code of Carlos Granados.
You can also read my another answer https://stackoverflow.com/a/12952999/520114

If you set the locale in the request, this is just used for the current request. The next time that a request is issued, the default_locale will be used. Even if now (2.1) the locale is set in the request instead of the session, "It is also possible to store the locale in the session instead of on a per request basis. If you do this, each subsequent request will have this locale." (from the docs). So, you need to do:
$this->get('session')->set('_locale', $newlang);

Related

Adding multi language option to a symfony 3 project

Reading the documentation of symfony 3, I got very confused, and I'm not sure if I'm doing everything right. This is how one of my normal controller looked at the start:
class IndexController extends Controller
{
/**
* #Route("/", name="index")
*/
public function indexAction(Request $request)
{
$articles = $this->getDoctrine()
->getRepository(Article::class)->findAll();
return $this->render("index.html.twig", array(
'articles' => $articles
));
}
}
I wanted to add option for multy language. Before symfony I saw doing this with simple saving in session the language, and a button for changing it. In symfony I added a translations folder, and a file for each language.
//messages.en.yml
base.menu.1: Home
base.menu.2: Products
base.menu.3: Brands
//messages.bg.yml
base.menu.1: Начало
base.menu.2: Продукти
base.menu.3: Марки
After this I saw in some titorial that I can add my _locale, in my route like this:
#Route("{_locale}/", name="index")
And this actually worked. My I could of change my home page's language by going in bg/, or en/.
But the locale variabe wasn't saved anywhere. If I went to other page, it doesn't know what language was setted up before. So I read more, and was I can fix this, by saving all of my routes in routing.yml like this:
index:
path: /{_locale}/
defaults: { _controller: AppBundle:Index:index }
requirements:
_locale: '%app.locales%'
and then set up my config:
parameters:
locale: bg
app.locales: bg|en
framework:
translator: { fallbacks: ['%locale%'] }
All this is working, except I have to move my routing from their controllers to routing.yml. I want to ask if this is the right method, to do all of this, because I'm not sure, the documentation isn't 100% clear (most likely I just cant understand it), and can't find any good titorials.
use the 'make locale sticky' method, as stated above,
then set the locale using
$request->setLocale($locale);
$request->getSession()->set('_locale', $locale);
//now redirect as the locale change will take affect on the next pageload
(set both, and redirect)
Whether by Annotation or by configuration in YAML, the end result is the same. You'll fall into a debate of for/against annotations if you're not careful though, and I'll just go ahead and say that I do not recommend them personally. Your routing.yml approach is the approach I would recommend, and should work perfectly fine.
I believe that you've read Symfony's post on How to Work with the User's Locale and what you're looking for is Making the Locale "Sticky" during a User's Session.
Symfony stores the locale setting in the Request, which means that this setting is not automtically saved ("sticky") across requests. But, you can store the locale in the session, so that it's used on subsequent requests.

Symfony, dynamic routing

I have a symfony project with multiple skins/templates that have their own routes, does anyone have an idea for a correct setup?
Every skin/template is its own bundle, since its not just skins and assets, but maybe also services that might exist in some skins.
Hostname decides the skin.
Using a custom RouteLoader to load the route.yml of the target bundle.
The custom RouteLoader does the job--but the generated routes are getting cached, and as far as i understand, there is no way to prevent route caching.
Some suggestions are:
Creating a /{dynamic} route, so manually form routes.. But i dont want to throw away that piece of functionality of the router, or refactor the entire project..
Prefix the routes with the template identifier. This would require me to load all route.yml files, which isnt possible since their share paths.
Anyone? I cant go with multiple projects really, the amount of skins will be around 20-30~.
The reason for this setup is because its a target of Content-as-a-Service .. service, multiple clients use the project as a platform, and their setting decides which templates gets used.
It sounds like you want to dynamically load bundles based on the host name? Not going to happen with Symfony 2 because of the caching. Especially the services.
Your best bet is to setup an app for each skin and then do some url majic to execute the desired app.php file. Clearly since you have defined a bundle for each skin then there is a finite number so having multiple apps should not be much or a burden.
It's possible that you might be able to work around the template issue. You would still need to load all your skin bundles but you could futz around with the template names or paths and probably get something to work.
But services? Unless you start appending host names to service id's then I don't see any work around.
I think it's possible to load dynamically twig templates depending of your user by adding a listener on kernel requests.
I can give you a piece of code which, I hope, could help you :
/**
* On Kernel Request triggers the request to get the user config
* then adds TWIG paths depending on user TemplateName
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
//$userConfig = Retrieve your user config
if (null === $userConfig->getTemplateConfig()->getTemplate()->getName())
{
throw new TemplateConfigNotFoundException(sprintf("Could not find TemplateConfig for %s", $userConfig->getName()));
}
$template = $userConfig->getTemplateConfig()->getTemplate()->getName();
$path = sprintf('%s/../../%s/Resources/views', __DIR__, ucfirst($template));
if (!is_dir($path)) {
throw new TemplateNotFoundException(sprintf("Could not find template %s", $template));
}
$this->loader->prependPath($path);
$this->loader->addPath(sprintf('%s/../Resources/views/Default', __DIR__));
}
With $this->loader defined as \Twig_Loader_Filesystem in your Listener constructor
Hope it can give you a clue
Symfony2 already supports host aware routing out-of-the-box, like this:
website_customer_1:
path: /
host: customer1.example.com
defaults: { _controller: Customer1Bundle:Main:startPage, theme: template1 }
website_customer_2:
path: /
host: customer2.example.com
defaults: { _controller: Customer1Bundle:Main:startPage, theme: template2 }

How do I change the URL Alias for Security/login in SilverStripe to user/login

I am working on a new website being built in SilverStripe. Currently I am having a ton of trouble trying to get the system to let me change the URL alias (or create a second one) for the Security controller's login (and eventually logout) function.
I have tried playing around with the routes.yml file and I tried creating the paths in my own UserController and loading directly from the Security controller with "return Security::login()". But that gives me errors about the use of the static functions.
Unfortunately I don't come from a ton of object oriented experience and this is the first CMS I have used that actually uses a bunch of true object orientation. The current version of SilverStripe we are using is 3.0 (but we will be upgrading to 3.1.1 in a few days).
Does anyone know much about the routing in SilverStripe?
as you stated correctly, SilverStripe has routes, and they can be defined in a yaml config file.
if you take a look at the existing routes.yml in the framework you can see how the default security route is defined:
https://github.com/silverstripe/silverstripe-framework/blob/fd6a1619cb7696d0f7e3ab344bc5ac7d9f6cfe77/_config/routes.yml#L17
if you just want to replace the Secuirty in Secuirty/login, its as easy as just creating your own routes.ymlin mysite/_config/ with the following content:
---
Name: myroutesorsomeotherrandomname
Before: '*'
After:
- '#rootroutes'
- '#coreroutes'
- '#modelascontrollerroutes'
- '#adminroutes'
---
Director:
rules:
'foobar//$Action/$ID/$OtherID': 'Security'
NOTE: make sure you ran a ?flush=1 to ensure the yml file is loaded (they get cached)
also make sure you use spaces in the yml file, if you use tabs you are going to have a bad time
if you however wish to also replace /login and /logout this is no longer a thing for routing.
login and logout are actions (php functions that are listed in Security::$allowed_actions and thus available as URL) on the on Security.
but its still rather easy, just subclass Security and create the actions you want:
<?php
class MySuperSecurity extends Security {
private static $allowed_actions = array(
'showMeTheLoginForm',
'alternative_login_action_with_dashes',
'doTheLogout',
);
function showMeTheLoginForm() {
// can be visited via http://website.com/foobar/showMeTheLoginForm
return $this->login();
}
function alternative_login_action_with_dashes() {
// can be visited via http://website.com/foobar/alternative-login-action-with-dashes
return $this->login();
}
function doTheLogout($redirect = true) {
// can be visited via http://website.com/foobar/doTheLogout
return $this->logout($redirect);
}
}
and make the route point to your new class instead of Security inside the routes.yml:
'foobar//$Action/$ID/$OtherID': 'MySuperSecurity'
NOTE: again, make sure you did a ?flush=1, both the private static $allowed_actions as well as the yml config file are cached by silverstripe.
NOTE: both solutions suggested in this post will create an additional route to login and does not replace the existing one, so the old Security/login will still be available
I don't know nothing about SilverStripe excepting that is a CMS, but i think SilverStripe must provide a way to aliases Url. Also an alternative is create Alias in virtual host definition if you're using apache or in .htaccess file. Refer to apache doc to further details. If you post a skeleton of your .htaccess file or VirtualHost definition i could help you.

Symfony2 routing: How to request that the route will be taken from only one template

I heve an embedded controller in my base template. It's a search bar.
For the search bar controller, I have a route "myProject/search".
What I would like is that this route will be taken only when the template where I am embedding the controller (base.html.twig) will call it, and not when i manually put in the browser: "myproject/search".
Any idea on how to do that.
I think, since some time you can't do it:
http://symfony.com/doc/current/book/templating.html#embedding-controllers
quote from the docs:
Even though this controller will only be used internally, you'll need
to create a route that points to the controller
(...)
Since Symfony 2.0.20/2.1.5, the Twig render tag now takes an absolute
url instead of a controller logical path. This fixes an important
security issue (CVE-2012-6431) reported on the official blog. If your
application uses an older version of Symfony or still uses the
previous render tag syntax, you should upgrade as soon as possible.
Anyway, I guess, you can try do it yourself by passing some "secret" argument to search action when you call it from your template. Next in the action you check if the argument was passed to it, and if not you throw 404.
Another way to achieve your goal is use .htaccess file.
You can restrict your route to a certain method by _method option in your routing configuration:
your_rote:
pattern: /myProject/search
defaults: { _controller: YourBundle:YourController:YourAction }
requirements:
_method: POST

Is it possible to restrict a route for AJAX only?

Is it possible to restrict a Symfony 2 route for XHR requests only? I want to declare routes, which are only accessible via AJAX.
I do not want to put some extra lines into each AJAX-specific-actions like that:
if ($request->isXmlHttpRequest()) {
// do something
} else {
// do something else
}
I want to define:
one rule for AJAX requests
one rule for GET/POST requests to the same URL
in order to get around having conditions like above.
I know this question is a bit older but meanwhile a new way to achieve this was introduced in Symfony 2.4.
Matching Expressions
For an ajax restriction it would look like this:
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
condition: "request.isXmlHttpRequest()"
Also possible in Annotation:
/**
* ContactAction
*
* #Route("/contact", name="contact", condition="request.isXmlHttpRequest()")
*/
My advice would be to define your own router service instead of default, which would extend from Symfony\Bundle\FrameworkBundle\Routing\Router, and redefine method resolveParameters() with implementing your own logic for handling additional requirements.
And then, you could do something like this in your routing:
your_route:
pattern: /somepattern
defaults: { somedefaults }
requirements:
_request_type: some_requirement
I'm not sure that you can prevent the request taking place, however you can check for an XHR request in the Controller by checking the current Request
The code would look like this:
if ($request->isXmlHttpRequest()) {
// ...
}
This is not 100% reliable, due to among other things, browser inconsistencies and the possibility of proxy interference. However it is the predominant method of checking for an asynchronous request and is recommended by many. If you are cr
URL Parameter
An alternative would be to add a parameter in your URL to identify the request as asynchronous. This is achieved by adding ?ajax=1 to your URL. Then, check for the parameter with:
$AjaxRequest = $request->getParameter('ajax');
If($AjaxRequest == 1) {
//...
}
Of course, at this point you could also create a specific Route e.g. /ajax/index/.
No, you cannot. It is not depend on which framework you are using, AJAX requests basically are just requests to a server. There is no 100% reliable solution, just "hacks".
What you're looking for does not exist in Symfony routing configuration.
Request::isXmlHttpRequest is even not 100% reliable, and checks HTTP headers put by your JavaScript library :
It works if your JavaScript library set an X-Requested-With HTTP header.
It is known to work with Prototype, Mootools, jQuery.
You can use the requirements for reach the described result.
So, suppose that you're defining routes onto yml format, you have to do something like this
my_route:
pattern: /path/to/route
defaults: { _controller: CompanyBundle:Controller:Action, _format: html }
requirements:
_format: xmlhttp /* not sure about the correct format because
i've never checked about */
And you can, of course, use _method: POST or _method: GET

Categories