Test Failed
Pull Request — feature/publishable (#31)
by Vincent
06:22
created

updateQueryBuilderForUnauthorizedUsers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Component Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentBundle\Extension;
15
16
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\ContextAwareQueryCollectionExtensionInterface;
17
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
18
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
19
use Doctrine\ORM\QueryBuilder;
20
use Silverback\ApiComponentBundle\Annotation\Publishable;
21
use Silverback\ApiComponentBundle\Publishable\PublishableHelper;
22
use Symfony\Component\HttpFoundation\RequestStack;
23
24
/**
25
 * @author Vincent Chalamon <[email protected]>
26
 */
27
final class PublishableExtension implements QueryItemExtensionInterface, ContextAwareQueryCollectionExtensionInterface
28
{
29
    private PublishableHelper $publishableHelper;
30
    private RequestStack $requestStack;
31
    private ?Publishable $configuration;
32
33
    public function __construct(PublishableHelper $publishableHelper, RequestStack $requestStack)
34
    {
35
        $this->publishableHelper = $publishableHelper;
36
        $this->requestStack = $requestStack;
37
    }
38
39
    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
40
    {
41
        $configuration = $this->getConfiguration($resourceClass);
42
        if (!$configuration || !($request = $this->requestStack->getCurrentRequest())) {
0 ignored issues
show
Unused Code introduced by
The assignment to $request is dead and can be removed.
Loading history...
43
            return;
44
        }
45
46
        if (!$this->isAllowed($context)) {
47
            // User has no access to draft object
48
            $this->updateQueryBuilderForUnauthorizedUsers($queryBuilder, $configuration);
49
50
            return;
51
        }
52
53
        // Reset queryBuilder to prevent an invalid DQL
54
        $queryBuilder->where('1 = 1');
55
56
        $alias = $queryBuilder->getRootAliases()[0];
57
        foreach ($identifiers as $identifier) {
58
            // (o.id = :id AND o.publishedAt IS NOT NULL AND o.publishedAt <= :currentTime)
59
            // OR ((o.publishedAt IS NULL OR o.publishedAt > :currentTime) AND o.publishedResource = :id)
60
            $queryBuilder->orWhere(
61
                $queryBuilder->expr()->andX(
62
                    $queryBuilder->expr()->eq("$alias.$identifier", ":id_$identifier"),
63
                    $queryBuilder->expr()->isNotNull("$alias.$configuration->fieldName"),
64
                    $queryBuilder->expr()->lte("$alias.$configuration->fieldName", ':currentTime'),
65
                ),
66
                $queryBuilder->expr()->andX(
67
                    $queryBuilder->expr()->orX(
68
                        $queryBuilder->expr()->isNull("$alias.$configuration->fieldName"),
69
                        $queryBuilder->expr()->gt("$alias.$configuration->fieldName", ':currentTime'),
70
                    ),
71
                    $queryBuilder->expr()->eq("$alias.$configuration->associationName", ":id_$identifier"),
72
                )
73
            )->setParameter('currentTime', new \DateTimeImmutable())
74
            ->setParameter("id_$identifier", $identifier);
75
        }
76
    }
77
78
    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = []): void
79
    {
80
        if (!$configuration = $this->getConfiguration($resourceClass)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $configuration is dead and can be removed.
Loading history...
81
            return;
82
        }
83
84
        $configuration = $this->getConfiguration($resourceClass);
85
        if (!$this->isAllowed($context)) {
86
            // User has no access to draft object
87
            $this->updateQueryBuilderForUnauthorizedUsers($queryBuilder, $configuration);
88
89
            return;
90
        }
91
92
        $alias = $queryBuilder->getRootAliases()[0];
93
        $publishedResourceAlias = $queryNameGenerator->generateJoinAlias($configuration->associationName);
94
        $queryBuilder->leftJoin("$alias.$configuration->associationName", $publishedResourceAlias);
95
96
        // (o.publishedAt IS NOT NULL AND o.publishedAt <= :currentTime) OR (o.publishedAt IS NULL OR o.publishedAt > :currentTime)
97
        $queryBuilder->orWhere(
98
            $queryBuilder->expr()->andX(
99
                $queryBuilder->expr()->isNotNull("$alias.$configuration->fieldName"),
100
                $queryBuilder->expr()->lte("$alias.$configuration->fieldName", ':currentTime'),
101
            ),
102
            $queryBuilder->expr()->orX(
103
                $queryBuilder->expr()->isNull("$alias.$configuration->fieldName"),
104
                $queryBuilder->expr()->gt("$alias.$configuration->fieldName", ':currentTime'),
105
            ),
106
        )->setParameter('currentTime', new \DateTimeImmutable());
107
    }
108
109
    private function isAllowed(array $context): bool
110
    {
111
        return $this->publishableHelper->isGranted() && false === ($context['filters']['published'] ?? false);
112
    }
113
114
    private function updateQueryBuilderForUnauthorizedUsers(QueryBuilder $queryBuilder, Publishable $configuration): void
115
    {
116
        $alias = $queryBuilder->getRootAliases()[0];
117
        $queryBuilder
118
            ->andWhere("$alias.$configuration->fieldName IS NOT NULL")
119
            ->andWhere("$alias.$configuration->fieldName >= :currentTime")
120
            ->setParameter('currentTime', new \DateTimeImmutable());
121
    }
122
123
    private function getConfiguration(string $resourceClass): ?Publishable
124
    {
125
        if (!$this->configuration && ($this->publishableHelper->isPublishable($resourceClass))) {
126
            $this->configuration = $this->publishableHelper->getConfiguration($resourceClass);
127
        }
128
129
        return $this->configuration;
130
    }
131
}
132