nicom974
nicom974

Reputation: 176

Symfony2 Unable to find controller inside an EventListener

I am trying to redirect to a controller from the EventListener but I get the following error message:

Unable to find controller "HRPortalSystemBundle:Home:login"

I am affirmative that the HomeController exists under \HRPortal\SystemBundle\Controller and that it does has a method called loginAction(). This action also has a route that works very well:

# routing.yml
login:
    path:    /login
    defaults: { _controller: HRPortalSystemBundle:Home:login }

My code is the following:

<?php
namespace HRPortal\SystemBundle\EventListener;

use HRPortal\SystemBundle\Controller\TokenAuthenticatedController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpFoundation\Request;

class TokenListener
{
    private $em;
    private $userRepo;
    private $session;

    public function __construct($em, $session)
    {
        $this->session = $session;
        $this->em = $em;
        $this->userRepo = $em->getRepository('HRPortalSystemBundle:Users');
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();

        if (!is_array($controller)) {
        return;
        }
        if ($controller[0] instanceof TokenAuthenticatedController) {
            if($this->session->has('id') && $this->session->has('token')){
                $sess_id = $this->session->get('id');
                $sess_token = $this->session->get('token');
                $user = $this->userRepo->findBy(array('id'=>$sess_id, 'token'=>$sess_token));
                if($user == null){
                    throw new AccessDeniedHttpException('We could not find the user');
                }else{
                    if($user->token != $sess_token){
                        throw new AccessDeniedHttpException('This action needs a valid token');
                    }
                }
            }else{
                $request = new Request();
                $resolver = new ControllerResolver();
                $request->attributes->set('_controller', 'HRPortalSystemBundle:Home:login');
                $event->setController($resolver->getController($request));
            }
        }
    }
}

Also I am not sure if the $resolver is used properly, as I guess this will be the next problem after solving this one.

Thanks in advance.

=== EDIT ===

I have done the following, and it seems it now finds the controller.

$request = new Request();
$resolver = new ControllerResolver();
$request->attributes->set('_controller', 'HRPortal\SystemBundle\Controller\HomeController::loginAction');
$event->setController($resolver->getController($request));

However, I get the following error message:

Error: Call to a member function get() on a non-object in /usr/local/apache2/htdocs/hrportal/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php line 106

So it seems like there's something wrong with the controller and the resolver.

Upvotes: 1

Views: 1718

Answers (2)

nicom974
nicom974

Reputation: 176

I finally worked out the problem. Thanks to Martin, I used the ControllerNameParser class to parse the Controller name. But I also needed to inject the container inside the EventListener from the services.yml file. I thought it'd be good to post the code for developers facing the same problem:

Services.yml:

parameters:
    session_handler.class:    HRPortal\SystemBundle\Services\SessionHandler
    token_listener.class:  HRPortal\SystemBundle\EventListener\TokenListener
    ...

services:
    session_handler:
        class:            "%session_handler.class%"
        arguments:
            em: @doctrine.orm.entity_manager
            session: @session
    tokens_listener:
        class:            "%token_listener.class%"
        arguments:
            em: @doctrine.orm.entity_manager
            container: @service_container
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
    ...

TokenListener.php

<?php
namespace HRPortal\SystemBundle\EventListener;

use HRPortal\SystemBundle\Controller\TokenAuthenticatedController;

use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpFoundation\Request;

use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;

class TokenListener extends Controller
{
    protected $em;
    protected $userRepo;
    protected $container;

    public function __construct($em, $container)
    {
        $this->container = $container;
        $this->em = $em;
        $this->userRepo = $em->getRepository('HRPortalSystemBundle:Users');
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $success = false;
        $controller = $event->getController();

        if (!is_array($controller)) {
            return;
        }
        if ($controller[0] instanceof TokenAuthenticatedController) {
            $session = $this->container->get('session_handler');
            $loggedIn = $session->isLoggedIn();
            if(!$loggedIn){
                $request = new Request();
                $request->attributes->set('_controller', 'HRPortal\SystemBundle\Controller\AuthController::loginAction');
                $parser = new ControllerNameParser($this->container->get('kernel'));
                $resolver = new ControllerResolver($this->container, $parser);
                $event->setController($resolver->getController($request)); 
            }
            return;
        }
    }
}

Upvotes: 0

Martin Lie
Martin Lie

Reputation: 1187

After having looked at the ControllerResolver source code, it seems like the getController method expects the _controller string to already be converted to a Classname::MethodName format (or a PHP callable).

This conversion is done by ControllerNameParser's parse method.

Upvotes: 2

Related Questions