I created a EventListener to set the locale based on the user preferences, i set the langage like this in my listener:
$request->setLocale($user->getLanguage());
$request->getSession()->set('_locale',$user->getLanguage());
I tried both..
I register the Listener in the service.yml:
app.event_listener.locale:
class: 'AppBundle\EventListener\LocaleListener'
arguments:
- '#security.token_storage'
tags:
- {name: 'kernel.event_listener', event: 'kernel.request', method: 'onKernelRequest'}
I also tried to add a priority: 17 to the service but it does not change anything...
The listener seems to works, i can get the Locale in my controller with a $request->getLocale()(or session).
But Twig is still in the default language I defined in the config.yml:
parameters:
locale: fr
I'm pretty lost now, any tips ?
I tried a lot of stuff (change the priority, check if the locale is passed to the front etc...)
Finally i forced the translator in my EventListener:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if ($this->tokenStorage->getToken()) {
$user = $this->tokenStorage->getToken()->getUser();
if ($user && $user instanceof User) {
$request->setLocale($user->getLanguage());
} elseif ($request->query->has('locale')) {
$request->setLocale($request->query->get('locale'));
} else {
$request->setLocale($request->getPreferredLanguage());
}
}
$this->translator->setLocale($request->getLocale());
}
I don't understand why, this should be done in the Symfony translator, but it works...
You have to set the locale for the translator to get the right translation in templates.
E.g in controller:
$this->get('translator')->setLocale($user->getLanguage());
Related
I come from Magento and I now use Sylius to modernize things a bit and have access to a less xml oriented platform because I find it really painful in 2022...
Unfortunately I did not find anything in Sylius regarding the management of prices according to the client currently logged.
So I want to use groups and channels: I added a channel relation to a user group to be able to use a channel according to the logged in user.
/**
* #ORM\Entity
* #ORM\Table(name="sylius_customer_group")
*/
class CustomerGroup extends BaseCustomerGroup
{
/**
* #ORM\ManyToOne(targetEntity=Channel::class)
*/
private $channel;
public function getChannel(): ?Channel
{
return $this->channel;
}
public function setChannel(?Channel $channel): self
{
$this->channel = $channel;
return $this;
}
}
Here is what I am trying to do with a service
services:
ChangingContextWithCustomerGroup:
class: App\Context\RequestQueryChannelContext
arguments:
- '#sylius.repository.channel'
- '#request_stack'
- '#sylius.context.customer'
tags:
- { name: sylius.context.channel, priority: 150 }
// src/Context/RequestQueryChannelContext.php
public function getChannel(): ChannelInterface
{
$request = $this->requestStack->getMainRequest();
if (!$request) {
throw new ChannelNotFoundException('Request Not Found!');
}
$customer = $this->customerContext->getCustomer();
if (!$customer instanceof Customer) {
throw new ChannelNotFoundException('Customer Not Found!');
}
$group = $customer->getGroup();
if (!$group instanceof CustomerGroup) {
throw new ChannelNotFoundException('Group Not Found!');
}
$channel = $group->getChannel();
if (!$channel instanceof ChannelInterface) {
throw new ChannelNotFoundException('Channel Not Found!');
}
return $channel;
}
My problem is that I can't get the customer on the mainRequest. It is null, so I cant have the customer => group => channel.
It works very well when I force the channel like this :
public function getChannel(): ChannelInterface
{
// ...
return $this->channelRepository->findOneByCode('fooBar');
}
so my system doesn't work. Is there a better solution?
thanks
The problem here is that the Channel context is called from the Sylius\Bundle\ShopBundle\EventListener\NonChannelLocaleListener which has priority 10 on the kernel.request event. In the channel context you want to use the customer context class, which in turn uses the TokenStorage to retrieve the user information.
The token however is not yet populated in the token storage at that point, because that happens in the firewall listener (Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener in dev environments), which has priority 8.
The solution I found is to lower the priority of the NonChannelLocaleListener to 7. That will make sure that the token is available and that the customer context can be used to retrieve the customer/shop user information.
Lowering the priority of that listener can be done by overriding the service definition in config/services.yaml:
services:
...
sylius.listener.non_channel_request_locale:
class: Sylius\Bundle\ShopBundle\EventListener\NonChannelLocaleListener
arguments:
- '#router'
- '#sylius.locale_provider'
- '#security.firewall.map'
- ['%sylius_shop.firewall_context_name%']
tags:
- { name: kernel.event_listener, event: kernel.request, method: restrictRequestLocale, priority: 7 }
Please note that this is on Sylius 1.10, so it might be possible that on other Sylius versions the priority of these listeners are slightly different. In that case, just use the bin/console debug:event-dispatcher command to figure out what the right priority should be.
I am trying to store some values on cache the first time I load a page. This is the code I am using:
$cached_items = [
'main_nav' => $main_nav,
'sub_nav' => $sub_nav,
'footer_nav' => $footer_nav,
'view_as' => $view_as,
];
$redisConnection = new Client('tcp://redis:6379');
$cache = new RedisAdapter($redisConnection);
$menu = $cache->getItem('mmi_menus');
if ($menu->isHit()) {
return $menu->get();
} else {
$menu->set($cached_items);
$cache->save($menu);
}
This caching is being done from a non Symfony controller - let's say it's a standalone file.
First problem with the code above,
the else condition is reach out all the time and I think it should not be since values are stored. (check here)
Second problem, having this function in a Symfony controller:
public function GenerateMenuItemsAction()
{
$redisConnection = new Client('tcp://redis:6379');
$cache = new RedisAdapter($redisConnection);
$menu = $cache->getItem('mmi_menus');
if ($menu->isHit()) {
return $this->render(
'CommonBundle:Layout:menu.html.twig',
['menu' => $menu->get()]
);
}
}
$menu->isHit() is null so all the time I am getting this exception from Symfony:
An exception has been thrown during the rendering of a template ("The
controller must return a response (null given). Did you forget to add
a return statement somewhere in your controller?").
Update
I am not using any TTL afaik maybe somehow a default one is setup but this is how the section looks like on the config.yml:
framework:
cache:
app: cache.adapter.redis
default_redis_provider: "redis://%redis_host%"
pools:
cache.pool1:
public: true
What I am missing here? Any ideas?
my config.yml looks like that:
framework:
cache:
system: cache.adapter.apcu
default_redis_provider: redis://%redis_password%#%redis_host%:%redis_port%
pools:
redis_pool:
adapter: cache.adapter.redis
public: true
default_lifetime: 0
provider: cache.default_redis_provider
So I can easily (in my Controller) do something like:
$this->get('redis_pool')->getItem('myitem');
Or you can inject 'redis_pool' as an argument to a Service.
I don't need any 'new' or extra Connection information/configuration - anything is done in config.yml and available as a Service across the application.
I'm having the same problem symfony2 is describing here
This comes in handy when you have a bundle but don't want to manually
add the routes for the bundle to app/config/routing.yml. This may be
especially important when you want to make the bundle reusable
TLDR; im trying to implement a custom Route Loader using this part of the symfony2 documentation
http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html#more-advanced-loaders
However it doesn't seem to be working, the route cannot be found;
This is what I've tried so far:
The loader:
<?php
//namespace Acme\DemoBundle\Routing;
namespace Gabriel\AdminPanelBundle\Routing;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
class AdvancedLoader extends Loader
{
public function load($resource, $type = null)
{
$collection = new RouteCollection();
$resource = '#GabrielAdminPanelBundle/Resources/config/routing.yml';
$type = 'yaml';
$importedRoutes = $this->import($resource, $type);
$collection->addCollection($importedRoutes);
return $collection;
}
public function supports($resource, $type = null)
{
return $type === 'advanced_extra';
}
}
here is my routing.yml
located in: src/Gabriel/AdminPanelBundle/Resources/config/routing.yml
the routing.yml
gabriel_admin_panel:
resource: "#GabrielAdminPanelBundle/Controller/"
type: annotation
prefix: /superuser
The Routes of the bundle can't be found unless I put the Routes back in the main app/config/routing.yml file, how to fix this?
Edit:
FileLoaderImportCircularReferenceException: Circular reference
detected in "/app/config/routing_dev.yml"
("/app/config/routing_dev.yml" > "/app/config/routing.yml" > "." >
"#GabrielAdminPanelBundle/Controller/" >
"/app/config/routing_dev.yml").
You must also configure service
# src/Gabriel/AdminPanelBundle/Resources/config/services.yml
your_bundle.routing_loader:
class: Gabriel\AdminPanelBundle\Routing\AdvancedLoader
tags:
- { name: routing.loader }
And routing file
# app/config/routing.yml
YourBundle_extra:
resource: .
type: advanced_extra
Is it possible to set a session cookie, upon successful login? An event listener? If so, which event? And how i can access the response object to attach the cookie to it?
You can create an EventListener which listens to the kernel.response Event and modify the response.
namespace Acme\DemoBundle\EventListener
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\Cookie;
class AddMyCookie
{
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
if (/** whatever **/) {
$response->headers->setCookie(new Cookie('cookie_name', 'cookie_value'));
}
}
}
in your services.yml:
parameters:
acme_demo.add_my_cookie.class: Acme\DemoBundle\EventListener\AddMyCookie
services:
acme_demo.add_my_cookie.kernel_response_listener:
class: %acme_demo.add_my_cookie.class%
tags:
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
Whatever you need to decide to set the cookie (security_context, usermanager, session, etc.) can be injected trough the DI Container.
I'm trying to write a symfony 2 functional test. This is my code:
<?php
namespace WebSite\MainBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ProductControllerTest extends WebTestCase
{
public function testPageContents()
{
$domCategoryLinksExpr = '.catalog-col-block > ul > li > a';
$client = static::createClient();
$crawler = $client->request('GET', '/catalog/');
$this->assertTrue($client->getResponse()->getStatusCode() == '200');
$countCategories = $crawler->filter($domCategoryLinksExpr)->count();
$this->assertTrue($crawler->filter($domCategoryLinksExpr)->count() > 0);
$categoryLink = $crawler->filter($domCategoryLinksExpr)->eq(rand(1, $countCategories))->link();
$crawler = $client->click($categoryLink);
}
}
But when i run this test:
phpunit -c app src/WebSite/MainBundle/Tests/Controller/
I got this:
1) WebSite\MainBundle\Tests\Controller\ProductControllerTest::testPageContents
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: No route found for "GET /app_dev.php/catalog/psp"
...
/app_dev.php/catalog/psp is the dynamic value of $categoryLink->getUri(). And
this route exists and correctly works in web browser. Any ideas?
UPD:
This is my routing rules:
routing_dev.yml:
...
_main:
resource: routing.yml
....
routing.yml:
....
WebSiteCategoryBundle:
resource: "#WebSiteCategoryBundle/Controller/"
type: annotation
prefix: /
....
src/WebSite/CategoryBundle/CategoryController.php:
/**
* #Route("/catalog")
*/
class CategoryController extends Controller
{
/**
* #Route("/{alias}", name="show_category" )
* #Template()
*/
public function showAction( $alias )
{
// some action here
}
}
It works fine in browser, but seems like $crowler does`not see this annotation rules.
UPD2: The problem was in "routing_test.yml" which missing in Symfony 2 standard edition.
So I create it:
routing_test.yml:
_main:
resource: routing_dev.yml
and exception disappear. Thanks to all.
It would be nice if you posted your routing.yml
You can also take a look at:
http://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/routing.html
You can use routing annotation at your actions.
To solve your problem I would like to see your routing config.
echo $client->getResponse()->getContent() will help you with debugging (even there is an exception). It will output html of the request.
It looks like your route does not exist and might be in wrong location (wrong environment specified?)