<?php declare(strict_types=1);
namespace App\Controller\CRUD;
use App\Repository\AbstractRepository;
use App\Response\ActionResult;
use App\Response\JsonTextResponse;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* @template TEntityClass of object
* @template-extends EntityController<TEntityClass>
*/
abstract class DetailsController extends EntityController
{
/**
* @var class-string<TEntityClass>
*/
private string $repository;
private string $template;
protected SerializerInterface $serializer;
protected bool $serializeNull = false;
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessDocComment
* @param class-string<TEntityClass> $repository FQCN for entity class
*/
public function __construct(string $repository, string $template)
{
$this->repository = $repository;
$this->template = $template;
}
/**
* @phpstan-param TEntityClass $entity
* @return string path to template
*/
protected function getTemplate($entity): string
{
return $this->template;
}
/**
* @return AbstractRepository<TEntityClass> Repository for related entity
*/
protected function getRepository(): AbstractRepository
{
/** @var AbstractRepository<TEntityClass> $repository */
$repository = $this->doctrine->getRepository($this->repository);
return $repository;
}
/**
* @return ActionResult|JsonTextResponse|Response
*/
public function handleRequest(Request $request)
{
$result = $this->execute($this->getFilters($request));
if ($this->isHandlingJson($request) && $this->isAjaxRequest($request)) {
$entity = $result->getParams()['entity'];
$data = [
'entity' => $entity,
];
$context = $this->configureSerializationContext(new SerializationContext());
return new JsonTextResponse(
function ($data) use ($context): string {
return $this->serializer->serialize($data, 'json', $context);
},
$data
);
}
return $result;
}
/**
* @phpstan-return TEntityClass
* @param array<string, array<mixed>|bool|int|float|object|string|null> $filters
* @return mixed
*/
public function provideEntity(array $filters)
{
if (!isset($filters['id']) || !is_numeric($filters['id'])) {
throw $this->createNotFoundException('Id parameter is required.');
}
return $this->getEntity((int) $filters['id']);
}
/**
* @return array<string, array<mixed>|bool|int|float|object|string|null>
*/
protected function getFilters(Request $request): array
{
$id = $request->attributes->get('id');
if (null === $id) {
throw $this->createNotFoundException('Id parameter is required.');
}
if (!is_string($id) && !is_int($id)) {
throw new UnexpectedTypeException($id, 'string|int');
}
return ['id' => $id];
}
/**
* @param array<string, array<mixed>|bool|int|float|object|string|null> $filters
*/
public function execute(array $filters = []): ActionResult
{
$entity = $this->provideEntity($filters);
$parameters = ['entity' => $entity];
foreach ($this->templateParams as $key => $value) {
$parameters[$key] = $value;
}
return new ActionResult($this->getTemplate($entity), $parameters);
}
protected function configureSerializationContext(SerializationContext $serializationContext): SerializationContext
{
if ($this->serializeNull) {
$serializationContext->setSerializeNull(true);
}
return $serializationContext;
}
/**
* @required
*/
public function setSerializer(SerializerInterface $serializer): void
{
$this->serializer = $serializer;
}
}