mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-04 11:18:25 +00:00 
			
		
		
		
	Generate export using denormalization
This commit is contained in:
		@@ -86,7 +86,7 @@ class ExportGeneration implements TrackCreationInterface
 | 
			
		||||
        return $this->options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function fromSavedExport(SavedExport $savedExport, null|\DateTimeImmutable $deletedAt = null): self
 | 
			
		||||
    public static function fromSavedExport(SavedExport $savedExport, ?\DateTimeImmutable $deletedAt = null): self
 | 
			
		||||
    {
 | 
			
		||||
        return new self($savedExport->getExportAlias(), $savedExport->getOptions(), $deletedAt);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,13 @@ interface DirectExportInterface extends ExportElementInterface
 | 
			
		||||
     *
 | 
			
		||||
     * @return FormattedExportGeneration
 | 
			
		||||
     */
 | 
			
		||||
    public function generate(array $acl, array $data = []): Response|FormattedExportGeneration;
 | 
			
		||||
    public function generate(array $acl, array $data, ExportGenerationContext $context): Response|FormattedExportGeneration;
 | 
			
		||||
 | 
			
		||||
    public function normalizeFormData(array $formData): array;
 | 
			
		||||
 | 
			
		||||
    public function denormalizeFormData(array $formData, int $fromVersion): array;
 | 
			
		||||
 | 
			
		||||
    public function getVersion(): int;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get a description, which will be used in UI (and translated).
 | 
			
		||||
 
 | 
			
		||||
@@ -11,42 +11,248 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\MainBundle\Export;
 | 
			
		||||
 | 
			
		||||
use Chill\DocStoreBundle\Entity\StoredObject;
 | 
			
		||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
 | 
			
		||||
use Chill\MainBundle\Entity\ExportGeneration;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Doctrine\DBAL\LockMode;
 | 
			
		||||
use Doctrine\ORM\EntityManagerInterface;
 | 
			
		||||
use Chill\MainBundle\Form\Type\Export\ExportType;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Response;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a single export.
 | 
			
		||||
 */
 | 
			
		||||
final readonly class ExportGenerator
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private ExportManager $exportManager,
 | 
			
		||||
        private StoredObjectManagerInterface $storedObjectManager,
 | 
			
		||||
        private EntityManagerInterface $entityManager,
 | 
			
		||||
        private ExportFormHelper $exportFormHelper,
 | 
			
		||||
        private ExportConfigNormalizer $configNormalizer,
 | 
			
		||||
        private LoggerInterface $logger,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function generate(ExportGeneration $exportGeneration, User $user): void
 | 
			
		||||
    public function generate(string $exportAlias, array $configuration, ?User $byUser = null): FormattedExportGeneration
 | 
			
		||||
    {
 | 
			
		||||
        $this->entityManager->wrapInTransaction(function () use ($exportGeneration) {
 | 
			
		||||
            $object = $exportGeneration->getStoredObject();
 | 
			
		||||
            $this->entityManager->refresh($exportGeneration, LockMode::PESSIMISTIC_WRITE);
 | 
			
		||||
            $this->entityManager->refresh($object, LockMode::PESSIMISTIC_WRITE);
 | 
			
		||||
        $data = $this->configNormalizer->denormalizeConfig($exportAlias, $configuration);
 | 
			
		||||
        $centers = $data['centers'];
 | 
			
		||||
 | 
			
		||||
            if (StoredObject::STATUS_PENDING !== $object->getStatus()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        $export = $this->exportManager->getExport($exportAlias);
 | 
			
		||||
        $context = new ExportGenerationContext($byUser);
 | 
			
		||||
 | 
			
		||||
            $generation = $this->exportManager->generateExport(
 | 
			
		||||
                $exportGeneration->getExportAlias(),
 | 
			
		||||
                $centers = $this->exportFormHelper->savedExportDataToFormData($exportGeneration, 'centers'),
 | 
			
		||||
                $this->exportFormHelper->savedExportDataToFormData($exportGeneration, 'export', ['picked_centers' => $centers]),
 | 
			
		||||
                $this->exportFormHelper->savedExportDataToFormData($exportGeneration, 'formatter', ['picked_centers' => $centers]),
 | 
			
		||||
                $user,
 | 
			
		||||
        if ($export instanceof DirectExportInterface) {
 | 
			
		||||
            $generatedExport = $export->generate(
 | 
			
		||||
                $this->buildCenterReachableScopes($centers),
 | 
			
		||||
                $data['export'],
 | 
			
		||||
                $context,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            $this->storedObjectManager->write($exportGeneration->getStoredObject(), $generation->content, $generation->contentType);
 | 
			
		||||
        });
 | 
			
		||||
            if ($generatedExport instanceof Response) {
 | 
			
		||||
                trigger_deprecation('chill-project/chill-bundles', '3.10', 'DirectExportInterface should not return a %s instance, but a %s instance', Response::class, FormattedExportGeneration::class);
 | 
			
		||||
 | 
			
		||||
                return new FormattedExportGeneration($generatedExport->getContent(), $generatedExport->headers->get('Content-Type'));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $generatedExport;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $export->initiateQuery(
 | 
			
		||||
            $this->retrieveUsedModifiers($data),
 | 
			
		||||
            $this->buildCenterReachableScopes($centers),
 | 
			
		||||
            $data['export'],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if ($query instanceof \Doctrine\ORM\NativeQuery) {
 | 
			
		||||
            // throw an error if the export require other modifier, which is
 | 
			
		||||
            // not allowed when the export return a `NativeQuery`
 | 
			
		||||
            if (\count($export->supportsModifiers()) > 0) {
 | 
			
		||||
                throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\Doctrine\ORM\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\ORM\QueryBuilder`');
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($query instanceof QueryBuilder) {
 | 
			
		||||
            // handle filters
 | 
			
		||||
            $this->handleFilters($query, $data[ExportType::FILTER_KEY], $context);
 | 
			
		||||
 | 
			
		||||
            // handle aggregators
 | 
			
		||||
            $this->handleAggregators($query, $data[ExportType::AGGREGATOR_KEY], $context);
 | 
			
		||||
 | 
			
		||||
            $this->logger->notice('[export] will execute this qb in export', [
 | 
			
		||||
                'dql' => $query->getDQL(),
 | 
			
		||||
            ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new \UnexpectedValueException('The method `intiateQuery` should return a `\Doctrine\ORM\NativeQuery` or a `Doctrine\ORM\QueryBuilder` object.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = $export->getResult($query, $data['export'], $context);
 | 
			
		||||
 | 
			
		||||
        if (!is_iterable($result)) {
 | 
			
		||||
            throw new \UnexpectedValueException(sprintf('The result of the export should be an iterable, %s given', \gettype($result)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $formatter = $this->exportManager->getFormatter($data['pick_formatter']);
 | 
			
		||||
        $filtersData = [];
 | 
			
		||||
        $aggregatorsData = [];
 | 
			
		||||
 | 
			
		||||
        if ($query instanceof QueryBuilder) {
 | 
			
		||||
            foreach ($this->retrieveUsedAggregators($data[ExportType::AGGREGATOR_KEY]) as $alias => $aggregator) {
 | 
			
		||||
                $aggregatorsData[$alias] = $data[ExportType::AGGREGATOR_KEY][$alias]['form'];
 | 
			
		||||
            }
 | 
			
		||||
            foreach ($this->retrieveUsedFilters($data[ExportType::FILTER_KEY]) as $alias => $filter) {
 | 
			
		||||
                $filtersData[$alias] = $data[ExportType::FILTER_KEY][$alias]['form'];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (method_exists($formatter, 'generate')) {
 | 
			
		||||
            return $formatter->generate(
 | 
			
		||||
                $result,
 | 
			
		||||
                $data['formatter']['form'],
 | 
			
		||||
                $exportAlias,
 | 
			
		||||
                $data['export'],
 | 
			
		||||
                $filtersData,
 | 
			
		||||
                $aggregatorsData,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        trigger_deprecation('chill-project/chill-bundles', '3.10', '%s should implements the "generate" method', FormatterInterface::class);
 | 
			
		||||
 | 
			
		||||
        $generatedExport =  $formatter->getResponse(
 | 
			
		||||
            $result,
 | 
			
		||||
            $data['formatter'],
 | 
			
		||||
            $exportAlias,
 | 
			
		||||
            $data['export']['form'],
 | 
			
		||||
            $filtersData,
 | 
			
		||||
            $aggregatorsData,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new FormattedExportGeneration($generatedExport->getContent(), $generatedExport->headers->get('content-type'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * parse the data to retrieve the used filters and aggregators.
 | 
			
		||||
     *
 | 
			
		||||
     * @return list<string>
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedModifiers(mixed $data): array
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = array_merge(
 | 
			
		||||
            $this->retrieveUsedFiltersType($data[ExportType::FILTER_KEY]),
 | 
			
		||||
            $this->retrieveUsedAggregatorsType($data[ExportType::AGGREGATOR_KEY]),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return array_values(array_unique($usedTypes));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve the filter used in this export.
 | 
			
		||||
     *
 | 
			
		||||
     * @return list<string> an array with types
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedFiltersType(mixed $data): array
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->retrieveUsedFilters($data) as $filter) {
 | 
			
		||||
            if (!\in_array($filter->applyOn(), $usedTypes, true)) {
 | 
			
		||||
                $usedTypes[] = $filter->applyOn();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $usedTypes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string[]
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedAggregatorsType(mixed $data): array
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->retrieveUsedAggregators($data) as $alias => $aggregator) {
 | 
			
		||||
            if (!\in_array($aggregator->applyOn(), $usedTypes, true)) {
 | 
			
		||||
                $usedTypes[] = $aggregator->applyOn();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $usedTypes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return iterable<string, AggregatorInterface>
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedAggregators(mixed $data): iterable
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $alias => $aggregatorData) {
 | 
			
		||||
            if (true === $aggregatorData['enabled']) {
 | 
			
		||||
                yield $alias => $this->exportManager->getAggregator($alias);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return iterable<string, FilterInterface>
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedFilters(mixed $data): iterable
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $alias => $filterData) {
 | 
			
		||||
            if (true === $filterData['enabled']) {
 | 
			
		||||
                yield $alias => $this->exportManager->getFilter($alias);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Alter the query with selected aggregators.
 | 
			
		||||
     */
 | 
			
		||||
    private function handleAggregators(
 | 
			
		||||
        QueryBuilder $qb,
 | 
			
		||||
        array $data,
 | 
			
		||||
        ExportGenerationContext $context,
 | 
			
		||||
    ): void {
 | 
			
		||||
        foreach ($this->retrieveUsedAggregators($data) as $alias => $aggregator) {
 | 
			
		||||
            $formData = $data[$alias];
 | 
			
		||||
            $aggregator->alterQuery($qb, $formData['form'], $context);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alter the query with selected filters.
 | 
			
		||||
     */
 | 
			
		||||
    private function handleFilters(
 | 
			
		||||
        QueryBuilder $qb,
 | 
			
		||||
        mixed $data,
 | 
			
		||||
        ExportGenerationContext $context,
 | 
			
		||||
    ): void {
 | 
			
		||||
        foreach ($this->retrieveUsedFilters($data) as $alias => $filter) {
 | 
			
		||||
            $formData = $data[$alias];
 | 
			
		||||
 | 
			
		||||
            $filter->alterQuery($qb, $formData['form'], $context);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * build the array required for defining centers and circles in the initiate
 | 
			
		||||
     * queries of ExportElementsInterfaces.
 | 
			
		||||
     *
 | 
			
		||||
     * @param list<Center>
 | 
			
		||||
     */
 | 
			
		||||
    private function buildCenterReachableScopes(array $centers)
 | 
			
		||||
    {
 | 
			
		||||
        return array_map(static fn (Center $center) => ['center' => $center, 'circles' => []], $centers);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ interface ExportInterface extends ExportElementInterface
 | 
			
		||||
     *
 | 
			
		||||
     * @return mixed[] an array of results
 | 
			
		||||
     */
 | 
			
		||||
    public function getResult($query, $data);
 | 
			
		||||
    public function getResult($query, $data, ExportGenerationContext $context);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the Export's type. This will inform _on what_ export will apply.
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,7 @@ namespace Chill\MainBundle\Export;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Form\Type\Export\ExportType;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Response;
 | 
			
		||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
 | 
			
		||||
 | 
			
		||||
@@ -165,118 +162,6 @@ class ExportManager
 | 
			
		||||
        $this->formatters[$alias] = $formatter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generateExport(string $exportAlias, array $pickedCentersData, array $data, array $formatterData, User $byUser): FormattedExportGeneration
 | 
			
		||||
    {
 | 
			
		||||
        $export = $this->getExport($exportAlias);
 | 
			
		||||
        $centers = $this->getPickedCenters($pickedCentersData);
 | 
			
		||||
        $context = new ExportGenerationContext($byUser);
 | 
			
		||||
 | 
			
		||||
        if ($export instanceof DirectExportInterface) {
 | 
			
		||||
            $generatedExport = $export->generate(
 | 
			
		||||
                $this->buildCenterReachableScopes($centers, $export),
 | 
			
		||||
                $data[ExportType::EXPORT_KEY],
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if ($generatedExport instanceof Response) {
 | 
			
		||||
                trigger_deprecation('chill-project/chill-bundles', '3.10', 'DirectExportInterface should not return a %s instance, but a %s instance', Response::class, FormattedExportGeneration::class);
 | 
			
		||||
 | 
			
		||||
                return new FormattedExportGeneration($generatedExport->getContent(), $generatedExport->headers->get('Content-Type'));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $generatedExport;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $export->initiateQuery(
 | 
			
		||||
            $this->retrieveUsedModifiers($data),
 | 
			
		||||
            $this->buildCenterReachableScopes($centers, $export),
 | 
			
		||||
            $export->denormalizeFormData($data[ExportType::EXPORT_KEY], $context),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if ($query instanceof \Doctrine\ORM\NativeQuery) {
 | 
			
		||||
            // throw an error if the export require other modifier, which is
 | 
			
		||||
            // not allowed when the export return a `NativeQuery`
 | 
			
		||||
            if (\count($export->supportsModifiers()) > 0) {
 | 
			
		||||
                throw new \LogicException("The export with alias `{$exportAlias}` return ".'a `\Doctrine\ORM\NativeQuery` and supports modifiers, which is not allowed. Either the method `supportsModifiers` should return an empty array, or return a `Doctrine\ORM\QueryBuilder`');
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($query instanceof QueryBuilder) {
 | 
			
		||||
            // handle filters
 | 
			
		||||
            $this->handleFilters($export, $query, $data[ExportType::FILTER_KEY], $centers, $context);
 | 
			
		||||
 | 
			
		||||
            // handle aggregators
 | 
			
		||||
            $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers, $context);
 | 
			
		||||
 | 
			
		||||
            $this->logger->notice('[export] will execute this qb in export', [
 | 
			
		||||
                'dql' => $query->getDQL(),
 | 
			
		||||
            ]);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new \UnexpectedValueException('The method `intiateQuery` should return a `\Doctrine\ORM\NativeQuery` or a `Doctrine\ORM\QueryBuilder` object.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $result = $export->getResult($query, $export->denormalizeFormData($data[ExportType::EXPORT_KEY], $context));
 | 
			
		||||
 | 
			
		||||
        if (!is_iterable($result)) {
 | 
			
		||||
            throw new \UnexpectedValueException(sprintf('The result of the export should be an iterable, %s given', \gettype($result)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** @var FormatterInterface $formatter */
 | 
			
		||||
        $formatter = $this->getFormatter($this->getFormatterAlias($data));
 | 
			
		||||
        $filtersData = [];
 | 
			
		||||
        $aggregatorsData = [];
 | 
			
		||||
 | 
			
		||||
        if ($query instanceof QueryBuilder) {
 | 
			
		||||
            $aggregators = $this->retrieveUsedAggregators($data[ExportType::AGGREGATOR_KEY]);
 | 
			
		||||
 | 
			
		||||
            foreach ($aggregators as $alias => $aggregator) {
 | 
			
		||||
                $aggregatorsData[$alias] = $data[ExportType::AGGREGATOR_KEY][$alias]['form'];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $filters = $this->retrieveUsedFilters($data[ExportType::FILTER_KEY]);
 | 
			
		||||
 | 
			
		||||
        foreach ($filters as $alias => $filter) {
 | 
			
		||||
            $filtersData[$alias] = $data[ExportType::FILTER_KEY][$alias]['form'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (method_exists($formatter, 'generate')) {
 | 
			
		||||
            return $formatter->generate(
 | 
			
		||||
                $result,
 | 
			
		||||
                $formatterData,
 | 
			
		||||
                $exportAlias,
 | 
			
		||||
                $data[ExportType::EXPORT_KEY],
 | 
			
		||||
                $filtersData,
 | 
			
		||||
                $aggregatorsData,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        trigger_deprecation('chill-project/chill-bundles', '3.10', '%s should implements the "generate" method', FormatterInterface::class);
 | 
			
		||||
 | 
			
		||||
        $generatedExport =  $formatter->getResponse(
 | 
			
		||||
            $result,
 | 
			
		||||
            $formatterData,
 | 
			
		||||
            $exportAlias,
 | 
			
		||||
            $data[ExportType::EXPORT_KEY],
 | 
			
		||||
            $filtersData,
 | 
			
		||||
            $aggregatorsData,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new FormattedExportGeneration($generatedExport->getContent(), $generatedExport->headers->get('content-type'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate a response which contains the requested data.
 | 
			
		||||
     */
 | 
			
		||||
    public function generate(string $exportAlias, array $pickedCentersData, array $data, array $formatterData): Response
 | 
			
		||||
    {
 | 
			
		||||
        $generated = $this->generateExport(
 | 
			
		||||
            $exportAlias,
 | 
			
		||||
            $pickedCentersData,
 | 
			
		||||
            $data,
 | 
			
		||||
            $formatterData,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new Response($generated->content, headers: ['Content-Type' => $generated->contentType]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param string $alias
 | 
			
		||||
     *
 | 
			
		||||
@@ -467,7 +352,7 @@ class ExportManager
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get the aggregators typse used in the form export data.
 | 
			
		||||
     * get the aggregators types used in the form export data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param array $data the data from the export form
 | 
			
		||||
     *
 | 
			
		||||
@@ -475,9 +360,15 @@ class ExportManager
 | 
			
		||||
     */
 | 
			
		||||
    public function getUsedAggregatorsAliases(array $data): array
 | 
			
		||||
    {
 | 
			
		||||
        $aggregators = $this->retrieveUsedAggregators($data[ExportType::AGGREGATOR_KEY]);
 | 
			
		||||
        $keys = [];
 | 
			
		||||
 | 
			
		||||
        return array_keys(iterator_to_array($aggregators));
 | 
			
		||||
        foreach ($data as $alias => $aggregatorData) {
 | 
			
		||||
            if (true === $aggregatorData['enabled']) {
 | 
			
		||||
                $keys[] = $alias;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return array_values(array_unique($keys));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -489,7 +380,6 @@ class ExportManager
 | 
			
		||||
        DirectExportInterface|ExportInterface|null $export = null,
 | 
			
		||||
        ?array $centers = null,
 | 
			
		||||
    ): bool {
 | 
			
		||||
        dump(__METHOD__, $this->tokenStorage->getToken()->getUser());
 | 
			
		||||
        if ($element instanceof ExportInterface || $element instanceof DirectExportInterface) {
 | 
			
		||||
            $role = $element->requiredRole();
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -510,7 +400,7 @@ class ExportManager
 | 
			
		||||
                $role
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        dump($centers);
 | 
			
		||||
 | 
			
		||||
        foreach ($centers as $center) {
 | 
			
		||||
            if (false === $this->authorizationChecker->isGranted($role, $center)) {
 | 
			
		||||
                // debugging
 | 
			
		||||
@@ -527,184 +417,4 @@ class ExportManager
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * build the array required for defining centers and circles in the initiate
 | 
			
		||||
     * queries of ExportElementsInterfaces.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Chill\MainBundle\Entity\Center[] $centers
 | 
			
		||||
     */
 | 
			
		||||
    private function buildCenterReachableScopes(array $centers, ExportElementInterface $element)
 | 
			
		||||
    {
 | 
			
		||||
        $r = [];
 | 
			
		||||
 | 
			
		||||
        $user = $this->tokenStorage->getToken()->getUser();
 | 
			
		||||
 | 
			
		||||
        if (!$user instanceof User) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($centers as $center) {
 | 
			
		||||
            $r[] = [
 | 
			
		||||
                'center' => $center,
 | 
			
		||||
                'circles' => $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                    $user,
 | 
			
		||||
                    $element->requiredRole(),
 | 
			
		||||
                    $center
 | 
			
		||||
                ),
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Alter the query with selected aggregators.
 | 
			
		||||
     *
 | 
			
		||||
     * Check for acl. If an user is not authorized to see an aggregator, throw an
 | 
			
		||||
     * UnauthorizedException.
 | 
			
		||||
     *
 | 
			
		||||
     * @throw UnauthorizedHttpException if the user is not authorized
 | 
			
		||||
     */
 | 
			
		||||
    private function handleAggregators(
 | 
			
		||||
        ExportInterface $export,
 | 
			
		||||
        QueryBuilder $qb,
 | 
			
		||||
        array $data,
 | 
			
		||||
        array $center,
 | 
			
		||||
        ExportGenerationContext $context,
 | 
			
		||||
    ) {
 | 
			
		||||
        $aggregators = $this->retrieveUsedAggregators($data);
 | 
			
		||||
 | 
			
		||||
        foreach ($aggregators as $alias => $aggregator) {
 | 
			
		||||
            $formData = $data[$alias];
 | 
			
		||||
            $aggregator->alterQuery($qb, $aggregator->denormalizeFormData($formData['form'], $context));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * alter the query with selected filters.
 | 
			
		||||
     *
 | 
			
		||||
     * This function check the acl.
 | 
			
		||||
     *
 | 
			
		||||
     * @param \Chill\MainBundle\Entity\Center[] $centers the picked centers
 | 
			
		||||
     *
 | 
			
		||||
     * @throw UnauthorizedHttpException if the user is not authorized
 | 
			
		||||
     */
 | 
			
		||||
    private function handleFilters(
 | 
			
		||||
        ExportInterface $export,
 | 
			
		||||
        QueryBuilder $qb,
 | 
			
		||||
        mixed $data,
 | 
			
		||||
        array $centers,
 | 
			
		||||
        ExportGenerationContext $context,
 | 
			
		||||
    ) {
 | 
			
		||||
        $filters = $this->retrieveUsedFilters($data);
 | 
			
		||||
 | 
			
		||||
        foreach ($filters as $alias => $filter) {
 | 
			
		||||
            $formData = $data[$alias];
 | 
			
		||||
 | 
			
		||||
            $this->logger->debug('alter query by filter '.$alias, [
 | 
			
		||||
                'class' => self::class, 'function' => __FUNCTION__,
 | 
			
		||||
            ]);
 | 
			
		||||
            $filter->alterQuery($qb, $filter->denormalizeFormData($formData['form'], $context));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return iterable<string, AggregatorInterface>
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedAggregators(mixed $data): iterable
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $alias => $aggregatorData) {
 | 
			
		||||
            if (true === $aggregatorData['enabled']) {
 | 
			
		||||
                yield $alias => $this->getAggregator($alias);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string[]
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedAggregatorsType(mixed $data)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->retrieveUsedAggregators($data) as $alias => $aggregator) {
 | 
			
		||||
            if (!\in_array($aggregator->applyOn(), $usedTypes, true)) {
 | 
			
		||||
                $usedTypes[] = $aggregator->applyOn();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $usedTypes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function retrieveUsedFilters(mixed $data): iterable
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $alias => $filterData) {
 | 
			
		||||
            if (true === $filterData['enabled']) {
 | 
			
		||||
                yield $alias => $this->getFilter($alias);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve the filter used in this export.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array an array with types
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedFiltersType(mixed $data): iterable
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $alias => $filterData) {
 | 
			
		||||
            if (true === $filterData['enabled']) {
 | 
			
		||||
                $filter = $this->getFilter($alias);
 | 
			
		||||
 | 
			
		||||
                if (!\in_array($filter->applyOn(), $usedTypes, true)) {
 | 
			
		||||
                    $usedTypes[] = $filter->applyOn();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $usedTypes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * parse the data to retrieve the used filters and aggregators.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string[]
 | 
			
		||||
     */
 | 
			
		||||
    private function retrieveUsedModifiers(mixed $data)
 | 
			
		||||
    {
 | 
			
		||||
        if (null === $data) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $usedTypes = array_merge(
 | 
			
		||||
            $this->retrieveUsedFiltersType($data[ExportType::FILTER_KEY]),
 | 
			
		||||
            $this->retrieveUsedAggregatorsType($data[ExportType::AGGREGATOR_KEY])
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $this->logger->debug(
 | 
			
		||||
            'Required types are '.implode(', ', $usedTypes),
 | 
			
		||||
            ['class' => self::class, 'function' => __FUNCTION__]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return array_unique($usedTypes);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,10 @@ interface ModifierInterface extends ExportElementInterface
 | 
			
		||||
     *
 | 
			
		||||
     * @param QueryBuilder $qb   the QueryBuilder initiated by the Export (and eventually modified by other Modifiers)
 | 
			
		||||
     * @param mixed[]      $data the data from the Form (builded by buildForm)
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function alterQuery(QueryBuilder $qb, $data/* , ExportGenerationContext $exportGenerationContext */);
 | 
			
		||||
    public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * On which type of Export this ModifiersInterface may apply.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										205
									
								
								src/Bundle/ChillMainBundle/Tests/Export/ExportGeneratorTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/Bundle/ChillMainBundle/Tests/Export/ExportGeneratorTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chill is a software for social workers
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\MainBundle\Tests\Export;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Export\AggregatorInterface;
 | 
			
		||||
use Chill\MainBundle\Export\DirectExportInterface;
 | 
			
		||||
use Chill\MainBundle\Export\ExportConfigNormalizer;
 | 
			
		||||
use Chill\MainBundle\Export\ExportGenerationContext;
 | 
			
		||||
use Chill\MainBundle\Export\ExportGenerator;
 | 
			
		||||
use Chill\MainBundle\Export\ExportInterface;
 | 
			
		||||
use Chill\MainBundle\Export\ExportManager;
 | 
			
		||||
use Chill\MainBundle\Export\FilterInterface;
 | 
			
		||||
use Chill\MainBundle\Export\FormattedExportGeneration;
 | 
			
		||||
use Chill\MainBundle\Export\FormatterInterface;
 | 
			
		||||
use Doctrine\ORM\NativeQuery;
 | 
			
		||||
use Doctrine\ORM\QueryBuilder;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Prophecy\Argument;
 | 
			
		||||
use Prophecy\PhpUnit\ProphecyTrait;
 | 
			
		||||
use Psr\Log\NullLogger;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @internal
 | 
			
		||||
 *
 | 
			
		||||
 * @coversNothing
 | 
			
		||||
 */
 | 
			
		||||
class ExportGeneratorTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    use ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
    public function testGenerateHappyScenario()
 | 
			
		||||
    {
 | 
			
		||||
        $initialData = ['initial' => 'test'];
 | 
			
		||||
        $fullConfig = [
 | 
			
		||||
            'export' => $formExportData = ['key' => 'form1'],
 | 
			
		||||
            'filters' => [
 | 
			
		||||
                'dummy_filter' => ['enabled' => true, 'form' => $formFilterData = ['key' => 'form2']],
 | 
			
		||||
                'disabled_filter' => ['enabled' => false],
 | 
			
		||||
            ],
 | 
			
		||||
            'aggregators' => [
 | 
			
		||||
                'dummy_aggregator' => ['enabled' => true, 'form' => $formAggregatorData = ['key' => 'form3']],
 | 
			
		||||
                'disabled_aggregator' => ['enabled' => false],
 | 
			
		||||
            ],
 | 
			
		||||
            'pick_formatter' => 'xlsx',
 | 
			
		||||
            'formatter' => ['form' => $formatterData = ['key' => 'form4']],
 | 
			
		||||
            'centers' => [$centerA = new Center(), $centerB = new Center()],
 | 
			
		||||
        ];
 | 
			
		||||
        $user = new User();
 | 
			
		||||
 | 
			
		||||
        $export = $this->prophesize(ExportInterface::class);
 | 
			
		||||
        $filter = $this->prophesize(FilterInterface::class);
 | 
			
		||||
        $filter->applyOn()->willReturn('tagada');
 | 
			
		||||
        $aggregator = $this->prophesize(AggregatorInterface::class);
 | 
			
		||||
        $aggregator->applyOn()->willReturn('tsointsoin');
 | 
			
		||||
        $formatter = $this->prophesize(FormatterInterface::class);
 | 
			
		||||
 | 
			
		||||
        $query = $this->prophesize(QueryBuilder::class);
 | 
			
		||||
 | 
			
		||||
        // required methods
 | 
			
		||||
        $export->initiateQuery(
 | 
			
		||||
            ['tagada', 'tsointsoin'],
 | 
			
		||||
            [['center' => $centerA, 'circles' => []], ['center' => $centerB, 'circles' => []]],
 | 
			
		||||
            ['key' => 'form1'],
 | 
			
		||||
        )->shouldBeCalled()->willReturn($query->reveal());
 | 
			
		||||
        $export->getResult($query->reveal(), $formExportData, Argument::that(static fn (ExportGenerationContext $context) => $context->byUser === $user))
 | 
			
		||||
            ->shouldBeCalled()->willReturn([['result0' => '0']]);
 | 
			
		||||
 | 
			
		||||
        $filter->alterQuery($query->reveal(), $formFilterData, Argument::that(static fn (ExportGenerationContext $context) => $context->byUser === $user))
 | 
			
		||||
            ->shouldBeCalled();
 | 
			
		||||
        $aggregator->alterQuery($query->reveal(), $formAggregatorData, Argument::that(static fn (ExportGenerationContext $context) => $context->byUser === $user))
 | 
			
		||||
            ->shouldBeCalled();
 | 
			
		||||
 | 
			
		||||
        $formatter->generate(
 | 
			
		||||
            [['result0' => '0']],
 | 
			
		||||
            $formatterData,
 | 
			
		||||
            'dummy',
 | 
			
		||||
            $formExportData,
 | 
			
		||||
            ['dummy_filter' => $formFilterData],
 | 
			
		||||
            ['dummy_aggregator' => $formAggregatorData]
 | 
			
		||||
        )
 | 
			
		||||
            ->shouldBeCalled()
 | 
			
		||||
            ->willReturn(new FormattedExportGeneration('export result', 'text/text'));
 | 
			
		||||
 | 
			
		||||
        $exportConfigNormalizer = $this->prophesize(ExportConfigNormalizer::class);
 | 
			
		||||
        $exportConfigNormalizer->denormalizeConfig('dummy', $initialData)->willReturn($fullConfig);
 | 
			
		||||
 | 
			
		||||
        $exportManager = $this->prophesize(ExportManager::class);
 | 
			
		||||
        $exportManager->getExport('dummy')->willReturn($export->reveal());
 | 
			
		||||
        $exportManager->getFilter('dummy_filter')->willReturn($filter->reveal());
 | 
			
		||||
        $exportManager->getAggregator('dummy_aggregator')->willReturn($aggregator->reveal());
 | 
			
		||||
        $exportManager->getFormatter('xlsx')->willReturn($formatter->reveal());
 | 
			
		||||
 | 
			
		||||
        $generator = new ExportGenerator($exportManager->reveal(), $exportConfigNormalizer->reveal(), new NullLogger());
 | 
			
		||||
 | 
			
		||||
        $actual = $generator->generate('dummy', $initialData, $user);
 | 
			
		||||
 | 
			
		||||
        self::assertInstanceOf(FormattedExportGeneration::class, $actual);
 | 
			
		||||
        self::assertEquals('export result', $actual->content);
 | 
			
		||||
        self::assertEquals('text/text', $actual->contentType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testGenerateNativeSqlHappyScenario()
 | 
			
		||||
    {
 | 
			
		||||
        $initialData = ['initial' => 'test'];
 | 
			
		||||
        $fullConfig = [
 | 
			
		||||
            'export' => $formExportData = ['key' => 'form1'],
 | 
			
		||||
            'filters' => [],
 | 
			
		||||
            'aggregators' => [],
 | 
			
		||||
            'pick_formatter' => 'xlsx',
 | 
			
		||||
            'formatter' => ['form' => $formatterData = ['key' => 'form4']],
 | 
			
		||||
            'centers' => [$centerA = new Center(), $centerB = new Center()],
 | 
			
		||||
        ];
 | 
			
		||||
        $user = new User();
 | 
			
		||||
 | 
			
		||||
        $export = $this->prophesize(ExportInterface::class);
 | 
			
		||||
        $formatter = $this->prophesize(FormatterInterface::class);
 | 
			
		||||
 | 
			
		||||
        $query = $this->prophesize(NativeQuery::class);
 | 
			
		||||
 | 
			
		||||
        // required methods
 | 
			
		||||
        $export->initiateQuery(
 | 
			
		||||
            [],
 | 
			
		||||
            [['center' => $centerA, 'circles' => []], ['center' => $centerB, 'circles' => []]],
 | 
			
		||||
            ['key' => 'form1'],
 | 
			
		||||
        )->shouldBeCalled()->willReturn($query->reveal());
 | 
			
		||||
        $export->getResult($query->reveal(), $formExportData, Argument::that(static fn (ExportGenerationContext $context) => $context->byUser === $user))
 | 
			
		||||
            ->shouldBeCalled()->willReturn([['result0' => '0']]);
 | 
			
		||||
        $export->supportsModifiers()->willReturn([]);
 | 
			
		||||
 | 
			
		||||
        $formatter->generate(
 | 
			
		||||
            [['result0' => '0']],
 | 
			
		||||
            $formatterData,
 | 
			
		||||
            'dummy',
 | 
			
		||||
            $formExportData,
 | 
			
		||||
            [],
 | 
			
		||||
            []
 | 
			
		||||
        )
 | 
			
		||||
            ->shouldBeCalled()
 | 
			
		||||
            ->willReturn(new FormattedExportGeneration('export result', 'text/text'));
 | 
			
		||||
 | 
			
		||||
        $exportConfigNormalizer = $this->prophesize(ExportConfigNormalizer::class);
 | 
			
		||||
        $exportConfigNormalizer->denormalizeConfig('dummy', $initialData)->willReturn($fullConfig);
 | 
			
		||||
 | 
			
		||||
        $exportManager = $this->prophesize(ExportManager::class);
 | 
			
		||||
        $exportManager->getExport('dummy')->willReturn($export->reveal());
 | 
			
		||||
        $exportManager->getFormatter('xlsx')->willReturn($formatter->reveal());
 | 
			
		||||
 | 
			
		||||
        $generator = new ExportGenerator($exportManager->reveal(), $exportConfigNormalizer->reveal(), new NullLogger());
 | 
			
		||||
 | 
			
		||||
        $actual = $generator->generate('dummy', $initialData, $user);
 | 
			
		||||
 | 
			
		||||
        self::assertInstanceOf(FormattedExportGeneration::class, $actual);
 | 
			
		||||
        self::assertEquals('export result', $actual->content);
 | 
			
		||||
        self::assertEquals('text/text', $actual->contentType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testGenerateDirectExportHappyScenario()
 | 
			
		||||
    {
 | 
			
		||||
        $initialData = ['initial' => 'test'];
 | 
			
		||||
        $fullConfig = [
 | 
			
		||||
            'export' => $formExportData = ['key' => 'form1'],
 | 
			
		||||
            'filters' => [],
 | 
			
		||||
            'aggregators' => [],
 | 
			
		||||
            'pick_formatter' => 'xlsx',
 | 
			
		||||
            'formatter' => ['form' => $formatterData = ['key' => 'form4']],
 | 
			
		||||
            'centers' => [$centerA = new Center(), $centerB = new Center()],
 | 
			
		||||
        ];
 | 
			
		||||
        $user = new User();
 | 
			
		||||
 | 
			
		||||
        $export = $this->prophesize(DirectExportInterface::class);
 | 
			
		||||
 | 
			
		||||
        // required methods
 | 
			
		||||
        $export->generate(
 | 
			
		||||
            [['center' => $centerA, 'circles' => []], ['center' => $centerB, 'circles' => []]],
 | 
			
		||||
            ['key' => 'form1'],
 | 
			
		||||
            Argument::that(static fn (ExportGenerationContext $context) => $user === $context->byUser),
 | 
			
		||||
        )->shouldBeCalled()
 | 
			
		||||
            ->willReturn(new FormattedExportGeneration('export result', 'text/text'));
 | 
			
		||||
 | 
			
		||||
        $exportConfigNormalizer = $this->prophesize(ExportConfigNormalizer::class);
 | 
			
		||||
        $exportConfigNormalizer->denormalizeConfig('dummy', $initialData)->willReturn($fullConfig);
 | 
			
		||||
 | 
			
		||||
        $exportManager = $this->prophesize(ExportManager::class);
 | 
			
		||||
        $exportManager->getExport('dummy')->willReturn($export->reveal());
 | 
			
		||||
 | 
			
		||||
        $generator = new ExportGenerator($exportManager->reveal(), $exportConfigNormalizer->reveal(), new NullLogger());
 | 
			
		||||
 | 
			
		||||
        $actual = $generator->generate('dummy', $initialData, $user);
 | 
			
		||||
 | 
			
		||||
        self::assertInstanceOf(FormattedExportGeneration::class, $actual);
 | 
			
		||||
        self::assertEquals('export result', $actual->content);
 | 
			
		||||
        self::assertEquals('text/text', $actual->contentType);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user