0

I have a plugin which looks up the user grants.

I declarated it in my module.config.php as follows:

'controller_plugins' => [
    'factories' => [
        Controller\Plugin\AccessPlugin::class => function($container) {
            return new Controller\Plugin\AccessPlugin(
                $container->get(Model\UserTable::class),
                $container->get(Model\GrantsTable::class),
                $container->get(Model\Authentication::class)
                );
        },
    ],
    'aliases' => [
        'access' => Controller\Plugin\AccessPlugin::class,
    ]
],

In my onDispatch(MvcEvent $event) event I want to fetch the http routing parameters, look up the grant and, if successful, redirect to the correct route.

  public function onDispatch(MvcEvent $event)
    {
            $controller = $event->getTarget();
            $controllerName = $event->getRouteMatch()->getParam('controller', null);
            $actionName = $event->getRouteMatch()->getParam('action', null);
            $actionName = str_replace('-', '', lcfirst(ucwords($actionName, '-')));
            $this->access()->checkAccess($controllerName, $actionName);


....

Of course the plugin can't be find, it isn't loaded yet:

Call to undefined method User\Module::access()

I'd like to call the plugin method anyway. Is there a possibility to use it in this case? Something like this:

        $grantplugin = fetch/call the plugin
        $isgranted = $grantplugin->checkAccess($controllerName, $actionName);

Any help appreciated!

**EDIT: I tried the solution ermengildo gave me. But it doesn't work, the reason is, that I haven't worked with factories yet. With a bit of help I can probably learn how to do it properly. Here my nippets:

I located the two services here:

location of services

I changed the module.php to (snippet!):

public function onDispatch(MvcEvent $event) {

    $controller = $event->getTarget();
    $controllerName = $event->getRouteMatch()->getParam('controller', null);
    $actionName = $event->getRouteMatch()->getParam('action', null);
    $actionName = str_replace('-', '', lcfirst(ucwords($actionName, '-')));
    $container = $event->getApplication()->getServiceManager();
    $accessService = $container->get(Service\AccessControl::class);
    $accessService->access()->checkAccess($controllerName, $actionName);

Last I tried to declarate the service as a factory:

'service_manager' => [
    'factories' => [
        // Avoid anonymous functions
        Service\AccessControl::class => Service\AccessControlFactory::class,
    ],
],

Remark: Here already I have the syntax warning: ...cannot be resolved to a type

If I debug, I get an exception:

Unable to resolve service "User\Service\AccessControl" to a factory; are you certain you provided it during configuration?

In the further explanation this line in my module.php shall be the problem:

 $accessService = $container->get(Service\AccessControl::class);
pia-sophie
  • 505
  • 4
  • 21

1 Answers1

1

The problem isn't that "the plugin hasn't been loaded yet", but that you are not inside a controller, and you are trying to call a controller plugin.

What you must do here is to create a service, retrive an instance from the service manager, and call the method on that instance.

Example

module.config.php

'service_manager' => [
    'factories' => [
        // Avoid anonymous functions
        \User\Service\AccessControl::class => \User\Service\AccessControlFactory::class
    ]
]

AccessControlFactory

namespace User\Service;

use Zend\ServiceManager\Factory\FactoryInterface;
use User\Service\AccessControl;

class AccessControlFactory implements FactoryInterface {

    /**
     * Factory called
     *
     * @param \Interop\Container\ContainerInterface $container
     * @param string $requestedName
     * @param array|null $options
     * @return TranslatorPlugin
     */
    public function __invoke(\Interop\Container\ContainerInterface $container, $requestedName, array $options = null) {
        $userTable = $container->get(\User\Model\UserTable::class);
        $grantsTable = $container->get(\User\Model\GrantsTable::class);
        $authentication = $container->get(\User\Model\Authentication::class);
        return new AccessControl($userTable, $grantsTable, $authentication);

    }

}

AccessControl

namespace User\Service;

use User\Model\UserTable;
use User\Model\GrantsTable;
use User\Model\Authentication;

class AccessControl {

    private $userTable;
    private $grantsTable;
    private $authentication;

    function __construct(UserTable $userTable, GrantsTable $grantsTable, Authentication $authentication) {
        $this->userTable = $userTable;
        $this->grantsTable = $grantsTable;
        $this->authentication = $authentication;

    }

    public function access(){
        // Do your stuff
    }

}

And finally, you can create it in your Module class and use it:

User\Module

public function onDispatch(MvcEvent $event) {
    $controller = $event->getTarget();
    $controllerName = $event->getRouteMatch()->getParam('controller', null);
    $actionName = $event->getRouteMatch()->getParam('action', null);
    $actionName = str_replace('-', '', lcfirst(ucwords($actionName, '-')));
    $container = $event->getApplication()->getServiceManager();
    $accessService = $container->get(\User\Service\AccessControl::class);
    $accessService->access()->checkAccess($controllerName, $actionName);

}

Side note

From your snippets, it is quite obvious what you are trying to achieve. I'd suggest you to take a look at this question/answer, and at the comment done by rkeet, because that's the proper way to do access controls. ;)

Ermenegildo
  • 1,286
  • 1
  • 12
  • 19
  • I tried it, knowing nothing about factories, I'm leaving with errors. I'd really like to understand because I could use this technic also for other things. So I edited my post and supplied some snippets. could you be so kind to have a second look? – pia-sophie Oct 15 '19 at 09:52
  • did you adapted the namespaces of the two classes? – Ermenegildo Oct 15 '19 at 10:04
  • I edited the code and added the `User` into the namespace. Keep in mind that this is an exemple, and not a "copy&paste&work", but it requires a bit of configuration, like choosing proper names, fixing namespaces, etc.. – Ermenegildo Oct 15 '19 at 10:07
  • As I already suggested you in a previous question, there is a good explaination in the [Zend tutorial](https://docs.zendframework.com/tutorials/getting-started/overview/), especially in the [Database and models section](https://docs.zendframework.com/tutorials/getting-started/database-and-models/) – Ermenegildo Oct 15 '19 at 10:10
  • There was one forgotten namespace Service; I adapted to namespace User\Service; That works now. Last I have trouble with this: $authentication = $container->get(AuthenticationService::class); "unable to resolve service" – pia-sophie Oct 15 '19 at 10:12
  • Yes,but I didn't understood everything in there. ;-( – pia-sophie Oct 15 '19 at 10:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200892/discussion-between-ermenegildo-and-pia-sophie). – Ermenegildo Oct 15 '19 at 11:21