components-web-app/api-components-bundle

View on GitHub
src/Serializer/Normalizer/RouteNormalizer.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

/*
 * This file is part of the Silverback API Components Bundle Project
 *
 * (c) Daniel West <daniel@silverback.is>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Silverback\ApiComponentsBundle\Serializer\Normalizer;

use Silverback\ApiComponentsBundle\Entity\Core\Route;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * @author Daniel West <daniel@silverback.is>
 */
class RouteNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
    use NormalizerAwareTrait;

    private const ALREADY_CALLED = 'ROUTE_NORMALIZER_ALREADY_CALLED';

    /**
     * @param Route      $object
     * @param mixed|null $format
     */
    public function normalize($object, $format = null, array $context = []): float|array|\ArrayObject|bool|int|string|null
    {
        $context[self::ALREADY_CALLED] = true;

        $finalRoute = $object;

        $redirectedRoutes = [$finalRoute->getId()];
        while ($nextRedirect = $finalRoute->getRedirect()) {
            if (\in_array($nextRedirect->getId(), $redirectedRoutes, true)) {
                throw new CircularReferenceException(sprintf('The redirect routes result in a circular reference: %s', implode(' -> ', $redirectedRoutes)));
            }
            $redirectedRoutes[] = $nextRedirect->getId();
            $finalRoute = $nextRedirect;
        }

        $isRedirect = $finalRoute !== $object;
        if ($isRedirect) {
            $object->setPage($finalRoute->getPage());
            $object->setPageData($finalRoute->getPageData());
        }

        $normalized = $this->normalizer->normalize($object, $format, $context);

        if ($isRedirect) {
            $normalized['redirectPath'] = $finalRoute->getPath();
        }

        $operationName = $context['operation_name'] ?? null;
        if ('_api_/routes_manifest/{id}{._format}_get' === $operationName) {
            return [
                'resource_iris' => $this->getResourceIrisFromArray($normalized),
            ];
        }

        return $normalized;
    }

    private function getResourceIrisFromArray(array $resource, array $iris = []): array
    {
        $resourceId = $resource['@id'] ?? null;
        if (
            $resourceId
            && !str_starts_with($resource['@id'] ?? null, '/.well-known/')
            && '/_/resource_metadatas' !== $resourceId
            && !\in_array($resourceId, $iris, true)
        ) {
            $iris[] = $resourceId;
        }
        foreach ($resource as $resourceKey => $resourceValue) {
            // may be a string or simple
            // may be an array representing a resource
            // may be an array of any other values
            // may be an array of arrays
            if (\is_array($resourceValue)) {
                // check if the array is representing a new resource
                if (isset($resourceValue['@id'])) {
                    $iris = $this->getResourceIrisFromArray($resourceValue, $iris);
                }
                // check if the array contains more resources
                foreach ($resourceValue as $nestedValue) {
                    if (isset($nestedValue['@id'])) {
                        $iris = $this->getResourceIrisFromArray($nestedValue, $iris);
                    }
                }
            }
        }

        return $iris;
    }

    public function supportsNormalization($data, $format = null, $context = []): bool
    {
        return !isset($context[self::ALREADY_CALLED]) && $data instanceof Route;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [Route::class => false];
    }
}