I'm asking you for help because I got an error while trying to create a log service for tracing.
I followed this tutorial to perform this task:
https://nehalist.io/logging-events-to-database-in-symfony/
No problem occurred when creating the Log entity and the dbHandler service seems to run.
When I introduce the argument in services.yaml (arguments): ["#doctrine.orm.entity_manager"] and I use it in the MenuController constructor parameters, that's where the problem occurs.
Here is the error message: "It's a requirement to specify a Metadata Driver and pass it to Doctrine\ORM\Configuration::setMetadataDriverImpl()"
Thanks for your help.
Code error
services.yaml
services:
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event
subscribers, etc.
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/Tests/'
App\Controller\:
resource: '../src/Controller/'
tags: ['controller.service_arguments']
arguments: ['#monolog.logger.db']
dbHandler:
class: App\Service\DbHandler
arguments: ["#doctrine.orm.entity_manager"]
monolog.yaml
monolog:
channels: ['db']
handlers:
dbHandler:
type: service
id: dbHandler
DbHandler.php
<?php
namespace App\Service;
use App\Entity\Log;
use Monolog\Handler\AbstractProcessingHandler;
use Doctrine\ORM\EntityManagerInterface;
class DbHandler extends AbstractProcessingHandler {
private $em;
public function __construct(EntityManagerInterface $em) {
parent::__construct();
$this->em = $em;
}
protected function write($record):void {
$log = new Log();
$log->setMessage($record['message']);
$log->setLevel($record['level']);
$log->setLevelName($record['level_name']);
$log->setContext($record['context']);
$this->em->persist($log);
$this->em->flush();
}
}
MenuController.php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MenuController extends AbstractController
{
private $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* #Route("/menu", name="menu")
* #IsGranted("ROLE_USER")
*/
public function menu(LoggerInterface $logger )
{
$logger->info('Test');
}
}
Related
I am trying to use Sonata Admin and followed the documentation step-by-step: https://symfony.com/bundles/SonataAdminBundle/current/getting_started/creating_an_admin.html
However, as I try to run the project it gives the following error:
In AdminSearchCompilerPass.php line 75: Service
"admin.media" must implement
Sonata\AdminBundle\Admin\AdminInterface.
This is my MediaAdmin Class:
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class MediaAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $form): void
{
$form->add('name', TextType::class);
}
protected function configureDatagridFilters(DatagridMapper $datagrid): void
{
$datagrid->add('name');
}
protected function configureListFields(ListMapper $list): void
{
$list->addIdentifier('name');
}
protected function configureShowFields(ShowMapper $show): void
{
$show->add('name');
}
}
This is my services.yaml
parameters:
locale: 'en'
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
admin.media:
class: App\Admin\MediaAdmin
tags:
- { name: sonata.admin, model_class: App\Entity\Media, manager_type: orm, label: Media }
I was just confronted with the same error. It was due to a class name miss-typing in services.yaml.
When encountering this error, one should check that the configured Admin class exists and can be loaded by the auto-loader:
Filename and class name are the same (e.g. App\Admin\MediaAdmin class is in src/Admin/MediaAdmin.php.
Class name in services.yaml matches the Admin class.
The error is miss-leading. I filed a bug report.
I'm trying to change the db connection based on current subdomain.
So far I've managed to create a kernel.request listener and injecting the entity manager.
services.yaml
services:
app.listener.currentSubdomain:
class: App\EventListener\CurrentSubdomainListener
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_listener, event: kernel.request }
CurrentSubdomainListener.php
<?php
namespace App\EventListener;
use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class CurrentSubdomainListener{
protected $entityManager;
public function __construct(EntityManager $entityManager){
$this->entityManager = $entityManager;
}
public function onKernelRequest(RequestEvent $event){
//Change entity manager connection
}
}
How can I set the entity manager connection inside onKernelRequest(), so that when I auto wire it into any controllers it'll automatically use the connection I set?
I am upgrading from Symfony 3.3 to Symfony 4.
KNPMenu was running fine under Symfony 3.3, but now I am seeing this exception:
An exception has been thrown during the rendering of a template ("The
menu "main" is not defined.").
services.yaml
App\Menu\MenuBuilder:
public: true
tags:
- { name: app.menu_builder.admin, method: createAdminMenu, alias: admin }
- { name: app.menu_builder.calendar, method: createCalendarMenu, alias: calendar }
- { name: app.menu_builder.main, method: createMainMenu, alias: main }
- { name: app.menu_builder.trailer, method: createTrailerMenu, alias: trailer }
- { name: app.menu_builder.user, method: createUserMenu, alias: user }
MenuBuilder
Namespace App\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Security\Core\Security;
class MenuBuilder implements ContainerAwareInterface
{
use ContainerAwareTrait;
private $factory;
/**
* #param FactoryInterface $factory
*/
public function __construct( FactoryInterface $factory )
{
$this->factory = $factory;
}
I put a die('here'); in the __construct, it is never executed.
Using php bin/console debug:container menu yields:
Information for Service "App\Menu\MenuBuilder"
==============================================
---------------- -------------------------------------------------------------------------
Option Value
---------------- -------------------------------------------------------------------------
Service ID App\Menu\MenuBuilder
Class App\Menu\MenuBuilder
Tags app.menu_builder.admin (method: createAdminMenu, alias: admin)
app.menu_builder.calendar (method: createCalendarMenu, alias: calendar)
app.menu_builder.main (method: createMainMenu, alias: main)
app.menu_builder.trailer (method: createTrailerMenu, alias: trailer)
app.menu_builder.user (method: createUserMenu, alias: user)
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
---------------- -------------------------------------------------------------------------
How can I get MenuBuilder __construct to execute?
Thank you all for your time.
This gist was helpful: https://gist.github.com/lsv/4d8044d21819f28f0dde52a3fb8211a0
This answer was helpful: How to avoid "knp_menu.factory" deprecation?
services.yaml
app.menu_builder:
class: App\Menu\MenuBuilder
app.menu.main:
class: Knp\Menu\MenuItem
factory: ['#app.menu_builder', 'createMainMenu']
arguments: { $options: [] }
tags:
- { name: knp_menu.menu, alias: main }
I used php bin/console debug:autowiring security to get the security authorization checker
Then I updated MenuBuilder.php like so:
Namespace App\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class MenuBuilder
{
private $factory;
private $security;
/**
* #param FactoryInterface $factory
*/
public function __construct( FactoryInterface $factory, AuthorizationCheckerInterface $security )
{
$this->factory = $factory;
$this->security = $security;
}
Adding the SecurityAuthorizationChecker allows me to control the menu items offered with this statement:
if( $this->security->isGranted( 'IS_AUTHENTICATED_FULLY' ) )
An error occurred when I tried to put the service #doctrine.orm.entity_manager as an argument for my listener UserDeletionListener.
My service:
se.doctrine_listener.user_deletion:
class: SE\CoreBundle\DoctrineListener\UserDeletionListener
arguments:
- "#doctrine.orm.entity_manager"
tags:
- { name: doctrine.event_listener, event: preRemove }
My listener:
namespace SE\CoreBundle\DoctrineListener;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\EntityManager;
use SE\UserBundle\Entity\User;
class UserDeletionListener
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
/...
Here is the error:
Circular reference detected for service "doctrine.dbal.default_connection", path: "doctrine.dbal.default_connection".
when you use EventArgs {lifecycle, preUpdate, etc...], you don't have to pass doctrine.orm.entity_manager anymore :-D You can get it by the method getEntityManager of the eventArgs itself
Add Lazy loading to doctrine Event Listener
*
- { name: doctrine.event_listener, event: preRemove, lazy: true }
I am able to use the new autowire feature of Symfony2 to inject a service into a service. However, I cannot inject a service into a controller. What am I not doing/doing wrong?
This is my services.yml file:
services:
home_controller:
class: AppBundle\Controller\HomeController
autowire: true
This is my ServiceConsumerDemo:
namespace AppBundle\Services;
class ServiceConsumerDemo {
private $serviceDemo;
public function __construct(ServiceDemo $serviceDemo) {
$this->serviceDemo = $serviceDemo;
}
public function getMessage(){
return $this->serviceDemo->helloWorld();
}
}
This is ServiceDemo:
namespace AppBundle\Services;
class ServiceDemo
{
public function helloWorld(){
return "hello, world!";
}
}
This is HomeController (which works):
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class HomeController extends Controller
{
/**
* #Route("/")
*
*/
public function indexAction(Request $request)
{
$message[0] = $this->get('service_consumer_demo')->getMessage();
return new \Symfony\Component\HttpFoundation\JsonResponse($message);
}
}
This is HomeController which does not work
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Services\ServiceConsumerDemo;
class HomeController extends Controller
{
private $serviceConsumerDemo;
public function __construct(ServiceConsumerDemo $serviceConsumerDemo) {
$this->serviceConsumerDemo = $serviceConsumerDemo;
}
/**
* #Route("/", name="homepage")
*
*/
public function indexAction(Request $request)
{
$message[0] = $this->serviceConsumerDemo->getMessage();
return new \Symfony\Component\HttpFoundation\JsonResponse($message);
}
}
This is the error thrown:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Controller\HomeController::__construct() must be an instance
of AppBundle\Services\ServiceConsumerDemo, none given,
How do I access the Services in the Controller? I understand I need to declare the controller as a service. So I'm mentioning the controller in the services.yml file. Is there anything else that I need to do?
By default, controllers are not considered as services, so you can't use autowiring with them.
However, it's a simple enough fix - add a #Route annotation on the class (i.e. not on an action method in the class) with a service definition.
From the documentation:
The #Route annotation on a controller class can also be used to assign
the controller class to a service so that the controller resolver will
instantiate the controller by fetching it from the DI container
instead of calling new PostController() itself.
For example:
/**
* #Route(service="app_controller")
*/
class AppController extends Controller
{
private $service;
public function __construct(SomeService $service)
{
$this->service = $service;
}
/** #Route("/") */
public function someAction()
{
$this->service->doSomething();
}
}
and in your config:
app_controller:
class: AppBundle\Controller\AppController
autowire: true
Symfony 3.3+ May 2017 UPDATE!
Symfony 3.3 allows native way to approach this.
# app/config/services.yml
services:
_defaults:
autowire: true # all services in this config are now autowired
App\: # no more manual registration of similar groups of services
resource: ../{Controller}
This means App\Controller\SomeController is autowired by default and to be found in app/Controller/SomeController.php file.
You can read:
more about _defaults here
and more about PSR-4 service registration
Thanks to autowiring in Symfony 2.8+ I could create bundle, that adds autowiring even for controllers: Symplify/ControllerAutowire
You can read more about this in the article.
Usage is as simple as:
class YourController
{
public function __construct(SomeDependency $someDependency)
{
// ...
}
}
Nothing more is required.
You do not pass service to your controller in services.yml. Change it to:
services:
service_demo:
class: AppBundle\Services\ServiceDemo
service_consumer_demo:
class: AppBundle\Services\ServiceConsumerDemo
autowire: true
home_controller:
class: AppBundle\Controller\HomeController
autowire: true
arguments:
service: "#service_consumer_demo"