RebelCode/rcmod-eddbk-cart

View on GitHub
src/Module/RenderCartBookingInfoHandler.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace RebelCode\EddBookings\Cart\Module;

use ArrayAccess;
use Carbon\Carbon;
use Dhii\Cache\ContainerInterface as CacheContainerInterface;
use Dhii\Data\Container\ContainerGetCapableTrait;
use Dhii\Data\Container\ContainerGetPathCapableTrait;
use Dhii\Data\Container\ContainerHasCapableTrait;
use Dhii\Data\Container\CreateContainerExceptionCapableTrait;
use Dhii\Data\Container\CreateNotFoundExceptionCapableTrait;
use Dhii\Data\Container\Exception\NotFoundExceptionInterface;
use Dhii\Data\Container\NormalizeContainerCapableTrait;
use Dhii\Data\Container\NormalizeKeyCapableTrait;
use Dhii\Exception\CreateInvalidArgumentExceptionCapableTrait;
use Dhii\Exception\CreateOutOfRangeExceptionCapableTrait;
use Dhii\Exception\CreateRuntimeExceptionCapableTrait;
use Dhii\I18n\StringTranslatingTrait;
use Dhii\Invocation\InvocableInterface;
use Dhii\Iterator\CountIterableCapableTrait;
use Dhii\Iterator\ResolveIteratorCapableTrait;
use Dhii\Output\TemplateAwareTrait;
use Dhii\Output\TemplateInterface;
use Dhii\Storage\Resource\SelectCapableInterface;
use Dhii\Util\Normalization\NormalizeArrayCapableTrait;
use Dhii\Util\Normalization\NormalizeIntCapableTrait;
use Dhii\Util\Normalization\NormalizeIterableCapableTrait;
use Dhii\Util\Normalization\NormalizeStringCapableTrait;
use Dhii\Util\String\StringableInterface as Stringable;
use Psr\Container\ContainerInterface;
use Psr\EventManager\EventInterface;
use RebelCode\Bookings\BookingFactoryInterface;
use RebelCode\Entity\GetCapableManagerInterface;
use stdClass;

/**
 * The handler for rendering booking info in the EDD cart, for cart items that correspond to bookings.
 *
 * @since [*next-version*]
 */
class RenderCartBookingInfoHandler implements InvocableInterface
{
    /* @since [*next-version*] */
    use TemplateAwareTrait;

    /* @since [*next-version*] */
    use GetBookingDisplayTimezoneCapableTrait;

    /* @since [*next-version*] */
    use GetBookingSessionTypeCapableTrait;

    /* @since [*next-version*] */
    use GetBookingSessionInfoCapableTrait;

    /* @since [*next-version*] */
    use ContainerGetPathCapableTrait;

    /* @since [*next-version*] */
    use ContainerGetCapableTrait;

    /* @since [*next-version*] */
    use ContainerHasCapableTrait;

    /* @since [*next-version*] */
    use CountIterableCapableTrait;

    /* @since [*next-version*] */
    use NormalizeTimezoneNameCapableTrait;

    /* @since [*next-version*] */
    use NormalizeIntCapableTrait;

    /* @since [*next-version*] */
    use NormalizeKeyCapableTrait;

    /* @since [*next-version*] */
    use NormalizeStringCapableTrait;

    /* @since [*next-version*] */
    use NormalizeArrayCapableTrait;

    /* @since [*next-version*] */
    use NormalizeIterableCapableTrait;

    /* @since [*next-version*] */
    use NormalizeContainerCapableTrait;

    /* @since [*next-version*] */
    use ResolveIteratorCapableTrait;

    /* @since [*next-version*] */
    use CreateContainerExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateNotFoundExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateOutOfRangeExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateInvalidArgumentExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateRuntimeExceptionCapableTrait;

    /* @since [*next-version*] */
    use StringTranslatingTrait;

    /**
     * The bookings SELECT resource model.
     *
     * @since [*next-version*]
     *
     * @var SelectCapableInterface
     */
    protected $bookingsSelectRm;

    /**
     * The factory for creating booking instances.
     *
     * @since [*next-version*]
     *
     * @var BookingFactoryInterface
     */
    protected $bookingFactory;

    /**
     * The services manager for retrieving services by ID.
     *
     * @since [*next-version*]
     *
     * @var GetCapableManagerInterface
     */
    protected $servicesManager;

    /**
     * The resources manager for retrieving resources by ID.
     *
     * @since [*next-version*]
     *
     * @var GetCapableManagerInterface
     */
    protected $resourcesManager;

    /**
     * The services cache.
     *
     * @since [*next-version*]
     *
     * @var CacheContainerInterface
     */
    protected $servicesCache;

    /**
     * The resources cache.
     *
     * @since [*next-version*]
     *
     * @var CacheContainerInterface
     */
    protected $resourcesCache;

    /**
     * The expression builder.
     *
     * @since [*next-version*]
     *
     * @var object
     */
    protected $exprBuilder;

    /**
     * The cart item data config.
     *
     * @since [*next-version*]
     *
     * @var array|stdClass|ArrayAccess|ContainerInterface
     */
    protected $cartItemConfig;

    /**
     * Constructor.
     *
     * @since [*next-version*]
     *
     * @param TemplateInterface                             $template         The template to use to render the info.
     * @param SelectCapableInterface                        $bookingsSelectRm The bookings SELECT resource model.
     * @param BookingFactoryInterface                       $bookingFactory   The bookings factory.
     * @param GetCapableManagerInterface                    $servicesManager  The services manager.
     * @param GetCapableManagerInterface                    $resourcesManager The resources manager.
     * @param CacheContainerInterface                       $serviceCache     The cache for service.
     * @param CacheContainerInterface                       $resourceCache    The cache for resource.
     * @param object                                        $exprBuilder      The expression builder.
     * @param array|stdClass|ArrayAccess|ContainerInterface $cartItemConfig   The cart item data config.
     * @param string|Stringable|null                        $fallbackTz       The fallback timezone to use for bookings
     *                                                                        that do not have a client timezone.
     */
    public function __construct(
        TemplateInterface $template,
        SelectCapableInterface $bookingsSelectRm,
        BookingFactoryInterface $bookingFactory,
        GetCapableManagerInterface $servicesManager,
        GetCapableManagerInterface $resourcesManager,
        CacheContainerInterface $serviceCache,
        CacheContainerInterface $resourceCache,
        $exprBuilder,
        $cartItemConfig,
        $fallbackTz
    ) {
        $this->_setTemplate($template);
        $this->_setFallbackTz($fallbackTz);

        $this->bookingsSelectRm = $bookingsSelectRm;
        $this->bookingFactory   = $bookingFactory;
        $this->servicesManager  = $servicesManager;
        $this->resourcesManager = $resourcesManager;
        $this->servicesCache    = $serviceCache;
        $this->resourcesCache   = $resourceCache;
        $this->exprBuilder      = $exprBuilder;
        $this->cartItemConfig   = $cartItemConfig;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function __invoke()
    {
        $event = func_get_arg(0);

        if (!($event instanceof EventInterface)) {
            throw $this->_createInvalidArgumentException(
                $this->__('Argument is not an event instance'), null, null, $event
            );
        }

        $item = $event->getParam(0);
        $item = $this->_normalizeContainer($item);

        $dataKey       = $this->_containerGetPath($this->cartItemConfig, ['data', 'key']);
        $eddBkKey      = $this->_containerGetPath($this->cartItemConfig, ['data', 'eddbk_key']);
        $bookingIdKey  = $this->_containerGetPath($this->cartItemConfig, ['data', 'booking_id_key']);
        $bookingIdPath = [$dataKey, $eddBkKey, $bookingIdKey];

        try {
            $bookingId = $this->_containerGetPath($item, $bookingIdPath);
        } catch (NotFoundExceptionInterface $exception) {
            // Cart item does not have a booking ID, and thus does not correspond to a booking.
            return;
        }

        // Alias expression builder
        $b = $this->exprBuilder;

        // Fetch the corresponding booking from storage
        $condition = $b->eq($b->var('id'), $b->lit($bookingId));
        $bookings  = $this->bookingsSelectRm->select($condition);
        // Stop if no booking was found, or if multiple bookings matched the ID (for some reason?)
        if ($this->_countIterable($bookings) !== 1) {
            return;
        }

        // Get the booking
        $booking = reset($bookings);

        echo $this->_renderBookingInfo($booking);
    }

    /**
     * Renders the information for a booking.
     *
     * @since [*next-version*]
     *
     * @param array|stdClass|ArrayAccess|ContainerInterface $bookingData The booking data.
     *
     * @return string|Stringable The render result.
     */
    protected function _renderBookingInfo($bookingData)
    {
        $booking = $this->bookingFactory->make([
            BookingFactoryInterface::K_DATA => $bookingData,
        ]);

        $format   = $this->_containerGet($this->cartItemConfig, 'booking_datetime_format');
        $clientTz = $this->_getDisplayTimezone($bookingData);

        // Get timestamps from booking
        $startTs = $this->_containerGet($bookingData, 'start');
        $endTs   = $this->_containerGet($bookingData, 'end');

        // Create date time helper instances
        $startDt = Carbon::createFromTimestampUTC($startTs);
        $endDt   = Carbon::createFromTimestampUTC($endTs);

        // Shift to client timezone, if available
        if ($clientTz !== null) {
            $startDt->setTimezone($clientTz);
            $endDt->setTimezone($clientTz);
        }

        // Format times to strings
        $startStr = $startDt->format($format);
        $endStr   = $endDt->format($format);

        // Get the matching session's info
        $sessionInfo = $this->_getBookingSessionInfo($booking);
        // Append the session label to the service name if it exists
        $sessionLabel = $this->_containerGet($sessionInfo, 'session_label');

        // Prepare the resources text
        $resources     = $this->_containerGet($sessionInfo, 'resource_names');
        $resourcesText = !empty($resources)
            ? sprintf('%s %s', $this->__('with'), implode(', ', $resources))
            : '';

        return $this->_getTemplate()->render([
            'session_label'     => $sessionLabel,
            'resources'         => $resourcesText,
            'start_datetime'    => $startTs,
            'end_datetime'      => $endTs,
            'start_text'        => $startStr,
            'end_text'          => $endStr,
            'timezone_text'     => $this->_normalizeTimezoneName($clientTz->getName()),
        ]);
    }

    /**
     * Retrieves the name of the service.
     *
     * @since [*next-version*]
     *
     * @param int|string|Stringable $serviceId The ID of the service.
     *
     * @return string|Stringable The name of the service.
     */
    protected function _getServiceName($serviceId)
    {
        $service = $this->servicesCache->get($serviceId, function ($serviceId) {
            return $this->servicesManager->get($serviceId);
        });

        return $this->_containerGet($service, 'name');
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _getResourcesManager()
    {
        return $this->resourcesManager;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _getResourceCache()
    {
        return $this->resourcesCache;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _getServicesManager()
    {
        return $this->servicesManager;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    protected function _getServiceCache()
    {
        return $this->servicesCache;
    }
}