<?php
namespace App\Controller\Api;
use App\Enum\OrganizationStatusEnum;
use App\Service\ElasticSearchService;
use Evo\Infrastructure\MappingORM\Organization;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @Route("/search")
*/
final class SearchController extends AbstractController
{
private ElasticSearchService $elasticSearchService;
private SerializerInterface $serializer;
private TranslatorInterface $translator;
public function __construct(
ElasticSearchService $elasticSearchService,
SerializerInterface $serializer,
TranslatorInterface $translator
) {
$this->elasticSearchService = $elasticSearchService;
$this->serializer = $serializer;
$this->translator = $translator;
}
/**
* @Route("/mailbox", name="app_search_mailbox", methods={"POST"})
*/
public function searchFromMailbox(Request $request)
{
$content = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
if (!isset($content['search_phrase']) || empty($content['search_phrase'])) {
throw new \InvalidArgumentException('Not valid search input!');
}
$results = $this->elasticSearchService->search(
ElasticSearchService::CONTEXT_MAILBOX,
$content['search_phrase'],
$content['postal_code'] ?? null
);
$normalized = $this->serializer->normalize($results, $this->extractFormat($request), [
'groups' => ['read_search', 'read_search_signature'],
'callbacks' => $this->getSerializerCallbacks(),
]);
return $this->json($normalized);
}
/**
* @Route("/admin", name="app_search_admin", methods={"POST"})
*/
public function searchFromAdmin(Request $request)
{
$content = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
if (!isset($content['search_phrase']) || empty($content['search_phrase'])) {
throw new \InvalidArgumentException('Not valid search input!');
}
$results = $this->elasticSearchService->search(
ElasticSearchService::CONTEXT_ADMIN,
$content['search_phrase'],
null
);
$normalized = $this->serializer->normalize($results, $this->extractFormat($request), [
'groups' => ['read_search'],
'callbacks' => $this->getSerializerCallbacks(),
]);
return $this->json($normalized);
}
private function extractFormat(Request $request): string
{
$acceptable = ['application/ld+json', 'application/json'];
$header = $request->getAcceptableContentTypes()[0] ?? '*/*';
// Use json by default if */* is accepted
$accept = ('*/*' !== $header) ? $header : $acceptable[0];
if (!in_array($accept, $acceptable, true)) {
throw new NotAcceptableHttpException();
}
return $request->getFormat($accept);
}
private function getSerializerCallbacks(): array
{
$translator = $this->translator;
return [
// For organizations only, translate status
'statusTranslated' => function ($innerObject, $outerObject, string $attributeName, string $format = null, array $context = []) use ($translator) {
if (!$outerObject instanceof Organization) {
return '';
}
if (null === $innerObject) {
return '';
}
if (!OrganizationStatusEnum::isValueExist($innerObject)) {
return '';
}
return $translator->trans(OrganizationStatusEnum::getReadableValue($innerObject));
},
'numberOfLetterParcel' => function ($innerObject, $outerObject, string $attributeName, string $format = null, array $context = []) {
if ($outerObject instanceof Organization) {
$numberOfLetterParcel = $this->getOrganizationNumberOfLetterParcel($outerObject, 'LETTER,LETTER_WITH_AR,NOTICE,BAILIFF_NOTICE,BAILIFF_DEED');
return $numberOfLetterParcel + $this->getOrganizationNumberOfLetterParcel($outerObject, 'PARCEL');
}
return 0;
},
];
}
private function getOrganizationNumberOfLetterParcel(Organization $organization, string $type): int
{
$letterParcel = $this->forward(LetterController::class.'::letterParcel', [
'type' => $type,
'orgaId' => $organization->getId(),
]);
return json_decode($letterParcel->getContent(), null, 512, JSON_THROW_ON_ERROR)->result;
}
}