Using symfony2. I have a listener class that is attempting to call a method from a different class (a controller) like so:
$authenticate = new AuthenticationController();
$authenticate->isTokenValid($token);
And the controller isTokenValid method:
public function isTokenValid($token) {
$conn = $this->get('database_connection');
Is throwing the error
Fatal error: Call to a member function get() on a non-object in /home/content/24/9254124/html/newsite/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php on line 246
If i load the controller method the proper way (using routing in the url) it works fine.
Symfony2 uses Dependency Injection pattern, you have to inject container that holds all services (like database connection):
$authenticate = new AuthenticationController();
$authenticate->setContainer($this->container);
$authenticate->isTokenValid($token);
Of course I assume here that your listener class is ContainerAware
[+] To make your listener ContainerAware, pass #service_container to it (example form services.yml)
my.listener:
class: ACME\MyBundle\ListenerController
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
kernel.event_listener:
event: kernel.controller
and then in constructor of you listener class:
public function __construct($container = null){
$this->container = $container;
}
I'm adding another answer because what #dev-null-dweller suggests is a bad practice: in almost every case you better to inject only the services you need — not the whole container:
use Doctrine\DBAL\Connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
my_listener:
arguments: [ #database_connection ]
Related
I created symfony service as described here: Symfony2: get Doctrine in a generic PHP class
Service.yml
app.lib.helpers:
class: AppBundle\Lib\Helpers
arguments: [doctrine.orm.entity_manager]
Class Helpers
class Helpers
{
protected $em;
public function __construct($entityManager)
{
$this->em = $entityManager;
}
public function checkStatuses()
{
$orderId = $this->em->getRepository('AppBundle:Orders')->findAll();
}
}
In controller action
$helper = $this->get('app.lib.helpers');
$helper->checkStatuses();
But im getting error:
Error: Call to a member function getRepository() on string
What causes that problem?
I think it should be
arguments: [ "#doctrine.orm.entity_manager" ]
in your Service.yml
Your service definition not correct, try this or you can read this for more explain http://symfony.com/doc/current/service_container.html:
app.lib.helpers:
class: AppBundle\Lib\Helpers
arguments: [#doctrine.orm.entity_manager]
I created a Listener Service to listen for the Login so that I could perform some database operations after login. I'm trying to inject another service into my Listener but I'm getting a fatal error:
Catchable fatal error: Argument 1 passed to
WX\ExchangeBundle\Service\SecurityListener::__construct() must be an
instance of WX\ExchangeBundle\Service\UserService, none given
config.yml:
services:
wxexchange_user_service:
class: WX\ExchangeBundle\Service\UserService
arguments: [#doctrine.orm.default_entity_manager]
wxexchange_login_listener:
class: WX\ExchangeBundle\Service\SecurityListener
arguements: [#wxexchange_user_service]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
SecurityListener:
namespace WX\ExchangeBundle\Service;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use WX\ExchangeBundle\Service\UserService;
class SecurityListener
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$lastLogin = new DateTime();
$user = $event->getAuthenticationToken()->getUser();
$this->userService->setLastLogin($user, $lastLogin);
}
}
I've already tried clearing the cache, but that did not resolve the issue.
There is a typo in your config.yml:
arguements: [#wxexchange_user_service]
must be:
arguments: [#wxexchange_user_service]
(note the 'e' in arguements)
Because of this, the SecurityListener is instantiated without any arguments, but it requires an object of type UserService. That is why you get the error message.
I am trying to create a service in Symfony2 to automatically pass Doctrine\ORM\EntityManager to __construct to avoid having to pass it each time I instantiate the class, i.e.
// use this
$TestClass= new TestClass;
// instead of this
$entityManager = $this->getDoctrine()->getEntityManager();
$TestClass= new TestClass($entityManager);
I created a class EntityManagerUser, tried to register it as a service and TestClass extends that.
services.yml is included, as another service works, and I've double-checked by adding (then removing) a syntax error.
I read the docs, this, this and this and I've ended up with the code below, which doesn't pass #doctrine.orm.entity_manager. However, the controller_listener service does receive #templating.
I've cleared cache via the console and manually deleted app/cache but I still see this error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Test\TestBundle\ServiceUser\EntityManagerUser::__construct() must be an instance of Test\TestBundle\ServiceUser\Doctrine\ORM\EntityManager, none given, called in D:\Documents\www\Test\live\src\Test\TestBundle\Controller\MyController.php on line 84 and defined in D:\Documents\www\Test\live\src\Test\TestBundle\ServiceUser\EntityManagerUser.php line 14
services.yml
services:
# this one doesn't throw an error and passes #templating to __construct
test.eventlistener.before_controller_listener:
class: Test\TestBundle\Eventlistener\BeforeControllerListener
arguments: [ #templating ]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# the following one doesn't pass #doctrine.orm.entity_manager
test.service_user.entity_manager_user:
class: Test\TestBundle\ServiceUser\EntityManagerUser
arguments: [ #doctrine.orm.entity_manager ]
src/Test/TestBundle/ServiceUser/EntityManagerUser.php
namespace Test\TestBundle\ServiceUser;
use Doctrine\ORM\EntityManager;
class EntityManagerUser{
protected $entityManager;
public function __construct(EntityManager $entityManager){
$this->entityManager = $entityManager;
// N.B. it's not possible to do it this way:
// $this->entityManager = new EntityManager;
}
// also tried public function __construct($entityManager){
// and public function __construct(Doctrine\ORM\EntityManager $entityManager){
}
src/Test/TestBundle/Classes/TestClass.php
namespace Test\TestBundle\Classes\TestClass;
use Test\TestBundle\ServiceUser\EntityManagerUser;
class TestClass extends EntityManagerUser{
/* currently no functions */
}
In my controller, line 84
$test= new TestClass;
// I tested that this throws the same error, it does // $test= new EntityManagerUser;
What have I missed?
Services only get their arguments if they are called through the service constructor:
$this->get('test.service_user.entity_manager_user');
Declaring the class as a service doenst make a difference if you create a new class and extend the original.
What you could do is also declare this new class as a service and still have it extend the base class.
test.classes.test_class:
class: Test\TestBundle\Classes\TestClass\TestClass
arguments: [ #doctrine.orm.entity_manager ]
then you dont have to define the constructor in the extended class because it is the parent.
then get the class by doing:
$testClass = $this->get('test.classes.test_class');
//will be instanceof Test\TestBundle\Classes\TestClass\TestClass
I worked out how to do what I wanted, which was not define entityManager each time I was required in an instanciated class.
It made sense to rename EntityManagerUser to ContainerListener, a static class, and inject #service_container into it through services, so it can then also return other classes.
namespace Test\TestBundle\EventListener;
class ContainerListener{
static $container;
// knock out the parent::onKernelRequest function that we don't want
public function onKernelRequest($event){
return;
}
public function __construct($container){
self::$container = $container;
}
static function twig(){
return self::$container->get('twig');
}
static function entityManager(){
return self::$container->get('doctrine')->getEntityManager();
}
static function entityManagerConnection(){
$entityManager = self::$container->get('doctrine')->getEntityManager();
return $entityManager->getConnection();
}
}
services.yml
services:
test.event_listener.container_listener:
class: Test\TestBundle\EventListener\ContainerListener
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
BaseClass.php gets entityManager
namespace Test\TestBundle\Class;
use Test\TestBundle\EventListener\ContainerListener;
class BaseClass{
public function __construct(){
$this->entityManager = ContainerListener::entityManager();
}
}
TestClass.php extends BaseClass as do others
class TestClass extends BaseClass(){
function someFunction(){
// etc etc
// $this->entityManager exists with no construct and without passing it
$stmt = $this->entityManager->getConnection()->prepare( $some_sql );
// etc etc
}
}
Somewhere in DefaultController.php
# nope # $entityManager = $this->getDoctrine()->getEntityManager();
# nope # $TestClass= new TestClass($entityManager);
$TestClass= new TestClass; # win!
I am currently using the Symfony2 event listener to change the controller to a different one based on a users authentication status. I get the listener to set the new controller but it is instantiated without the container parameter (i.e. $this->container returns null).
Is there anyway to pass the container on to the controller I am changing to?
class AuthenticationListener
{
public function onController(FilterControllerEvent $event)
{
$request = $event->getRequest();
$session = $request->getSession();
if (!$session->has('authenticated') || $session->get('authenticated') === false)
{
$controller = $event->getController();
if (!($controller[0] instanceof AuthenticateController) && !($controller[0] instanceof ExceptionController))
{
$event->setController(array(new AuthenticateController(), 'loginAction'));
}
}
}
}
The container is not set, when you create the controller automatically. Call setContainer after constructing the controller. Afterwards you can pass it to the event.
In this case AuthenticationListener its just a class
if you want to use $this->container in this class you must do like this:
class BeforeControllerListener extends ContainerAware
{
...
}
and in config.yml
core.listener.before_controller:
class: App\YourBundle\EventListener\YourListener
tags: [ {name: kernel.event_listener, event: kernel.controller, method: onKernelController}]
calls:
- [ setContainer,[ #service_container ]]
I need to get doctrine working inside my helper, im trying to use like i normaly do in a controller:
$giftRepository = $this->getDoctrine( )->getRepository( 'DonePunctisBundle:Gift' );
But this gave me:
FATAL ERROR: CALL TO UNDEFINED METHOD
DONE\PUNCTISBUNDLE\HELPER\UTILITYHELPER::GETDOCTRINE() IN
/VAR/WWW/VHOSTS/PUNCTIS.COM/HTTPDOCS/SRC/DONE/PUNCTISBUNDLE/HELPER/UTILITYHELPER.PH
What Im missing here?
EDIT:
services file
services:
templating.helper.utility:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#service_container]
tags:
- { name: templating.helper, alias: utility }
Firts lines of helper file
<?php
namespace Done\PunctisBundle\Helper;
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Templating\EngineInterface;
class UtilityHelper extends Helper {
/*
* Dependency injection
*/
private $container;
public function __construct( $container )
{
$this->container = $container;
}
The problem here is that your Helper class is not container-aware; that is, it has no idea about all the services Symfony has loaded (monolog, twig, ...and doctrine).
You fix this by passing "doctrine" to it. This is called Dependency Injection, and is one of the core things that makes Symfony awesome. Here's how it works:
First, give your Helper class a place for the Doctrine service to live, and require it in the Helper's constructor:
class UtilityHelper
{
private $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function doSomething()
{
// Do something here
}
}
Then, you use services.yml to define how Symfony should construct that instance of Helper:
services:
helper:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#doctrine]
In this case, #doctrine is a placeholder that means "insert the Doctrine service here".
So now, in your Controller, or in anything else that is container-aware, you can get access to Doctrine through the Helper class like this:
class SomeController()
{
public function someAction()
{
$this->get("helper")->doctrine->getRepository(...);
}
}
EDIT
After looking at your edit, it appears that you're injecting the entire service container into the Helper class. That's not a best practice -- you should only inject what you need. However, you can still do it:
services.yml
services:
helper:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#service_container]
UtilityHelper.php
class UtilityHelper
{
private $container;
public function __construct($container)
{
$this->container = $container;
}
public function doSomething()
{
// This won't work, because UtilityHelper doesn't have a getDoctrine() method:
// $this->getDoctrine()->getRepository(...)
// Instead, think about what you have access to...
$container = $this->container;
// Now, you need to get Doctrine
// This won't work... getDoctrine() is a shortcut method, available only in a Controller
// $container->getDoctrine()->getRepository(...)
$container->get("doctrine")->getRepository(...)
}
}
I've included a few comments there that highlight some common pitfalls. Hope this helps.
In Helper, Services etc you cannot use it like in actions.
You need to pass it like argument to youre Helper via service description in conf file(services.yml or *.xml).
Example:
services:
%service_name%:
class: %path_to_youre_helper_class%
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: %name% }
And dont forget catch it in __construct of youre Helper.
Example:
use Doctrine\ORM\EntityManager;
....
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
You can use it like:
public function myMethod()
{
$repo = $this->em->getRepository('DonePunctisBundle:Gift');
}