I use Objectmanger for use Doctrine .. I create instance of objectManager and I create service but I have this bug:
Catchable Fatal Error: Argument 1 passed to Tag\TagBundle\Form\Types\TagsType::__construct() must be an instance of Doctrine\Common\Persistance\ObjectManager, instance of Doctrine\ORM\EntityManager given, called in /var/www/html/TagProject/var/cache/dev/appDevDebugProjectContainer.php on line 2412 and defined
code TagsType:
<?php
namespace Tag\TagBundle\Form\Types;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Tag\TagBundle\Form\DataTransformer\TagsTransformer;
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\Common\Persistance\ObjectManager;
class TagsType extends AbstractType {
/**
* #var ObjectManager
*/
private $manager;
public function __construct(ObjectManager $manager){
$this->manager = $manager;
}
public function buildForm(FormBuilderInterface $builder, array $options){
$builder
->addModelTransformer(new CollectionToArrayTransformer(),true)
->addModelTransformer(new TagsTransformer($this->manager),true);
}
public function getParent(){
return TextType::class;
}
}
code TagsTransformer:
<?php
namespace Tag\TagBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Tag\TagBundle\Entity\Tag;
use Doctrine\Common\Persistance\ObjectManager;
class TagsTransformer implements DataTransformerInterface {
/**
* #var ObjectManager
*/
private $manager;
public function __construct(ObjectManager $manager){
$this->manager = $manager;
}
public function transform($value) {
dump($value);
return implode(',',$value);
}
public function reverseTransform($string)
{
$names = explode(',',$string);
$tags = $this->manager->getRepository('TagBundle:Tag')->findBy(['name' => $names]);
$newNames = array_diff($names, $tags);
foreach($names as $name){
$tag = new tag();
$tag->setName($name);
$tags[] = $tag;
}
return $tags;
}
}
code services.yml:
services:
tag.form.types.tages:
class: Tag\TagBundle\Form\Types\TagsType
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: form.type }
# tag.example:
# class: Tag\TagBundle\Example
# arguments: ["#service_id", "plain_value", "%parameter%"]
You're injecting an instance of EntityManager so you should use the corresponding interface Doctrine\ORM\EntityManagerInterface for typehinting.
use Doctrine\ORM\EntityManagerInterface;
class TagsTransformer implements DataTransformerInterface
{
/**
* #var EntityManagerInterface
*/
private $manager;
public function __construct(EntityManagerInterface $manager)
{
$this->manager = $manager;
}
Related
Because I have many controllers, after the user is logged in, I want to transfer some user settings to Twig. But I don't want to make it in each controller:
Eg:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$user = $this->getUser();
$settings = $user->getSettings();
// ...
}
}
Is there a possibility to make it at a higher level from the call of the DefaultController?
Solution 1
Use app.user variable in Twig which is globally accessible:
{{ app.user.username }}
More info: https://symfony.com/doc/current/templates.html#the-app-global-variable
Solution 2
Write custom Twig function:
// src/Twig/UserExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Symfony\Component\Security\Core\Security;
class UserExtension extends AbstractExtension
{
Security $security
public function __construct(Security $security)
{
// Avoid calling getUser() in the constructor: auth may not
// be complete yet. Instead, store the entire Security object.
$this->security = $security;
}
public function getFunctions()
{
return [
new TwigFunction('get_user_settings', [$this, 'getUserSettings']),
];
}
public function getUserSettings()
{
$user = $this->security->getUser();
return $user->getSettings();
}
}
Usage in Twig:
{{ get_user_settings().setting1 }}
More info: https://symfony.com/doc/current/templating/twig_extension.html
How to inject current logged user: https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service
Ok, maybe I don't describe it clearly. This is the solution I have searched for:
<?php
namespace AppBundle\Subscriber;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class ControllerSubscriber implements EventSubscriberInterface
{
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var EntityManagerInterface
*/
private $em;
/**
* ControllerSubscriber constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param EntityManagerInterface $em
*/
public function __construct(TokenStorageInterface $tokenStorage, EntityManagerInterface $em)
{
$this->tokenStorage = $tokenStorage;
$this->em = $em;
}
/**
* #param FilterControllerEvent $event
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if (!$token = $this->tokenStorage->getToken()) {
return;
}
if (!$user = $token->getUser()) {
return;
}
// if no user
if (!$user instanceof UserInterface) {
return;
} else {
// .... do something
}
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::CONTROLLER => array(array('onKernelController', 1)),
);
}
}
services.yml
app.controller.subscriber:
class: AppBundle\Subscriber\ControllerSubscriber
arguments: ["#security.token_storage","#doctrine.orm.default_entity_manager"]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
In a symfony 4 project, many services/controllers need log.
Trying to use the advantage of traits & autowire options given by symfony, I created a loggerTrait that will be passed to the different services.
namespace App\Helper;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
trait LoggerTrait
{
/** #var LoggerInterface */
private $logger;
/** #var array */
private $context = [];
/**
* #return LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return $this->logger;
}
/**
* #required
*
* #param LoggerInterface|null $logger
*/
public function setLogger(?LoggerInterface $logger): void
{
$this->logger = $logger;
}
public function logDebug(string $message, array $context = []): void
{
$this->log(LogLevel::DEBUG, $message, $context);
}
...
}
(inspired by symfonycasts.com)
A service will be using this trait
namespace App\Service;
use App\Helper\LoggerTrait;
class BaseService
{
use LoggerTrait;
/** #var string */
private $name;
public function __construct(string $serviceName)
{
$this->name = $serviceName;
}
public function logName()
{
$this->logInfo('Name of the service', ['name' => $this->name]);
}
}
It works perfectly but I couldn't succeed to test it.
I tried to extend KernelTestCase in my test to mock an loggerInterface but I receive Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "Psr\Log\LoggerInterface" service is private, you cannot replace it which make perfect sens.
Here my test:
namespace App\Tests\Service;
use App\Service\BaseService;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class BaseServiceTest extends KernelTestCase
{
private function loggerMock()
{
return $this->createMock(LoggerInterface::class);
}
protected function setUp()
{
self::bootKernel();
}
/**
* #test
* #covers ::logName
*/
public function itShouldLogName()
{
// returns the real and unchanged service container
$container = self::$kernel->getContainer();
// gets the special container that allows fetching private services
$container = self::$container;
$loggerMock = $this->loggerMock();
$loggerMock->expect(self::once())
->method('log')
->with('info', 'Name of the service', ['name' => 'service_test']);
$this->logger = $container->set(LoggerInterface::class, $loggerMock);
$baseService = new BaseService('service_test');
var_dump($baseService->getLogger());
}
}
Is there a solution to test such a logger inside the service ?
You can override the service to be public (only for the test environment) in your config_test.yml as follows:
services:
Psr\Log\LoggerInterface:
public: true
This is commonly done for testing private services.
In Symfony2 it was straightforward to override the RoutingExtension, that I could inject some extra parameters.
I'm using a dynamic domain to route to different parts of my application.
{subdomain}.domain.com
However, I don't want to have to specify subdomain every time I call path or url in twig.
I could create my own unique filter name, but I'd rather not.
Previously, we could put this in the services.yaml file and it would work.
services:
twig.extension.routing:
class: AppBundle\Twig\Extension\RoutingExtension
public: false
arguments:
- '#router'
- '#request_stack'
- '%domain%'
Symfony2 Twig overriding default path function
With Symfony Flex, all I get is Unable to register extension "App\TwigExtension\TwigRoutingExtension" as it is already registered.
This is how to override Twig's routing path/url functions in Symfony 5.x using AbstractExtension:
namespace App\Twig;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class MyRoutingExtension extends AbstractExtension
{
public function __construct(
private UrlGeneratorInterface $generator
){}
public function getFunctions(): array
{
return [
new TwigFunction('path', [$this, 'getPath']),
new TwigFunction('url', [$this, 'getUrl']),
];
}
public function getPath(string $name, array $parameters = [], bool $relative = false): string
{
return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
}
public function getUrl(string $name, array $parameters = [], bool $schemeRelative = false): string
{
return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL);
}
}
In SF4 it's even simpler:
first composer require symfony/twig-bundle twig/extensions
Normally autowiring is enable so you can simply do:
<?php
namespace App\Twig;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class TestExtension extends RoutingExtension
{
public function __construct(UrlGeneratorInterface $generator)
{
parent::__construct($generator);
}
public function getPath($name, $parameters = array(), $relative = false)
{
return parent::getPath($name, $parameters, $relative);
}
}
If you want to setup a service forget about arguments definition it's boring :). Assuming your %domain% is available in your parameter do something like this:
<?php
namespace App\Twig;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RouterInterface;
class TestExtension extends RoutingExtension
{
/** #var RouterInterface $router */
protected $router;
/** #var RequestStack $stack */
protected $stack;
/** #var mixed $domain */
protected $domain;
/**
* TestExtension constructor.
*
* #param RouterInterface $router
* #param RequestStack $stack
* #param ParameterBagInterface $bag
*/
public function __construct(RouterInterface $router, RequestStack $stack, ParameterBagInterface $bag)
{
$this->router = $router;
$this->stack = $stack;
$this->domain = $bag->get('domain');
}
public function getPath($name, $parameters = array(), $relative = false)
{
return parent::getPath($name, $parameters, $relative);
}
}
I have a form subscriber, i want to use doctrine manager in it. but i got the error message:
"Catchable Fatal Error: Argument 1 passed to ...\ProductEavAttributesSubscriber::__construct() must be an instance of Doctrine\ORM\EntityManager, instance of Symfony\Component\Form\FormFactory given。
there is my subscriber file code:
namespace Demo\Bundle\ProductBundle\EventListener;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
class ProductEavAttributesSubscriber implements EventSubscriberInterface
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
// coding
}
in service.yml
demo.product.eav.attribute:
class: ...\EventListener\ProductEntityAttributesSubscriber
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: form.type, alias: product_eav_attribute }
In form type:
$builder->add('specifications', ProductEavAttributeType::class);
In the ProductEavAttributeType file:
class ProductEavAttributeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$factory = $builder->getFormFactory();
$productEavAttributes = new ProductEavAttributesSubscriber($factory);
$builder->addEventSubscriber($productEavAttributes);
}
}
So, how to do now?
I have a problem in redirect path according to role in FOSuserBndle.The problem is in :
FatalErrorException: Error: Call to a member function generate() on a
non-object in.....
This is the event listener
namespace Register\UserBundle\EventListener;
///////////////////////////////
//////////////////////////////
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine; // for Symfony 2.1.0+
// use Symfony\Bundle\DoctrineBundle\Registry as Doctrine; // for Symfony 2.0.x
/**
* Custom login listener.
*/
class LoginListener
{
protected $router;
/** #var \Symfony\Component\Security\Core\SecurityContext */
private $securityContext;
/** #var \Doctrine\ORM\EntityManager */
private $em;
/**
* Constructor
*
* #param SecurityContext $securityContext
* #param Doctrine $doctrine
*/
public function __construct( SecurityContext $securityContext, Doctrine $doctrine )
{
$this->securityContext = $securityContext;
$this->em = $doctrine->getEntityManager();
}
/**
* Do the magic.
*
* #param InteractiveLoginEvent $event
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
if ($this->securityContext->isGranted('ROLE_PATIENT')) {
$response = new RedirectResponse($this->router->generate('demands'));
//this->redirect($this->generateUrl('home'));
}
if ($this->securityContext->isGranted('ROLE_THERAPIST')) {
// user has logged in using remember_me cookie
print_r("FFFFFFFFFFFFFTHERAPIST");
die;
}
// do some other magic here
$user = $event->getAuthenticationToken()->getUser();
$type = $user->getType()->getId();
print_r( $type);
die;
// ...
}
}
It seems that the router is not injected in your listener/service.
Listener class :
...
use Symfony\Component\Routing\Router;
class LoginListener
{
protected $container;
protected $em;
protected $router;
public function __construct(ContainerInterface $container, EntityManager $em, Router $router)
{
$this->container = $container;
$this->em = $em;
$this->router = $router;
}
...
}
Service declaration :
my_bundle.my_listener:
class: %my_bundle.my_listener.class%
arguments: [ #service_container, #doctrine.orm.entity_manager, #router ]