Issues (128)

src/DataProvider/PageDataProvider.php (2 issues)

1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components 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\ApiComponentsBundle\DataProvider;
15
16
use ApiPlatform\Metadata\ApiResource;
17
use ApiPlatform\Metadata\IriConverterInterface;
18
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
19
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
20
use Doctrine\Common\Collections\ArrayCollection;
21
use Doctrine\Common\Proxy\Proxy;
22
use Doctrine\ORM\EntityManager;
23
use Doctrine\Persistence\ManagerRegistry;
24
use Silverback\ApiComponentsBundle\Entity\Core\AbstractPageData;
25
use Silverback\ApiComponentsBundle\Metadata\PageDataComponentMetadata;
26
use Silverback\ApiComponentsBundle\Metadata\PageDataPropertyMetadata;
27
use Silverback\ApiComponentsBundle\Metadata\Provider\PageDataMetadataProvider;
28
use Silverback\ApiComponentsBundle\Repository\Core\RouteRepository;
29
use Symfony\Component\HttpFoundation\RequestStack;
30
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
31
32
/**
33
 * @author Daniel West <[email protected]>
34
 */
35
class PageDataProvider
36
{
37
    public function __construct(
38
        private readonly RequestStack $requestStack,
39
        private readonly RouteRepository $routeRepository,
40
        private readonly IriConverterInterface $iriConverter,
41
        private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory,
42
        private readonly PageDataMetadataProvider $pageDataMetadataProvider,
43
        private readonly ManagerRegistry $managerRegistry
44
    ) {
45
    }
46
47
    public function getOriginalRequestPath(): ?string
48
    {
49
        $request = $this->requestStack->getCurrentRequest();
50
        if (!$request) {
51
            return null;
52
        }
53
        $path = $request->headers->get('path');
54
        if (!$path) {
55
            throw new UnprocessableEntityHttpException('Could not find path header to retrieve page data');
56
        }
57
58
        return parse_url($path, \PHP_URL_PATH);
59
    }
60
61
    public function getPageData(): ?AbstractPageData
62
    {
63
        $path = $this->getOriginalRequestPath();
64
        if (!$path) {
65
            return null;
66
        }
67
68
        $route = $this->routeRepository->findOneByIdOrPath($path);
69
        if (!$route) {
70
            $object = $this->iriConverter->getResourceFromIri($path);
71
            if ($object instanceof AbstractPageData) {
72
                return $object;
73
            }
74
75
            return null;
76
        }
77
78
        return $route->getPageData();
79
    }
80
81
    public function findPageDataComponentMetadata(object $component): iterable
82
    {
83
        $resourceShortName = $this->getComponentShortName($component);
84
        if (!$resourceShortName) {
85
            return;
86
        }
87
        $pageDataLocations = $this->getPageDataLocations($resourceShortName);
88
        foreach ($pageDataLocations as $pageDataClassName => $properties) {
89
            if ($metadata = $this->findPageDataResourcesByPropertiesAndComponent($pageDataClassName, $properties, $component)) {
90
                yield $metadata;
91
            }
92
        }
93
    }
94
95
    public function findPageDataResourcesByPages(iterable $pages): array
96
    {
97
        $em = $this->managerRegistry->getManagerForClass(AbstractPageData::class);
98
        if (!$em instanceof EntityManager) {
99
            return [];
100
        }
101
        $qb = $em->createQueryBuilder();
102
        $expr = $qb->expr();
103
        $qb
104
            ->select('pd')
105
            ->from(AbstractPageData::class, 'pd');
106
        foreach ($pages as $x => $page) {
107
            $paramName = 'page_' . $x;
108
            $qb->setParameter($paramName, $page);
109
            $qb->orWhere($expr->eq('pd.page', ":$paramName"));
110
        }
111
112
        return $qb->getQuery()->getResult() ?: [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $qb->getQuery()->getResult() ?: array() could return the type integer which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
113
    }
114
115
    private function findPageDataResourcesByPropertiesAndComponent(string $pageDataClassName, ArrayCollection $properties, object $component): ?PageDataComponentMetadata
116
    {
117
        $em = $this->managerRegistry->getManagerForClass($pageDataClassName);
118
        if (!$em instanceof EntityManager) {
119
            return null;
120
        }
121
        $qb = $em->createQueryBuilder();
122
        $expr = $qb->expr();
123
        $qb
124
            ->select('pd')
125
            ->from($pageDataClassName, 'pd')
126
            ->setParameter('component', $component);
127
        foreach ($properties as $property) {
128
            $qb->orWhere($expr->eq('pd.' . $property, ':component'));
129
        }
130
131
        return new PageDataComponentMetadata($qb->getQuery()->getResult() ?: [], $properties);
0 ignored issues
show
It seems like $qb->getQuery()->getResult() ?: array() can also be of type integer; however, parameter $pageDataResources of Silverback\ApiComponents...Metadata::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

131
        return new PageDataComponentMetadata(/** @scrutinizer ignore-type */ $qb->getQuery()->getResult() ?: [], $properties);
Loading history...
132
    }
133
134
    private function getPageDataLocations(string $resourceShortName): array
135
    {
136
        $pageDataMetadatas = $this->pageDataMetadataProvider->createAll();
137
        $pageDataLocations = [];
138
        foreach ($pageDataMetadatas as $pageDataMetadata) {
139
            $resourceProperties = $pageDataMetadata->findPropertiesByComponentShortName($resourceShortName);
140
            if ($resourceProperties->count() > 0) {
141
                $pageDataLocations[$pageDataMetadata->getResourceClass()] = $resourceProperties->map(static function (PageDataPropertyMetadata $metadata) {
142
                    return $metadata->getProperty();
143
                });
144
            }
145
        }
146
147
        return $pageDataLocations;
148
    }
149
150
    private function getComponentShortName(object $component): ?string
151
    {
152
        $resourceClass = $component::class;
153
        if ($component instanceof Proxy) {
154
            $em = $this->managerRegistry->getManagerForClass($resourceClass);
155
            if (!$em) {
156
                return null;
157
            }
158
            if ($classMetadata = $em->getClassMetadata($resourceClass)) {
159
                $resourceClass = $classMetadata->getName();
160
            }
161
        }
162
163
        /** @var ResourceMetadataCollection $metadatas */
164
        $metadatas = $this->resourceMetadataFactory->create($resourceClass);
165
        /** @var ApiResource $metadata */
166
        foreach ($metadatas as $metadata) {
167
            if ($shortName = $metadata->getShortName()) {
168
                return $shortName;
169
            }
170
        }
171
172
        return null;
173
    }
174
}
175