portrino/typo3-fractal-view

View on GitHub
src/Mvc/View/FractalView.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Portrino\Typo3FractalView\Mvc\View;

/*
 * This file is part of the TYPO3 Fractal View project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read
 * LICENSE file that was distributed with this source code.
 *
 */

use ArrayAccess;
use InvalidArgumentException;
use League\Fractal\Manager;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
use Portrino\Typo3FractalView\Serializer\ArraySerializer;
use TYPO3\CMS\Extbase\Mvc\View\JsonView;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;

/**
 * Class FractalView
 *
 * @package Portrino\Typo3FractalView\Mvc\View
 */
class FractalView extends JsonView
{
    /**
     * @var \League\Fractal\Manager
     * @inject
     */
    protected $fractalManager;

    /**
     * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
     * @inject
     */
    protected $objectManager;

    /**
     * @var string
     */
    protected $includes = '';

    /**
     * @var string
     */
    protected $excludes = '';

    /**
     * The rendering configuration for this Fractal view which
     * to determine which Transformer should render each variable / object
     *
     * The configuration array must have the following structure:
     *
     * Example 1:
     *
     * array(
     *      'book' => \Acme\Bar\Fractal\Transformer\BookTransformer::class
     *      'author' => '\Acme\Bar\Fractal\Transformer\AuthorTransformer'
     * )
     */
    protected $configuration = [];

    /**
     * @param Manager $fractalManager
     */
    public function injectFractalManager(Manager $fractalManager)
    {
        $this->fractalManager = $fractalManager;
    }

    /**
     * @param ObjectManagerInterface $objectManager
     */
    public function injectObjectManager(ObjectManagerInterface $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    /**
     * Transforms the value view variable to a serializable
     * array representation using a YAML view configuration and JSON encodes
     * the result.
     *
     * @return string The JSON encoded variables
     * @api
     */
    public function render()
    {
        $this->setIncludesFromRequest();
        $this->setExcludesFromRequest();

        return parent::render();
    }

    /**
     * Sets the includes from magic GET param "_includes"
     *
     * @codeCoverageIgnore
     * @return             void
     */
    protected function setIncludesFromRequest()
    {
        if ($this->controllerContext->getRequest()->hasArgument('_includes')) {
            $this->setIncludes($this->controllerContext->getRequest()->getArgument('_includes'));
        }
    }

    /**
     * Sets the excludes from magic GET param "_excludes"
     *
     * @codeCoverageIgnore
     * @return             void
     */
    protected function setExcludesFromRequest()
    {
        if ($this->controllerContext->getRequest()->hasArgument('_excludes')) {
            $this->setExcludes($this->controllerContext->getRequest()->getArgument('_excludes'));
        }
    }

    /**
     * Loads the configuration and transforms the value to a serializable
     * array via fractal
     *
     * @return array An array containing the values, ready to be JSON encoded
     * @api
     */
    protected function renderArray()
    {
        $result = [];

        $this->fractalManager->setSerializer(new ArraySerializer());
        $this->fractalManager->parseIncludes($this->includes);
        $this->fractalManager->parseExcludes($this->excludes);

        sort($this->variablesToRender);

        if (count($this->variablesToRender) === 1) {
            $variableName = current($this->variablesToRender);
            $valueToRender = isset($this->variables[$variableName]) ? $this->variables[$variableName] : null;
            $configuration = isset($this->configuration[$variableName]) ? $this->configuration[$variableName] : '';
            $result = $this->transformValue($valueToRender, [0 => $configuration]);
        }

        if (count($this->variablesToRender) > 1) {
            foreach ($this->variablesToRender as $variableName) {
                $valueToRender = isset($this->variables[$variableName]) ? $this->variables[$variableName] : null;
                $configuration = isset($this->configuration[$variableName]) ? $this->configuration[$variableName] : '';
                $transformedObject = $this->transformValue($valueToRender, [0 => $configuration]);
                $result[$variableName] = isset($transformedObject) ? $transformedObject : '';
            }
        }

        // prevent data array key in result
        return $result;
    }

    /**
     * Transforms a value depending on type
     *
     * @param  mixed $value The value to transform
     * @param  array $configuration Configuration for transforming the value
     * @return array The transformed value
     */
    protected function transformValue($value, array $configuration)
    {
        $result = $value;
        if (is_array($value) || $value instanceof ArrayAccess) {
            $result = $this->transformCollection($value, $configuration);
        } else if (is_object($value)) {
            $result = $this->transformObject($value, $configuration);
        }
        return $result;
    }

    /**
     * Traverses the given object structure in order to transform it into an
     * array structure.
     *
     * @param  object $object Object to traverse
     * @param  array $configuration Configuration for transforming the given object or NULL
     * @return array Object structure as an array
     * @throws InvalidArgumentException
     */
    protected function transformObject($object, array $configuration)
    {
        $transformer = $this->getTransformer($configuration[0]);
        $resource = new Item($object, $transformer);
        return $this->fractalManager->createData($resource)->toArray();
    }

    /**
     * Traverses the given collection
     *
     * @param  array|ArrayAccess $collection Collection to traverse
     * @param  array $configuration Configuration for transforming the given object or NULL
     * @return array Object structure as an array
     * @throws InvalidArgumentException
     */
    protected function transformCollection($collection, array $configuration)
    {
        $transformer = $this->getTransformer($configuration[0]);
        $resource = new Collection($collection, $transformer);
        return $this->fractalManager->createData($resource)->toArray();
    }

    /**
     * @param string $transformerClassName
     * @return TransformerAbstract
     * @throws InvalidArgumentException
     */
    protected function getTransformer($transformerClassName)
    {
        /**
         *
         *
         * @var TransformerAbstract $result
         */
        $result = $this->objectManager->get($transformerClassName);

        if ($result instanceof TransformerAbstract === false) {
            throw new InvalidArgumentException(
                'Argument $transformerClassName should extend League\Fractal\TransformerAbstract'
            );
        }

        return $result;
    }

    /**
     * @param string $includes
     */
    public function setIncludes($includes)
    {
        $this->includes = $includes;
    }

    /**
     * @param string $excludes
     */
    public function setExcludes($excludes)
    {
        $this->excludes = $excludes;
    }

    /**
     * @return mixed
     */
    public function getConfiguration()
    {
        return $this->configuration;
    }

    /**
     * @param string $variable
     */
    public function addVariableToRender($variable)
    {
        $this->variablesToRender[$variable] = $variable;
    }

    /**
     * @param array $variables
     */
    public function addVariablesToRender($variables)
    {
        foreach ($variables as $variable) {
            $this->variablesToRender[$variable] = $variable;
        }
    }
}