I am developing a application with symfony2. Im facing a problem with localization. I want to set the in the postLoad event in doctrine lifecycle, but can find a way to do that. I am using the route method to set my local for example:
http://example.com/en/content
here is my listener:
namespace MyApiBundle\Listener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
class LocaleListener
{
private $local;
public function __construct($local) {
$this->local = $local;
}
public function postLoad(LifecycleEventArgs $args)
{
$local= 'en'; // I need to get the local from here
$entity = $args->getEntity();
if(method_exists($entity, 'setLocale')) {
$entity->setLocale($local);
}
}
}
Is there any quick way get the local from here? Cant use the new Request() as it always returning the en I also have 3 other language. Thanks for help
Yes, you can. You can inject #request_stack service into your listener, get request from it and read locale.
There is, however, a Doctrine extension that probably does what you want: Translatable
Thanks #Igor Pantovic
here I got it work, here is my local listner:
#/src/MyApiBUndle/Listner/LocalListner.php
namespace MyApiBundle\Listener;
use Symfony\Component\HttpFoundation\RequestStack;
use Doctrine\ORM\Event\LifecycleEventArgs;
class LocaleListener {
private $requestStack;
/**
* #param RequestStack $requestStack
*/
public function __construct(RequestStack $requestStack) {
$this->requestStack = $requestStack;
}
/**
* #param LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
$local= $this->requestStack->getCurrentRequest()->getLocale();
$entity = $args->getEntity();
if(method_exists($entity, 'setLocale')) {
$entity->setLocale($local);
}
}
}
and my service
services:
my_api.listener.locale_listener:
class: MyApiBundle\Listener\LocaleListener
tags:
- { name: doctrine.event_listener, event: postLoad }
# #request_stack must be quoted "":
arguments: ["#request_stack"]
hope this will help other too
Related
i'm trying to load translations from database in Symfony 4. The Translator instance doesn't call the custom loader i wrote using this tutorial (https://medium.com/#andrew72ru/store-translation-messages-in-database-in-symfony-3f12e579df74).
I created dummy files in the /translation folder (messages.it.db) to trigger the loader but it doesn't get called.
services.yaml
parameters:
locales: ['it','en']
db_i18n.entity: App\Entity\Translation
services:
translation.loader.db:
class: App\Loader\DbLoader
arguments:
- '#service_container'
- '#doctrine.orm.entity_manager'
tags:
- { name: translation.loader, alias: db}
DbLoader.php
namespace App\Loader;
use Creative\DbI18nBundle\Interfaces\EntityInterface;
use Creative\DbI18nBundle\Interfaces\TranslationRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\MessageCatalogue;
class DbLoader implements LoaderInterface
{
/**
* #var EntityManagerInterface
*/
private $doctrine;
/**
* #var string
*/
private $entityClass;
public function __construct(ContainerInterface $container, EntityManagerInterface $doctrine)
{
$this->doctrine = $doctrine;
$this->entityClass = $container->getParameter('db_i18n.entity');
}
public function load($resource, $locale, $domain = 'messages')
{
$messages = $this->getRepository()->findByDomainAndLocale($domain, $locale);
$values = array_map(static function (EntityInterface $entity) {
return $entity->getTranslation();
}, $messages);
$catalogue = new MessageCatalogue($locale, [
$domain => $values
]);
return $catalogue;
}
public function getRepository(): TranslationRepositoryInterface
{
return $this->doctrine->getRepository($this->entityClass);
}
}
Here's my translation table
Here is the test code i'm using to call the Translator
TestController.php
class TestController extends AbstractController
{
/**
* #Route("/test", name="test")
*/
public function index(TranslatorInterface $translator)
{
$translator->trans('prova', [], 'messages', 'it');
return new Response();
}
}
The result is supposed to be "prova it" but I get "prova" instead, which is the key of the translation. I tried to put a dd() on the DbLoader constructor and it's never been called.
I also have in my project Api Platform, but i don't think it's causing this problem.
I resolved my issue.
By using dd() on my Translator instance i discovered that Symfony wasn't loading my translation files correctly. Looking through the properties i noticed the path of my translation files were not correct.
I placed them in src/Resources/translations instead and then it worked!
I am trying to inject public services like entityManager in the constructor of a service I created but I keep having this error :
Too few arguments to function App\Services\BillingInterface::__construct(), 0 passed in /var/www/.../src/Controller/TestController.php on line 144 and exactly 1 expected.
In my controllers, services are correctly injected in different methods but in the service I created it's not injected in the constructor.
I didn't change anything in the services.yaml as the documentation says autowire is automatic in Symfony 4.2
PS : I recently updated from Symfony 4.1 to 4.2 and I'm not sure but I think it worked before.
Maybe a library didn't updated correctly but I don't find any errors.
Here are the informations for the service
Service code :
#/src/Services/BillingInterface
namespace App\Services;
use Doctrine\ORM\EntityManagerInterface;
class BillingInterface {
private $em;
public function __construct(EntityManagerInterface $entityManager)
{
$this->em = $entityManager;
}
}
Controller code :
namespace App\Controller;
use App\Services\BillingInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestController extends AbstractController {
public function teest(EntityManagerInterface $entityManager)
{
$billing = new BillingInterface();
}
}
And If I instantiate BillingInterface with $entityManager parameter of Controller, it works but I would like it injected directly in the BillingInterface class constructor.
And finally, here is what is written in Symfony's documentation :
// src/Service/MessageGenerator.php
// ...
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function getHappyMessage()
{
$this->logger->info('About to find a happy message!');
// ...
}
}
Link : https://symfony.com/doc/current/service_container.html
Chapter : Injecting Services/Config into a Service
So, I don't know what's wrong with my Service.
Thank you for your answers.
Since your BillingInterface is a service - you need to use its instance that is provided by Symfony container instead of attempting to instantiate it by yourself. Your controller needs to inject this service in order to be able to use it:
namespace App\Controller;
use App\Services\BillingInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestController extends AbstractController
{
/**
* #var BillingInterface
*/
private $billing;
/**
* #param BillingInterface $billing
*/
public function __construct(BillingInterface $billing)
{
$this->billing = $billing;
}
public function teest(EntityManagerInterface $entityManager)
{
// Use $this->billing ...
}
}
I wanted to set password automatically when one register the form. So I use REGISTRATION_INITIALIZE to trigger the event. Unfortunately it's not working.
Listener:
<?php
namespace Acme\UserBundle\EventListener;
use Doctrine\ORM\EntityManager;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RegistrationListener implements EventSubscriberInterface
{
/**
* #var EntityManager
*/
private $em;
/**
* #param \Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInit',
);
}
public function onRegistrationInit(UserEvent $userEvent)
{
$user = $userEvent->getUser();
$user->setPassword('abcdeffffff');
}
Services:
#src/Acme/UserBundle/Resources/config/services.yml
services:
acme_user.registration:
class: Acme\UserBundle\EventListener\RegistrationListener
arguments:
entityManager: "#doctrine.orm.entity_manager"
tags:
- { name: kernel.event_subscriber}
So the password is not setting and it shows the password should not be blank.
What am I doing wrong? Any help!
EDIT:
The problem was I was defining service at the wrong place.
Instead of src/Acme/UserBundle/Resources/config/services.yml, It should be app/config/services.yml.
I saw src/Acme/UserBundle/Resources/config/services.ymlin http://symfony.com/doc/master/bundles/FOSUserBundle/controller_events.html, But was not working for me!
Maybe I am wrong, but I think your service definition uses features from Symfony 3.3 but you are using Symfony 3.2.8, e.g.
New in version 3.3: The ability to configure an argument by its name ($adminEmail) was added in Symfony 3.3. Previously, you could configure it only by its index (2 in this case) or by using empty quotes for the other arguments.
Try updating your service definition to:
#src/Acme/UserBundle/Resources/config/services.yml
services:
acme_user.registration:
class: Acme\UserBundle\EventListener\RegistrationListener
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_subscriber}
And a remark: as you are actually using a subscriber you should rename the class RegistrationListener to RegistrationSubscriber.
I'm new in Symfony 2.
I have a function called "addNewTarjeta" in a personalized entity respository.
<?php
namespace Elkanogroup\ClientesBundle\Repository;
/**
* ClienteRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ClienteRepository extends \Doctrine\ORM\EntityRepository {
/**
* Asigna una tarjeta a este cliente.
*/
public function addNewTarjeta(Cliente $cliente) {
$tarjeta = new \Elkanogroup\ClientesBundle\Entity\Tarjeta();
$tarjeta->setNumeroTarjeta('5555 5555 5555 5555');
$tarjeta->setCliente($cliente);
$tarjeta->setFechaExpedicion(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($tarjeta);
$flush = $em->flush();
if ($flush != null) {
return false;
}
return true;
}
I have a listener waiting for a doctrine event postPersist. I would like to call to "addNewTarjeta" from a postPersist function.
I'm trying to do something like this:
<?php
namespace Elkanogroup\ClientesBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Elkanogroup\ClientesBundle\Entity\Cliente;
use Elkanogroup\ClientesBundle\Repository\ClienteRepository;
class ClienteListener {
public function postPersist(Cliente $cliente, LifecycleEventArgs $args) {
$cliente->addNewTarjeta($cliente);
}
But it doesnt work. Symfony says:
Attempted to call an undefined method named "addNewTarjeta" of class
"Elkanogroup\ClientesBundle\Entity\Cliente".
Can anyone help me ?? Thanks and sorry for my bad english.
Everyone here says that you need to inject the entity manager but to me it's not true: you can retrive it from LifecycleEventArgs without inject anything.
Just do
$args->getObjectManager();
and you're done.
Just a note: usually repos are used to keep custom queries (via DQL or plain SQL or query builder). A logic like this should be fitted inside a service (a manager, helper or whatever).
As #dragoste said, you need to inject the entitymanager service into your listener.
It can be done in services.yml:
name.of.your.listener:
class: AppBundle\Listener\MyListener
arguments: ["#doctrine.orm.entity_manager"]
And then, add a public function __construct(\Doctrine\ORM\EntityManager $entityManager) method in your listener:
<?php
namespace AppBundle\Listener;
class MyListener
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $entityManager;
public function __construct(\Doctrine\ORM\EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
I setup a listener class where i'll set the ownerid column on any doctrine prePersist. My services.yml file looks like this ...
services:
my.listener:
class: App\SharedBundle\Listener\EntityListener
arguments: ["#security.context"]
tags:
- { name: doctrine.event_listener, event: prePersist }
and my class looks like this ...
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\SecurityContextInterface;
class EntityListener
{
protected $securityContext;
public function __construct(SecurityContextInterface $securityContext)
{
$this->securityContext = $securityContext;
}
/**
*
* #param LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
$entity->setCreatedby();
}
}
The result of this is the following error.
ServiceCircularReferenceException: Circular reference detected for service "doctrine.orm.default_entity_manager", path: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> my.listener -> security.context -> security.authentication.manager -> fos_user.user_manager".
My assumption is that the security context has already been injected somewhere in the chain but I don't know how to access it. Any ideas?
I had similar problems and the only workaround was to pass the whole container in the constructor (arguments: ['#service_container']).
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyListener
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
// ...
public function prePersist(LifeCycleEventArgs $args)
{
$securityContext = $this->container->get('security.context');
// ...
}
}
As of Symfony 2.6 this issue should be fixed. A pull request has just been accepted into the master. Your problem is described in here.
https://github.com/symfony/symfony/pull/11690
As of Symfony 2.6, you can inject the security.token_storage into your listener. This service will contain the token as used by the SecurityContext in <=2.5. In 3.0 this service will replace the SecurityContext::getToken() altogether. You can see a basic change list here: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service
Example usage in 2.6:
Your configuration:
services:
my.entityListener:
class: App\SharedBundle\Listener\EntityListener
arguments:
- "#security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
Your Listener
namespace App\SharedBundle\Listener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class EntityListener
{
private $token_storage;
public function __construct(TokenStorageInterface $token_storage)
{
$this->token_storage = $token_storage;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entity->setCreatedBy($this->token_storage->getToken()->getUsername());
}
}
For a nice created_by example, you can use https://github.com/hostnet/entity-blamable-component/blob/master/src/Listener/BlamableListener.php for inspiration. It uses the hostnet/entity-tracker-component which provides a special event that is fired when an entity is changed during your request. There's also a bundle to configure this in Symfony2
https://github.com/hostnet/entity-tracker-component
https://github.com/hostnet/entity-tracker-bundle
There's a great answer already in this thread but everything changes. Now there're entity listeners classes in Doctrine:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners-class
So you can add an annotation to your entity like:
/**
* #ORM\EntityListeners({"App\Entity\Listener\PhotoListener"})
* #ORM\Entity(repositoryClass="App\Repository\PhotoRepository")
*/
class Photo
{
// Entity code here...
}
And create a class like this:
class PhotoListener
{
private $container;
function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/** #ORM\PreRemove() */
public function preRemoveHandler(Photo $photo, LifecycleEventArgs $event): void
{
// Some code here...
}
}
Also you should define this listener in services.yml like that:
photo_listener:
class: App\Entity\Listener\PhotoListener
public: false
autowire: true
tags:
- {name: doctrine.orm.entity_listener}
I use the doctrine config files to set preUpdate or prePersist methods:
Project\MainBundle\Entity\YourEntity:
type: entity
table: yourentities
repositoryClass: Project\MainBundle\Repository\YourEntitytRepository
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
lifecycleCallbacks:
prePersist: [methodNameHere]
preUpdate: [anotherMethodHere]
And the methods are declared in the entity, this way you don't need a listener and if you need a more general method you can make a BaseEntity to keep that method and extend the other entites from that. Hope it helps!
Symfony 6.2.4
Add this in your Entity :
#[ORM\EntityListeners(["App\Doctrine\MyListener"])]
Add this in your services.yaml:
App\Doctrine\MyListener:
tags: [doctrine.orm.entity_listener]
Then you can do this :
<?php
namespace App\Doctrine;
use App\Entity\MyEntity;
use Symfony\Component\Security\Core\Security;
class MyListener
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function prePersist(MyEntity $myEntity)
{
//Your stuff
}
}
Hope it helps.