ZF2: use service manager within a user service

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

ZF2: use service manager within a user service

Ralf Eggert
Hi,

another question for understanding.

I have a user server which handles registration, login, logout, password
reset and some other stuff. This service is used by the controller. The
service is stuffed with a couple of other objects which looks like this:

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'User\Service\User' =>  function($sm) {
                $mapper    = $sm->get('User\Mapper\User');
                $options   = $sm->get('User\Options\Module');
                $auth      = $sm->get('Zend\Authentication\Service');
                $mailer    = $sm->get('Application\Mail\Mailer');
                $service   = new UserService(
                    $mapper, $options, $auth, $mailer
                );
                return $service;
            },
        ),
    );
}

This works perfectly. But now I have a couple of form classe which also
need some factory configuration for adding input filters and stuff. Now
I could create all these forms in my factory for 'User\Service\User' as
well and pass it to my service. But then I will create a lot of forms
for each request which might not even be needed.

Is it good or bad practice to pass an instance of the service manager to
my 'User\Service\User' service?

With the service manager within my service I could use it to create form
objects on the fly with the defined factory.

What do others think about this approach?

Thanks and regards,

Ralf
Reply | Threaded
Open this post in threaded view
|

RE: ZF2: use service manager within a user service

demiankatz
If your User\Service\User class implements the ServiceLocatorAwareInterface, then the service manager will be automatically injected and you can access it using $this->getServiceLocator().  Whether or not this is the best approach is probably a matter of opinion and depends on the circumstances, but it's a useful pattern in the framework that you can exploit.

- Demian
________________________________________
From: Ralf Eggert [[hidden email]]
Sent: Friday, August 31, 2012 12:08 PM
To: [hidden email]
Subject: [zf-contributors] ZF2: use service manager within a user service

Hi,

another question for understanding.

I have a user server which handles registration, login, logout, password
reset and some other stuff. This service is used by the controller. The
service is stuffed with a couple of other objects which looks like this:

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'User\Service\User' =>  function($sm) {
                $mapper    = $sm->get('User\Mapper\User');
                $options   = $sm->get('User\Options\Module');
                $auth      = $sm->get('Zend\Authentication\Service');
                $mailer    = $sm->get('Application\Mail\Mailer');
                $service   = new UserService(
                    $mapper, $options, $auth, $mailer
                );
                return $service;
            },
        ),
    );
}

This works perfectly. But now I have a couple of form classe which also
need some factory configuration for adding input filters and stuff. Now
I could create all these forms in my factory for 'User\Service\User' as
well and pass it to my service. But then I will create a lot of forms
for each request which might not even be needed.

Is it good or bad practice to pass an instance of the service manager to
my 'User\Service\User' service?

With the service manager within my service I could use it to create form
objects on the fly with the defined factory.

What do others think about this approach?

Thanks and regards,

Ralf
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

weierophinney
Administrator
In reply to this post by Ralf Eggert
-- Ralf Eggert <[hidden email]> wrote
(on Friday, 31 August 2012, 06:08 PM +0200):

> I have a user server which handles registration, login, logout, password
> reset and some other stuff. This service is used by the controller. The
> service is stuffed with a couple of other objects which looks like this:
>
> public function getServiceConfig()
> {
>     return array(
>         'factories' => array(
>             'User\Service\User' =>  function($sm) {
>                 $mapper    = $sm->get('User\Mapper\User');
>                 $options   = $sm->get('User\Options\Module');
>                 $auth      = $sm->get('Zend\Authentication\Service');
>                 $mailer    = $sm->get('Application\Mail\Mailer');
>                 $service   = new UserService(
>                     $mapper, $options, $auth, $mailer
>                 );
>                 return $service;
>             },
>         ),
>     );
> }
>
> This works perfectly. But now I have a couple of form classe which also
> need some factory configuration for adding input filters and stuff. Now
> I could create all these forms in my factory for 'User\Service\User' as
> well and pass it to my service. But then I will create a lot of forms
> for each request which might not even be needed.
>
> Is it good or bad practice to pass an instance of the service manager to
> my 'User\Service\User' service?
>
> With the service manager within my service I could use it to create form
> objects on the fly with the defined factory.
>
> What do others think about this approach?

While it's expedient, it means soft-dependencies.

Another approach is to use events that enclose the service locator, and
when triggered, inject the User service with additional dependencies.

As an example, your controller could do the following:

    $this->getEventManager()->trigger('such-and-such-form');

In your module, you'd register an event that does the injection:

    public function onBootstrap($e)
    {
        $app = $e->getApplication();
        $this->services = $app->getServiceManager();
        $events = $this->services->get('SharedEventManager');

        $events->attach('My\Controller\SomeController', 'such-and-such-form', array($this, 'onSuchAndSuchForm'));
    }

    public function onSuchAndSuchForm($e)
    {
        $service = $this->services->get('User\Service\User');
        $form    = $this->services->get('SuchAndSuchForm');
        $service->setSuchAndSuchForm($form);
    }

This gives you the benefit of a context-specific injection, without
needing to pass the service locator around. Your code remains testable,
because you can inject the form into the service in your test scaffold
when needed. And the context-specificity is apparent based on the events
and how and where you attach.

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

Ralf Eggert
In reply to this post by demiankatz
Hi Demian,

Demian Katz schrieb am 31.08.2012 18:20:
> If your User\Service\User class implements the ServiceLocatorAwareInterface, then the service manager will be automatically injected and you can access it using $this->getServiceLocator().  Whether or not this is the best approach is probably a matter of opinion and depends on the circumstances, but it's a useful pattern in the framework that you can exploit.

Thanks for your reply. I will look into it.

Regards,

Ralf
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

Ralf Eggert
In reply to this post by weierophinney
Hi Matthew,

thanks for your reply.

> Another approach is to use events that enclose the service locator, and
> when triggered, inject the User service with additional dependencies.
>
> As an example, your controller could do the following:
>
>     $this->getEventManager()->trigger('such-and-such-form');

But since my user service handles the forms I would need to inject the
eventManager to my user service, wouldn't I?

> In your module, you'd register an event that does the injection:
>
>     public function onBootstrap($e)
>     {
>         $app = $e->getApplication();
>         $this->services = $app->getServiceManager();
>         $events = $this->services->get('SharedEventManager');
>
>         $events->attach('My\Controller\SomeController', 'such-and-such-form', array($this, 'onSuchAndSuchForm'));
>     }
>
>     public function onSuchAndSuchForm($e)
>     {
>         $service = $this->services->get('User\Service\User');
>         $form    = $this->services->get('SuchAndSuchForm');
>         $service->setSuchAndSuchForm($form);
>     }
>

Ok, but then I need to attach the event to my User\Service\User, to get
this working?

Regards,

Ralf

Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

weierophinney
Administrator
-- Ralf Eggert <[hidden email]> wrote
(on Friday, 31 August 2012, 06:48 PM +0200):

> Hi Matthew,
>
> thanks for your reply.
>
> > Another approach is to use events that enclose the service locator, and
> > when triggered, inject the User service with additional dependencies.
> >
> > As an example, your controller could do the following:
> >
> >     $this->getEventManager()->trigger('such-and-such-form');
>
> But since my user service handles the forms I would need to inject the
> eventManager to my user service, wouldn't I?

Make it EventManagerAware -- then it will be automatically injected with
it. You'll need to provide setEventManager and getEventManager methods,
and have the setEventManager method set appropriate identifiers, but
it's a nice trade-off.

> > In your module, you'd register an event that does the injection:
> >
> >     public function onBootstrap($e)
> >     {
> >         $app = $e->getApplication();
> >         $this->services = $app->getServiceManager();
> >         $events = $this->services->get('SharedEventManager');
> >
> >         $events->attach('My\Controller\SomeController', 'such-and-such-form', array($this, 'onSuchAndSuchForm'));
> >     }
> >
> >     public function onSuchAndSuchForm($e)
> >     {
> >         $service = $this->services->get('User\Service\User');
> >         $form    = $this->services->get('SuchAndSuchForm');
> >         $service->setSuchAndSuchForm($form);
> >     }
> >
>
> Ok, but then I need to attach the event to my User\Service\User, to get
> this working?

Yes -- simply attach to the service instead of the controller as I did
in the onBootstrap method.

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

Ralf Eggert
Hi Matthew,

I implemented it as suggested. My Module class:

-------------------------------------------------------------------------
class Module
{
    public function onBootstrap(EventInterface $e)
    {
        $e->getApplication()->getEventManager()->attachAggregate(
            new RouteListener()
        );

        [...]

        // get shared event manager
        $sharedEventManager = $e->getApplication()->getServiceManager()
                                ->get('SharedEventManager');

        // add form event
        $sharedEventManager->attach(
            'User\Service\User', 'set-register-form',
            array($this, 'onRegisterForm')
        );
    }

    public function onRegisterForm(EventInterface $e)
    {
        \Zend\Debug\Debug::dump('onRegisterForm');

        $service = $e->getApplication()->getServiceManager()
                     ->get('User\Service\User');
        $form    = $e->getApplication()->getServiceManager()
                     ->get('User\Form\UserRegister');
        $service->setRegisterForm($form);
    }
}
-------------------------------------------------------------------------

and my service class

-------------------------------------------------------------------------
class User implements EventManagerAwareInterface
{
    public function getRegisterForm()
    {
        if (null === $this->registerForm) {
            $result = $this->getEventManager()
                           ->trigger('set-register-form');

            \Zend\Debug\Debug::dump($result);
        }

        return $this->registerForm;
    }

    public function setRegisterForm(UserSaveForm $form)
    {
        $this->registerForm = $form;
    }
}
-------------------------------------------------------------------------

The output onRegisterForm from the Module::onRegisterForm() method is
not done. The output from the User::getRegisterForm() form looks like this:

-------------------------------------------------------------------------
object(Zend\EventManager\ResponseCollection)#312 (3) {
  ["stopped":protected] => bool(false)
  ["flags":"SplDoublyLinkedList":private] => int(6)
  ["dllist":"SplDoublyLinkedList":private] => array(0) {
  }
}
-------------------------------------------------------------------------

So, the trigger() call does not seem to work. What am I doing wrong?

Anyway, in my Module::onBootstrap() methode I am attaching a custom
listener with the normal Eventmanager. Why do I need to use the
SharedEventManager for this stuff?

I will go now and use my custom listener for the form setup. Maybe that
works...

Regards,

Ralf
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

Ralf Eggert
Hi again,

> I will go now and use my custom listener for the form setup. Maybe that
> works...

Just want to say that I did not manage to get this running with my
custom listener as well.

Please advise how to solve this.

Regards,

Ralf
Reply | Threaded
Open this post in threaded view
|

Re: ZF2: use service manager within a user service

_DyLeR_
This post has NOT been accepted by the mailing list yet.
In reply to this post by Ralf Eggert
Hi, could run the event in the service? If so, put the solution, I have the same problem ...