Search code examples
servicezend-framework2

Redirect inside a service class?


I've created my own service class and I have a function inside it, handleRedirect() that's supposed to perform some minimal logical check before choosing to which route to redirect.

class LoginService
{
    private $CartTable;
    private $SessionCustomer;
    private $Customer;

    public function __construct(Container $SessionCustomer, CartTable $CartTable, Customer $Customer)
    {
        $this->SessionCustomer  = $SessionCustomer;
        $this->CartTable        = $CartTable;
        $this->Customer         = $Customer;

        $this->prepareSession();
        $this->setCartOwner();
        $this->handleRedirect();
    }

    public function prepareSession()
    {
        // Store user's first name
        $this->SessionCustomer->offsetSet('first_name', $this->Customer->first_name);
        // Store user id
        $this->SessionCustomer->offsetSet('customer_id', $this->Customer->customer_id);
    }

    public function handleRedirect()
    {
        // If redirected to log in, or if previous page visited before logging in is cart page:
        //      Redirect to shipping_info
        //  Else
        //      Redirect to /
    }

    public function setCartOwner()
    {
        // GET USER ID FROM SESSION
        $customer_id = $this->SessionCustomer->offsetGet('customer_id');
        // GET CART ID FROM SESSION
        $cart_id = $this->SessionCustomer->offsetGet('cart_id');
        // UPDATE
        $this->CartTable->updateCartCustomerId($customer_id, $cart_id);
    }
}

This service is invoked in the controller after a successful login or registration. I'm not sure what's the best way to access redirect()->toRoute(); from here (or if I should do it here).

Also if you have other comments on how my code is structured please feel free to leave them.


Solution

  • Using plugins within your services is a bad idea as they require a controller to be set. When a service is created and you inject a plugin it has no idea of the controller instance so it will result in an error exception. If you want to redirect the user you might just edit the response object as the redirect plugin does.

    Notice that I stripped the code to keep the example clear and simple.

    class LoginServiceFactory implements FactoryInterface
    {
        public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
        {
            return new LoginService($container->get('Application')->getMvcEvent());
        }
    }
    
    class LoginService
    {
        /**
         * @var \Zend\Mvc\MvcEvent
         */
        private $event;
    
        /**
         * RedirectService constructor.
         * @param \Zend\Mvc\MvcEvent $event
         */
        public function __construct(\Zend\Mvc\MvcEvent $event)
        {
            $this->event = $event;
        }
    
        /**
         * @return Response|\Zend\Stdlib\ResponseInterface
         */
        public function handleRedirect()
        {
            // conditions check
            if (true) {
                $url = $this->event->getRouter()->assemble([], ['name' => 'home']);
            } else {
                $url = $this->event->getRouter()->assemble([], ['name' => 'cart/shipping-info']);
            }
    
            /** @var \Zend\Http\Response $response */
            $response = $this->event->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $url);
            $response->setStatusCode(302);
    
            return $response;
        }
    }
    

    Now from within your controller you can do the following:

    return $loginService->handleRedirect();