<?php
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Listing\Transformer\ListingResponseTransformerInterface;
use App\Service\ApiWebService;
use App\Service\ApiWebServiceFilterBuilder\AllIdsFilterBuilder;
use App\Service\ApiWebServiceFilterBuilder\Crm\Traits\FieldNameConvertorTrait;
use App\Service\ApiWebServiceFilterBuilder\FilterBuildersChain;
use App\Service\ApiWebServiceFilterBuilder\PaginationFilterBuilder;
use App\Service\Cache\CacheManager;
use App\Service\Cache\Exception\UnableToSaveKeyException;
use App\Service\Provider\ApiProviderInterface;
use Psr\Cache\CacheException;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
abstract class AbstractCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
{
public const CONTEXT_TAB = 'tab';
public const DISABLE_CUSTOMER_FILTER = 'disableCustomerFilter';
public const DISABLE_AUTHENTICATION = 'disableAuthentication';
public const FETCH_CUSTOMER_FILES = 'fetchCustomerFiles';
protected const ENTITY = null;
protected const ENDPOINT = '/';
protected const METHOD = Request::METHOD_GET;
protected const IGNORE_FALSE_VALUE_FOR = [
'myContacts',
'unSubscribedOfMailing',
'myQuotes',
'isMaintenance',
'myTasks',
];
use FieldNameConvertorTrait;
/**
* @var FilterBuildersChain
*/
protected $filterBuildersChain;
/**
* @var ApiWebService
*/
protected $apiWebService;
/**
* @var ApiProviderInterface
*/
protected $apiProvider;
/**
* @var CacheManager
*/
private $cacheManager;
/**
* @var ListingResponseTransformerInterface
*/
private $listingResponseTransformer;
/**
* @param FilterBuildersChain $filterBuildersChain
* @param ApiWebService $apiWebService
* @param ApiProviderInterface $apiProvider
* @param CacheManager $cacheManager
* @param ListingResponseTransformerInterface $listingResponseTransformer
*/
public function __construct(
FilterBuildersChain $filterBuildersChain,
ApiWebService $apiWebService,
ApiProviderInterface $apiProvider,
CacheManager $cacheManager,
ListingResponseTransformerInterface $listingResponseTransformer
) {
$this->filterBuildersChain = $filterBuildersChain;
$this->apiWebService = $apiWebService;
$this->apiProvider = $apiProvider;
$this->cacheManager = $cacheManager;
$this->listingResponseTransformer = $listingResponseTransformer;
}
/**
* @param string $resourceClass
* @param string|null $operationName
* @param array $context
*
* @return bool
*/
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
$entity = $this::ENTITY;
return $entity === $resourceClass
&& (null === $operationName || 'get' === $operationName)
;
}
/**
* @param string $resourceClass
* @param string|null $operationName
* @param array $context
*
* @return iterable
*
* @throws CacheException
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws ExceptionInterface
* @throws InvalidArgumentException
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
* @throws UnableToSaveKeyException
*/
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): iterable
{
$response = $this->getRawCollection($context['filters'] ?? [], $context);
if (isset($context['filters'][AllIdsFilterBuilder::DATA_FILTER_KEY])) {
foreach ($response['hydra:member'] as $key => $responseContent) {
// @todo voir si on laisse les info comme ça depuis crm => [ [0] => "monObjet", "autre_champ" => "valeur"]
//valable pour Prospect et contact et devis
if (isset($responseContent[0])) {
$response['hydra:member'][$key] = $responseContent[0];
}
// @todo refacto : voir si l'on ne pas renvoyer directement prospectId directement depuis business
if (isset($response['hydra:member'][$key]['prospect.id'])) {
$response['hydra:member'][$key]['prospectId'] = $response['hydra:member'][$key]['prospect.id'];
unset($response['hydra:member'][$key]['prospect.id']);
}
}
return $response;
}
$entities = [];
foreach ($response['hydra:member'] as $responseContent) {
// @todo voir si on laisse les info comme ça depuis crm => [ [0] => "monObjet", "autre_champ" => "valeur"]
//valable pour Prospect et contact et devis
if (isset($responseContent[0])) {
// Do not change order of array_merge parameters
// Otherwise, the first element of the array will be overwritten by the second one
// We'll loose createdByRealName, managedByRealName and updatedByRealName
//
// The array_filter method is used to remove null values
$responseContent = array_merge($responseContent[0], array_filter($responseContent));
unset($responseContent[0]);
}
// @todo refacto : voir si l'on ne pas renvoyer directement prospectId directement depuis business
if (isset($responseContent['prospect.id'])) {
$responseContent['prospectId'] = $responseContent['prospect.id'];
unset($responseContent['prospect.id']);
}
$class = $this::ENTITY;
$entity = new $class();
$entity->importFromData($responseContent);
$entities[] = $entity;
}
if (isset($context['transformResponse']) && $context['transformResponse']) {
$response['hydra:member'] = $this->listingResponseTransformer->transform($this::ENTITY, $entities, $context);
return $response;
}
return $entities;
}
/**
* @param array $searchBody
* @param array $context
*
* @return array
*
* @throws CacheException
* @throws ClientExceptionInterface
* @throws DecodingExceptionInterface
* @throws InvalidArgumentException
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
* @throws UnableToSaveKeyException
*/
public function getRawCollection(array $searchBody, array $context = []): array
{
$processedQueries = [];
$searchBodyWithCorrectCrmKeys = [];
foreach ($searchBody as $key => $value) {
if ((empty($value) && '0' !== $value && false !== $value) || (isset($value['equal']) && !isset($value['begin']) && !isset($value['end']))) {
continue;
}
/*@todo refacto V4: voir si possible de modifier les checkboxType des champs de IGNORE_FALSE_VALUE_FOR en Radio
ou select pour obtenir une valeur a null si le champs n'est pas nécessaire plutôt que de recourir à cette condition */
if (false === $value && in_array($key, self::IGNORE_FALSE_VALUE_FOR, true)) {
continue;
}
$searchBodyWithCorrectCrmKeys[$this->getCrmSearchKey($key, $this::ENTITY)] = $value;
}
// STEP 1: On vérifie si il y a des builders spécifiques pour chaque filtres
foreach ($this->filterBuildersChain->getFilterBuilders() as $filterBuilder) {
if ($filterBuilder->supports($this::ENTITY, $searchBodyWithCorrectCrmKeys)) {
$processedQueries = array_merge($processedQueries, $filterBuilder->addFilter($this::ENTITY, $this->apiWebService, $searchBodyWithCorrectCrmKeys));
}
}
// STEP 2: On ajoute les filtres restants
foreach ($searchBodyWithCorrectCrmKeys as $searchKey => $searchValue) {
if (in_array($searchKey, $processedQueries, true)) {
continue;
}
$this->apiWebService->addFilter($searchKey, $searchValue);
$processedQueries[] = $searchKey;
}
if (!($context[self::DISABLE_CUSTOMER_FILTER] ?? false) && !isset($this->apiWebService->getFilters()['customerId'])) {
$this->apiWebService->addFilterCustomer();
}
if ($context[self::DISABLE_AUTHENTICATION] ?? false) {
$this->apiWebService->disableAuth();
}
$action = 'list_'.base64_encode(json_encode($this->apiWebService->getFilters()));
$response = $this->cacheManager->get($this::ENTITY, $action);
if (null !== $response && isset($_SERVER['REDIS_ENABLE_V4']) && 'true' === $_SERVER['REDIS_ENABLE_V4']) {
$this->apiWebService->reset();
return $response;
}
$response = $this
->apiWebService
->send($this->apiProvider, $this::METHOD, $this::ENDPOINT)
->toArray()
;
$this->cacheManager->set($this::ENTITY, $action, $response);
return $response;
}
/**
* @param array $searchBody
*
* @return bool
*/
protected function isAllIds(array $searchBody): bool
{
return isset($searchBody[AllIdsFilterBuilder::DATA_FILTER_KEY]) && $searchBody[AllIdsFilterBuilder::DATA_FILTER_KEY];
}
/**
* @param array $searchBody
*
* @return bool
*/
protected function isPaginationDisabled(array $searchBody): bool
{
return (isset($searchBody[PaginationFilterBuilder::ACTIVATE_PAGNIATION_KEY]) && 'false' === $searchBody[PaginationFilterBuilder::ACTIVATE_PAGNIATION_KEY])
|| $this->isAllIds($searchBody);
}
}