mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-28 13:06:13 +00:00
[generic doc] listing generic doc for person
This commit is contained in:
parent
eb107f5a15
commit
40af1e64ac
@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\DocStoreBundle;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
@ -22,6 +23,8 @@ class ChillDocStoreBundle extends Bundle
|
||||
{
|
||||
$container->registerForAutoconfiguration(GenericDocForAccompanyingPeriodProviderInterface::class)
|
||||
->addTag('chill_doc_store.generic_doc_accompanying_period_provider');
|
||||
$container->registerForAutoconfiguration(GenericDocForPersonProviderInterface::class)
|
||||
->addTag('chill_doc_store.generic_doc_person_provider');
|
||||
$container->registerForAutoconfiguration(GenericDocRendererInterface::class)
|
||||
->addTag('chill_doc_store.generic_doc_renderer');
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
<?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\DocStoreBundle\Controller;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\Manager;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
final readonly class GenericDocForPerson
|
||||
{
|
||||
public function __construct(
|
||||
private FilterOrderHelperFactory $filterOrderHelperFactory,
|
||||
private Manager $manager,
|
||||
private PaginatorFactory $paginator,
|
||||
private Security $security,
|
||||
private EngineInterface $twig,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*
|
||||
* @Route("/{_locale}/doc-store/generic-doc/by-person/{id}/index", name="chill_docstore_generic-doc_by-person_index")
|
||||
*/
|
||||
public function list(Person $person): Response
|
||||
{
|
||||
if (!$this->security->isGranted(PersonDocumentVoter::SEE, $person)) {
|
||||
throw new AccessDeniedHttpException("not allowed to see the documents for person");
|
||||
}
|
||||
|
||||
$filterBuilder = $this->filterOrderHelperFactory
|
||||
->create(self::class)
|
||||
->addSearchBox()
|
||||
->addDateRange('dateRange', 'generic_doc.filter.date-range');
|
||||
|
||||
if ([] !== $places = $this->manager->placesForPerson($person)) {
|
||||
$filterBuilder->addCheckbox('places', $places, [], array_map(
|
||||
static fn (string $k) => 'generic_doc.filter.keys.' . $k,
|
||||
$places
|
||||
));
|
||||
}
|
||||
|
||||
$filter = $filterBuilder
|
||||
->build();
|
||||
|
||||
['to' => $endDate, 'from' => $startDate ] = $filter->getDateRangeData('dateRange');
|
||||
$content = $filter->getQueryString();
|
||||
|
||||
$nb = $this->manager->countDocForPerson(
|
||||
$person,
|
||||
$startDate,
|
||||
$endDate,
|
||||
$content,
|
||||
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
|
||||
);
|
||||
$paginator = $this->paginator->create($nb);
|
||||
|
||||
$documents = $this->manager->findDocForPerson(
|
||||
$person,
|
||||
$paginator->getCurrentPageFirstItemNumber(),
|
||||
$paginator->getItemsPerPage(),
|
||||
$startDate,
|
||||
$endDate,
|
||||
$content,
|
||||
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
|
||||
);
|
||||
|
||||
return new Response($this->twig->render(
|
||||
'@ChillDocStore/GenericDoc/person_list.html.twig',
|
||||
[
|
||||
'person' => $person,
|
||||
'pagination' => $paginator,
|
||||
'documents' => iterator_to_array($documents),
|
||||
'filter' => $filter,
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
}
|
@ -25,8 +25,6 @@ interface GenericDocForAccompanyingPeriodProviderInterface
|
||||
|
||||
/**
|
||||
* Return true if the user is allowed to see some documents for this provider.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool;
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?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\DocStoreBundle\GenericDoc;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
|
||||
interface GenericDocForPersonProviderInterface
|
||||
{
|
||||
public function buildFetchQueryForPerson(
|
||||
Person $person,
|
||||
?DateTimeImmutable $startDate = null,
|
||||
?DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
?string $origin = null
|
||||
): FetchQueryInterface;
|
||||
|
||||
/**
|
||||
* Return true if the user is allowed to see some documents for this provider.
|
||||
*/
|
||||
public function isAllowedForPerson(Person $person): bool;
|
||||
}
|
@ -17,16 +17,21 @@ use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
|
||||
class Manager
|
||||
final readonly class Manager
|
||||
{
|
||||
private readonly FetchQueryToSqlBuilder $builder;
|
||||
private FetchQueryToSqlBuilder $builder;
|
||||
|
||||
public function __construct(
|
||||
/**
|
||||
* @var iterable<GenericDocForAccompanyingPeriodProviderInterface>
|
||||
*/
|
||||
private readonly iterable $providersForAccompanyingPeriod,
|
||||
private readonly Connection $connection,
|
||||
private iterable $providersForAccompanyingPeriod,
|
||||
|
||||
/**
|
||||
* @var iterable<GenericDocForPersonProviderInterface>
|
||||
*/
|
||||
private iterable $providersForPerson,
|
||||
private Connection $connection,
|
||||
) {
|
||||
$this->builder = new FetchQueryToSqlBuilder();
|
||||
}
|
||||
@ -44,6 +49,11 @@ class Manager
|
||||
): int {
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||
|
||||
return $this->countDoc($sql, $params, $types);
|
||||
}
|
||||
|
||||
private function countDoc(string $sql, array $params, array $types): int
|
||||
{
|
||||
if ($sql === '') {
|
||||
return 0;
|
||||
}
|
||||
@ -60,8 +70,21 @@ class Manager
|
||||
return $number;
|
||||
}
|
||||
|
||||
public function countDocForPerson(
|
||||
Person $person,
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
): int {
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||
|
||||
return $this->countDoc($sql, $params, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $places places to search. When empty, search in all places
|
||||
* @return iterable<GenericDocDTO>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function findDocForAccompanyingPeriod(
|
||||
@ -75,6 +98,15 @@ class Manager
|
||||
): iterable {
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
|
||||
|
||||
return $this->findDocs($accompanyingPeriod, $sql, $params, $types, $offset, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \JsonException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function findDocs(AccompanyingPeriod|Person $linked, string $sql, array $params, array $types, int $offset, int $limit): iterable
|
||||
{
|
||||
if ($sql === '') {
|
||||
return [];
|
||||
}
|
||||
@ -88,20 +120,50 @@ class Manager
|
||||
$row['key'],
|
||||
json_decode($row['identifiers'], true, 512, JSON_THROW_ON_ERROR),
|
||||
new \DateTimeImmutable($row['doc_date']),
|
||||
$accompanyingPeriod,
|
||||
$linked,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $places places to search. When empty, search in all places
|
||||
* @return iterable<GenericDocDTO>
|
||||
*/
|
||||
public function findDocForPerson(
|
||||
Person $person,
|
||||
int $offset = 0,
|
||||
int $limit = 20,
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
array $places = []
|
||||
): iterable {
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places);
|
||||
|
||||
return $this->findDocs($person, $sql, $params, $types, $offset, $limit);
|
||||
}
|
||||
|
||||
public function placesForPerson(Person $person): array
|
||||
{
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person);
|
||||
|
||||
return $this->places($sql, $params, $types);
|
||||
}
|
||||
|
||||
public function placesForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): array
|
||||
{
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod);
|
||||
|
||||
return $this->places($sql, $params, $types);
|
||||
}
|
||||
|
||||
private function places(string $sql, array $params, array $types): array
|
||||
{
|
||||
if ($sql === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq";
|
||||
$runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq ORDER BY key";
|
||||
|
||||
$keys = [];
|
||||
|
||||
@ -122,17 +184,28 @@ class Manager
|
||||
?string $content = null,
|
||||
array $places = [],
|
||||
): array {
|
||||
$sql = [];
|
||||
$params = [];
|
||||
$types = [];
|
||||
$queries = [];
|
||||
|
||||
if ($linked instanceof AccompanyingPeriod) {
|
||||
foreach ($this->providersForAccompanyingPeriod as $provider) {
|
||||
if (!$provider->isAllowedForAccompanyingPeriod($linked)) {
|
||||
continue;
|
||||
}
|
||||
$query = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content);
|
||||
$queries[] = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->providersForPerson as $provider) {
|
||||
if (!$provider->isAllowedForPerson($linked)) {
|
||||
continue;
|
||||
}
|
||||
$queries[] = $provider->buildFetchQueryForPerson($linked, $startDate, $endDate, $content);
|
||||
}
|
||||
}
|
||||
$sql = [];
|
||||
$params = [];
|
||||
$types = [];
|
||||
|
||||
foreach ($queries as $query) {
|
||||
if ([] !== $places and !in_array($query->getSelectKeyString(), $places, true)) {
|
||||
continue;
|
||||
}
|
||||
@ -143,7 +216,6 @@ class Manager
|
||||
$params = [...$params, ...$p];
|
||||
$types = [...$types, ...$t];
|
||||
}
|
||||
}
|
||||
|
||||
return ['sql' => implode(' UNION ', $sql), 'params' => $params, 'types' => $types];
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
<?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\DocStoreBundle\GenericDoc\Providers;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class PersonDocumentGenericDocProvider implements GenericDocForPersonProviderInterface
|
||||
{
|
||||
public const KEY = 'person_document';
|
||||
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildFetchQueryForPerson(
|
||||
Person $person,
|
||||
?DateTimeImmutable $startDate = null,
|
||||
?DateTimeImmutable $endDate = null,
|
||||
?string $content = null,
|
||||
?string $origin = null
|
||||
): FetchQueryInterface {
|
||||
return $this->personDocumentACLAwareRepository->buildFetchQueryForPerson(
|
||||
$person,
|
||||
$startDate,
|
||||
$endDate,
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
public function isAllowedForPerson(Person $person): bool
|
||||
{
|
||||
return $this->security->isGranted(PersonDocumentVoter::SEE, $person);
|
||||
}
|
||||
}
|
@ -12,20 +12,25 @@ declare(strict_types=1);
|
||||
namespace Chill\DocStoreBundle\GenericDoc\Renderer;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
||||
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||
use Chill\DocStoreBundle\GenericDoc\Twig\GenericDocRendererInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\Providers\AccompanyingProviderCourseDocumentGenericDoc;
|
||||
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
|
||||
use Chill\DocStoreBundle\Repository\PersonDocumentRepository;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
|
||||
final readonly class AccompanyingCourseDocumentGenericDocRenderer implements GenericDocRendererInterface
|
||||
{
|
||||
public function __construct(
|
||||
private AccompanyingCourseDocumentRepository $accompanyingCourseDocumentRepository,
|
||||
private PersonDocumentRepository $personDocumentRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(GenericDocDTO $genericDocDTO, $options = []): bool
|
||||
{
|
||||
return $genericDocDTO->key === AccompanyingProviderCourseDocumentGenericDoc::KEY;
|
||||
return $genericDocDTO->key === AccompanyingProviderCourseDocumentGenericDoc::KEY
|
||||
|| $genericDocDTO->key === PersonDocumentGenericDocProvider::KEY;
|
||||
}
|
||||
|
||||
public function getTemplate(GenericDocDTO $genericDocDTO, $options = []): string
|
||||
@ -35,10 +40,19 @@ final readonly class AccompanyingCourseDocumentGenericDocRenderer implements Gen
|
||||
|
||||
public function getTemplateData(GenericDocDTO $genericDocDTO, $options = []): array
|
||||
{
|
||||
if ($genericDocDTO->linked instanceof AccompanyingPeriod) {
|
||||
return [
|
||||
'document' => $this->accompanyingCourseDocumentRepository->find($genericDocDTO->identifiers['id']),
|
||||
'accompanyingCourse' => $genericDocDTO->linked,
|
||||
'options' => $options,
|
||||
];
|
||||
}
|
||||
|
||||
// this is a person
|
||||
return [
|
||||
'document' => $this->personDocumentRepository->find($genericDocDTO->identifiers['id']),
|
||||
'person' => $genericDocDTO->linked,
|
||||
'options' => $options,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -12,30 +12,36 @@ declare(strict_types=1);
|
||||
namespace Chill\DocStoreBundle\Repository;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareRepositoryInterface
|
||||
{
|
||||
private AuthorizationHelperInterface $authorizationHelper;
|
||||
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser;
|
||||
|
||||
private CenterResolverDispatcher $centerResolverDispatcher;
|
||||
private CenterResolverManagerInterface $centerResolverManager;
|
||||
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private Security $security;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, AuthorizationHelperInterface $authorizationHelper, CenterResolverDispatcher $centerResolverDispatcher, Security $security)
|
||||
public function __construct(EntityManagerInterface $em, CenterResolverManagerInterface $centerResolverManager, AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->authorizationHelper = $authorizationHelper;
|
||||
$this->centerResolverDispatcher = $centerResolverDispatcher;
|
||||
$this->security = $security;
|
||||
$this->centerResolverManager = $centerResolverManager;
|
||||
$this->authorizationHelperForCurrentUser = $authorizationHelperForCurrentUser;
|
||||
}
|
||||
|
||||
public function buildQueryByPerson(Person $person): QueryBuilder
|
||||
@ -49,6 +55,62 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
||||
return $qb;
|
||||
}
|
||||
|
||||
|
||||
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface
|
||||
{
|
||||
$query = $this->buildBaseFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||
|
||||
return $this->addFetchQueryByPersonACL($query, $person);
|
||||
}
|
||||
|
||||
public function buildBaseFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery
|
||||
{
|
||||
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||
|
||||
$query = new FetchQuery(
|
||||
PersonDocumentGenericDocProvider::KEY,
|
||||
sprintf('jsonb_build_object(\'id\', person_document.%s)', $personDocMetadata->getSingleIdentifierColumnName()),
|
||||
sprintf('person_document.%s', $personDocMetadata->getColumnName('date')),
|
||||
sprintf('%s AS person_document', $personDocMetadata->getSchemaName().'.'.$personDocMetadata->getTableName())
|
||||
);
|
||||
|
||||
$query->addWhereClause(
|
||||
sprintf('person_document.%s = ?', $personDocMetadata->getSingleAssociationJoinColumnName('person')),
|
||||
[$person->getId()],
|
||||
[Types::INTEGER]
|
||||
);
|
||||
|
||||
if (null !== $startDate) {
|
||||
$query->addWhereClause(
|
||||
sprintf('? <= %s', $personDocMetadata->getColumnName('date')),
|
||||
[$startDate],
|
||||
[Types::DATE_IMMUTABLE]
|
||||
);
|
||||
}
|
||||
|
||||
if (null !== $endDate) {
|
||||
$query->addWhereClause(
|
||||
sprintf('? >= %s', $personDocMetadata->getColumnName('date')),
|
||||
[$endDate],
|
||||
[Types::DATE_IMMUTABLE]
|
||||
);
|
||||
}
|
||||
|
||||
if (null !== $content and '' !== $content) {
|
||||
$query->addWhereClause(
|
||||
sprintf(
|
||||
'(%s ilike ? OR %s ilike ?)',
|
||||
$personDocMetadata->getColumnName('title'),
|
||||
$personDocMetadata->getColumnName('description')
|
||||
),
|
||||
['%' . $content . '%', '%' . $content . '%'],
|
||||
[Types::STRING, Types::STRING]
|
||||
);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function countByPerson(Person $person): int
|
||||
{
|
||||
$qb = $this->buildQueryByPerson($person)->select('COUNT(d)');
|
||||
@ -75,16 +137,58 @@ class PersonDocumentACLAwareRepository implements PersonDocumentACLAwareReposito
|
||||
|
||||
private function addACL(QueryBuilder $qb, Person $person): void
|
||||
{
|
||||
$center = $this->centerResolverDispatcher->resolveCenter($person);
|
||||
$reachableScopes = [];
|
||||
|
||||
$reachableScopes = $this->authorizationHelper
|
||||
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||
$reachableScopes = [
|
||||
...$reachableScopes,
|
||||
...$this->authorizationHelperForCurrentUser
|
||||
->getReachableScopes(
|
||||
$this->security->getUser(),
|
||||
PersonDocumentVoter::SEE,
|
||||
$center
|
||||
);
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
if ([] === $reachableScopes) {
|
||||
$qb->andWhere("'FALSE' = 'TRUE'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$qb->andWhere($qb->expr()->in('d.scope', ':scopes'))
|
||||
->setParameter('scopes', $reachableScopes);
|
||||
}
|
||||
|
||||
private function addFetchQueryByPersonACL(FetchQuery $fetchQuery, Person $person): FetchQuery
|
||||
{
|
||||
$personDocMetadata = $this->em->getClassMetadata(PersonDocument::class);
|
||||
|
||||
$reachableScopes = [];
|
||||
|
||||
foreach ($this->centerResolverManager->resolveCenters($person) as $center) {
|
||||
$reachableScopes = [
|
||||
...$reachableScopes,
|
||||
...$this->authorizationHelperForCurrentUser->getReachableScopes(PersonDocumentVoter::SEE, $center)
|
||||
];
|
||||
}
|
||||
|
||||
if ([] === $reachableScopes) {
|
||||
$fetchQuery->addWhereClause('FALSE = TRUE');
|
||||
|
||||
return $fetchQuery;
|
||||
}
|
||||
|
||||
$fetchQuery->addWhereClause(
|
||||
sprintf(
|
||||
'person_document.%s IN (%s)',
|
||||
$personDocMetadata->getSingleAssociationJoinColumnName('scope'),
|
||||
implode(', ', array_fill(0, count($reachableScopes), '?'))
|
||||
),
|
||||
array_map(static fn (Scope $s) => $s->getId(), $reachableScopes),
|
||||
array_fill(0, count($reachableScopes), Types::INTEGER)
|
||||
);
|
||||
|
||||
return $fetchQuery;
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\DocStoreBundle\Repository;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
|
||||
interface PersonDocumentACLAwareRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @deprecated use fetch query for listing and counting person documents
|
||||
*/
|
||||
public function countByPerson(Person $person): int;
|
||||
|
||||
/**
|
||||
* @deprecated use fetch query for listing and counting person documents
|
||||
*/
|
||||
public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array;
|
||||
|
||||
public function buildFetchQueryForPerson(
|
||||
Person $person,
|
||||
?\DateTimeImmutable $startDate = null,
|
||||
?\DateTimeImmutable $endDate = null,
|
||||
?string $content = null
|
||||
): FetchQueryInterface;
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\DocStoreBundle\Repository;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @template ObjectRepository<PersonDocument::class>
|
||||
*/
|
||||
readonly class PersonDocumentRepository implements ObjectRepository
|
||||
{
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager
|
||||
)
|
||||
{
|
||||
$this->repository = $this->entityManager->getRepository($this->getClassName());
|
||||
}
|
||||
|
||||
public function find($id): ?PersonDocument
|
||||
{
|
||||
return $this->repository->find($id);
|
||||
}
|
||||
|
||||
public function findAll()
|
||||
{
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null)
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria): ?PersonDocument
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return PersonDocument::class;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
{#
|
||||
* Copyright (C) 2018, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
||||
|
||||
{% set activeRouteKey = '' %}
|
||||
|
||||
{% import "@ChillDocStore/Macro/macro.html.twig" as m %}
|
||||
|
||||
{% block title %}
|
||||
{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_script_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ encore_entry_link_tags('mod_docgen_picktemplate') }}
|
||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="col-md-10 col-xxl">
|
||||
<h1>{{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}</h1>
|
||||
|
||||
{{ filter|chill_render_filter_order_helper }}
|
||||
|
||||
{% if documents|length == 0 %}
|
||||
<p class="chill-no-data-statement">{{ 'No documents'|trans }}</p>
|
||||
{% else %}
|
||||
<div class="flex-table chill-task-list">
|
||||
{% for document in documents %}
|
||||
{{ document|chill_generic_doc_render }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ chill_pagination(pagination) }}
|
||||
|
||||
<div data-docgen-template-picker="data-docgen-template-picker" data-entity-class="Chill\PersonBundle\Entity\Person" data-entity-id="{{ person.id }}"></div>
|
||||
|
||||
{% if is_granted('CHILL_PERSON_DOCUMENT_CREATE', person) %}
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li class="create">
|
||||
<a href="{{ path('person_document_new', {'person': person.id}) }}" class="btn btn-create">
|
||||
{{ 'Create new document' | trans }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@ -14,9 +14,12 @@ namespace Chill\DocStoreBundle\Tests\GenericDoc;
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQuery;
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocDTO;
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForPersonProviderInterface;
|
||||
use Chill\DocStoreBundle\GenericDoc\Manager;
|
||||
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@ -53,7 +56,8 @@ class ManagerTest extends KernelTestCase
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocProvider()],
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
@ -62,6 +66,27 @@ class ManagerTest extends KernelTestCase
|
||||
self::assertIsInt($nb);
|
||||
}
|
||||
|
||||
public function testCountByPerson(): void
|
||||
{
|
||||
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $person) {
|
||||
throw new \UnexpectedValueException("person found");
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
$nb = $manager->countDocForPerson($person);
|
||||
|
||||
self::assertIsInt($nb);
|
||||
}
|
||||
|
||||
public function testFindDocByAccompanyingPeriod(): void
|
||||
{
|
||||
$period = $this->em->createQuery('SELECT a FROM '.AccompanyingPeriod::class.' a')
|
||||
@ -73,7 +98,8 @@ class ManagerTest extends KernelTestCase
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocProvider()],
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
@ -81,14 +107,77 @@ class ManagerTest extends KernelTestCase
|
||||
self::assertInstanceOf(GenericDocDTO::class, $doc);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFindDocByPerson(): void
|
||||
{
|
||||
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $person) {
|
||||
throw new \UnexpectedValueException("person not found");
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
foreach ($manager->findDocForPerson($person) as $doc) {
|
||||
self::assertInstanceOf(GenericDocDTO::class, $doc);
|
||||
}
|
||||
}
|
||||
|
||||
public function testPlacesForPerson(): void
|
||||
{
|
||||
$person = $this->em->createQuery('SELECT a FROM '.Person::class.' a')
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $person) {
|
||||
throw new \UnexpectedValueException("person not found");
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
$places = $manager->placesForPerson($person);
|
||||
|
||||
self::assertEquals(['dummy_person_doc'], $places);
|
||||
}
|
||||
|
||||
public function testPlacesForAccompanyingPeriod(): void
|
||||
{
|
||||
$period = $this->em->createQuery('SELECT a FROM '.AccompanyingPeriod::class.' a')
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $period) {
|
||||
throw new \UnexpectedValueException("period not found");
|
||||
}
|
||||
|
||||
$manager = new Manager(
|
||||
[new SimpleGenericDocAccompanyingPeriodProvider()],
|
||||
[new SimpleGenericDocPersonProvider()],
|
||||
$this->connection,
|
||||
);
|
||||
|
||||
$places = $manager->placesForAccompanyingPeriod($period);
|
||||
|
||||
self::assertEquals(['accompanying_course_document_dummy'], $places);
|
||||
}
|
||||
}
|
||||
|
||||
final readonly class SimpleGenericDocProvider implements GenericDocForAccompanyingPeriodProviderInterface
|
||||
final readonly class SimpleGenericDocAccompanyingPeriodProvider implements GenericDocForAccompanyingPeriodProviderInterface
|
||||
{
|
||||
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||
{
|
||||
$query = new FetchQuery(
|
||||
'accompanying_course_document',
|
||||
'accompanying_course_document_dummy',
|
||||
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
|
||||
'd',
|
||||
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
|
||||
@ -104,3 +193,25 @@ final readonly class SimpleGenericDocProvider implements GenericDocForAccompanyi
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final readonly class SimpleGenericDocPersonProvider implements GenericDocForPersonProviderInterface
|
||||
{
|
||||
public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
|
||||
{
|
||||
$query = new FetchQuery(
|
||||
'dummy_person_doc',
|
||||
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
|
||||
'd',
|
||||
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
|
||||
);
|
||||
|
||||
$query->addWhereClause('d > ?', [new \DateTimeImmutable('2023-01-01')], [Types::DATE_IMMUTABLE]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function isAllowedForPerson(Person $person): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
<?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\DocStoreBundle\Tests\GenericDoc\Providers;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
|
||||
use Chill\DocStoreBundle\GenericDoc\Providers\PersonDocumentGenericDocProvider;
|
||||
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepositoryInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class PersonDocumentGenericDocProviderTest extends KernelTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private PersonDocumentACLAwareRepositoryInterface $personDocumentACLAwareRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
$this->personDocumentACLAwareRepository = self::$container->get(PersonDocumentACLAwareRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDataBuildFetchQueryForPerson
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function testBuildFetchQueryForPerson(?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?string $content): void
|
||||
{
|
||||
$security = $this->prophesize(Security::class);
|
||||
$person = $this->entityManager->createQuery('SELECT a FROM '.Person::class.' a')
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $person) {
|
||||
throw new \UnexpectedValueException("person found");
|
||||
}
|
||||
|
||||
$provider = new PersonDocumentGenericDocProvider(
|
||||
$security->reveal(),
|
||||
$this->personDocumentACLAwareRepository
|
||||
);
|
||||
|
||||
$query = $provider->buildFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||
|
||||
$nb = $this->entityManager->getConnection()
|
||||
->fetchOne("SELECT COUNT(*) AS nb FROM (${sql}) AS sq", $params, $types);
|
||||
|
||||
self::assertIsInt($nb, "Test that the query is syntactically correct");
|
||||
}
|
||||
|
||||
public function provideDataBuildFetchQueryForPerson(): iterable
|
||||
{
|
||||
yield [null, null, null];
|
||||
yield [new DateTimeImmutable('1 year ago'), null, null];
|
||||
yield [null, new DateTimeImmutable('1 year ago'), null];
|
||||
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), null];
|
||||
yield [null, null, 'test'];
|
||||
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), 'test'];
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?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\DocStoreBundle\Tests\Repository;
|
||||
|
||||
use Chill\DocStoreBundle\GenericDoc\FetchQueryToSqlBuilder;
|
||||
use Chill\DocStoreBundle\Repository\PersonDocumentACLAwareRepository;
|
||||
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
|
||||
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class PersonDocumentACLAwareRepositoryTest extends KernelTestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private ScopeRepositoryInterface $scopeRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
$this->scopeRepository = self::$container->get(ScopeRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDataBuildFetchQueryForPerson
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function testBuildFetchQueryForPerson(?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): void
|
||||
{
|
||||
$centerManager = $this->prophesize(CenterResolverManagerInterface::class);
|
||||
$centerManager->resolveCenters(Argument::type(Person::class))
|
||||
->willReturn([new Center()]);
|
||||
|
||||
$scopes = $this->scopeRepository->findAll();
|
||||
$authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class);
|
||||
$authorizationHelper->getReachableScopes(PersonDocumentVoter::SEE, Argument::any())->willReturn($scopes);
|
||||
|
||||
$repository = new PersonDocumentACLAwareRepository(
|
||||
$this->entityManager,
|
||||
$centerManager->reveal(),
|
||||
$authorizationHelper->reveal()
|
||||
);
|
||||
|
||||
$person = $this->entityManager->createQuery("SELECT p FROM " . Person::class . " p")
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $person) {
|
||||
throw new \RuntimeException("person not exists in database");
|
||||
}
|
||||
|
||||
$query = $repository->buildFetchQueryForPerson($person, $startDate, $endDate, $content);
|
||||
['sql' => $sql, 'params' => $params, 'types' => $types] = (new FetchQueryToSqlBuilder())->toSql($query);
|
||||
|
||||
$nb = $this->entityManager->getConnection()
|
||||
->fetchOne("SELECT COUNT(*) FROM ({$sql}) AS sq", $params, $types);
|
||||
|
||||
self::assertIsInt($nb, "test that the query could be executed");
|
||||
}
|
||||
|
||||
public function provideDataBuildFetchQueryForPerson(): iterable
|
||||
{
|
||||
yield [null, null, null];
|
||||
yield [new DateTimeImmutable('1 year ago'), null, null];
|
||||
yield [null, new DateTimeImmutable('1 year ago'), null];
|
||||
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), null];
|
||||
yield [null, null, 'test'];
|
||||
yield [new DateTimeImmutable('2 years ago'), new DateTimeImmutable('1 year ago'), 'test'];
|
||||
}
|
||||
|
||||
}
|
@ -50,6 +50,7 @@ services:
|
||||
autoconfigure: true
|
||||
arguments:
|
||||
$providersForAccompanyingPeriod: !tagged_iterator chill_doc_store.generic_doc_accompanying_period_provider
|
||||
$providersForPerson: !tagged_iterator chill_doc_store.generic_doc_person_provider
|
||||
|
||||
Chill\DocStoreBundle\GenericDoc\Twig\GenericDocExtension:
|
||||
autoconfigure: true
|
||||
|
Loading…
x
Reference in New Issue
Block a user