Leadtech/BootFramework

View on GitHub
src/Leadtech/Boot/Http/Service/AbstractService.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Boot\Http\Service;

use Boot\Http\Router\RouteMatch;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class AbstractService.
 */
abstract class AbstractService implements ServiceInterface
{
    /** @var  ContainerInterface */
    private $serviceContainer;

    /** @var  Request */
    private $request;

    /** @var  RouteMatch */
    private $routeMatch;

    /**
     * Make constructor protected. A service must be created using the createService factory method.
     *
     * @param ContainerInterface $serviceContainer
     */
    protected function __construct(ContainerInterface $serviceContainer)
    {
        $this->serviceContainer = $serviceContainer;
    }

    /**
     * Factory method to get a service instance.
     *
     * @param ContainerInterface $serviceContainer
     *
     * @return ServiceInterface
     */
    public static function createService(ContainerInterface $serviceContainer)
    {
        return new static($serviceContainer);
    }

    /**
     * @return ContainerInterface
     *
     * @codeCoverageIgnore   can ignore coverage, is out of scope, getter is for the concrete service implementation.
     */
    public function getServiceContainer()
    {
        return $this->serviceContainer;
    }

    /**
     * @param RouteMatch $routeMatch
     * @param Request    $request
     *
     * @throws \Exception
     *
     * @return response
     */
    public function invoke(RouteMatch $routeMatch, Request $request)
    {
        // Set route match & request
        $this->request = $request;
        $this->routeMatch = $routeMatch;

        try {

            // Trigger before the service method is invoked
            $this->preInvoke();

            // Execute method
            $response = $this->{$routeMatch->getMethodName()}();

            // Symfony works with any of the items below,  however I prefer a strongly typed interface.
            // The invoke method must always return an instance of response.
            if ($response === null || $response === '') {
                // do nothing
            } elseif ($response instanceof Response) {
                // do nothing...
            } elseif (is_array($response)) {
                $response = new JsonResponse($response);
            } elseif ($response instanceof \JsonSerializable) {
                $response = new JsonResponse($response->jsonSerialize());
            } elseif (is_scalar($response)) {
                $response = new Response($response);
            } else {
                throw new \DomainException('Invalid response format');
            }

            // Trigger after a service method is invoked and response validness is verified
            $this->onSuccess($response);

        } catch (\Exception $e) {
            // Optional handler to send alternative error response
            $response = $this->onException($e);
            if (!$response instanceof Response) {
                // Rethrow
                throw $e;
            }
        }

        return $response;
    }

    /**
     * Called before the service method is invoked...
     *
     * @return void
     */
    protected function preInvoke()
    {
        // optionally implement in concrete service
    }

    /**
     * Called after the service method is successfully invoked...
     *
     * @param Response $response
     *
     * @return void
     */
    protected function onSuccess(Response $response)
    {
        // optionally implement in concrete service
    }

    /**
     * Called in case of an uncaught exception after invoking a service method.
     * Optionally return a response object. When null is returned the framework will send a 500 response.
     *
     * @param \Exception $e
     * @return null|Response
     */
    protected function onException(\Exception $e)
    {
        // optionally implement in concrete service
        return null;
    }

    /**
     * @return Request
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * @return RouteMatch
     */
    public function getRouteMatch()
    {
        return $this->routeMatch;
    }
}