2

I'm trying to implement multiple controllers which listens to one route /account.

There are two controllers and only one should be executed on that URL where the choice lies within user's role.

namespace AppBundle\Controller;

use AppBundle\Entity\Role;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route("/account")
 */
abstract class DashboardController extends Controller
{
    protected $userRoles;

    public function __construct()
    {
        $this->userRoles = $this->getUser()->getRoles();
    }

    /**
     * Get all user roles
     */
    public function getRoles()
    {
        return $this->userRoles;
    }

    /**
     * Get user account type
     *
     * @return Role
     */
    public function getAccountType(): Role
    {
        $accountType = new Role();
        foreach ($this->userRoles as $role) {
            if(Role::ROLE_STUDENT == $role->getName()) {
                $accountType = $role;
            } else if(Role::ROLE_SCHOOL_REPRESENTATIVE == $role->getName()) {
                $accountType = $role;
            } else if(Role::ROLE_EMPLOYER == $role->getName()) {
                $accountType = $role;
            } else if(Role::ROLE_ADMIN == $role->getName()) {
                $accountType = $role;
            }
        }

        return $accountType;
    }
}
namespace AppBundle\Controller;

class CompanyDashboardController extends DashboardController
{

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @Route("/", name="dashboard_company_home", methods={"GET"})
     * @return Response
     */
    public function index()
    {
        return $this->render('dashboard/company/index.html.twig');
    }
}
namespace AppBundle\Controller;

class AdminDashboardController extends DashboardController
{

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @Route("/", name="dashboard_admin_home", methods={"GET"})
     * @return Response
     */
    public function index()
    {
        return $this->render('dashboard/admin/index.html.twig');
    }
}

That's what I've got so far.

yivi
  • 42,438
  • 18
  • 116
  • 138
Nick
  • 169
  • 1
  • 10
  • Why not include the return statements in the appropriate `getAccountType()` method? For example, `if(Role::ROLE_ADMIN == $role->getName()) { return $this->render('dashboard/admin/index.html.twig');...` – geoB Feb 25 '20 at 19:24
  • Because there are many other files and those two dashboards act differently.They've got nothing in common.As I mentioned, I must render one controller according to some condition, and in our case, that's user's role. – Nick Feb 25 '20 at 19:38
  • 1
    A question: does the path to the route `/account` appear in more than one place? If only one place another option is to use Twig's `{% if is_granted(...) %} {{render(controller...` as in this [doc](https://symfony.com/doc/current/templates.html#embedding-controllers) – geoB Feb 25 '20 at 19:45
  • That path occurs only once, the url is being changed, after logging in, to `/account`.Then the main controller (DashboardController) must decide which one to execute.I've never worked with embedded controllers but I will check it.More advice will be appreciated! – Nick Feb 25 '20 at 19:59
  • 2
    an alternative is the `forward` method every default controller has, with which you could implement a "multiplexer" (which this essentially is) very basic docs: https://symfony.com/doc/current/controller/forwarding.html you can't have two routes with the same path, they will override one another (the latter wins), so you need a "central" route that forwards to the appropriate methods/controllers. or you chose different paths, which would probably be much simpler. – Jakumi Feb 25 '20 at 20:00
  • Please show how `...the url is being changed, after logging in...` – geoB Feb 25 '20 at 20:12
  • 1
    Pretty simple.After post request in order to try to sign in, on success, we redirect to the respective path: `window.location.href = '/account';`And then logic begins.But, I believe this is harder as Jakumi said.I'll reckon on refactoring the whole thing.The easier way would be just to have three different urls. – Nick Feb 25 '20 at 20:24

1 Answers1

1

You can't do this with "route" declarations, since the route listener is executed with higher priority than the security listener. Both happen during the KernelEvents::REQUEST event, but routing comes before firewall.

When the route to controller mapping is being resolved, you do not have yet user information (which is why you can't simply attach another a listener and inject the user information on the Request object, so it's available to use in the route declaration for expression matching, for example).

Basically, one route, one controller. If you want to have diverging logic for these users, you'll have to apply it after you get into the controller.

yivi
  • 42,438
  • 18
  • 116
  • 138