Test Failed
Push — master ( 5db298...a758a4 )
by Daniel
04:14
created

ComponentUsageMetadataFactory   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 61
dl 0
loc 107
rs 10
c 1
b 0
f 0
wmc 17

5 Methods

Rating   Name   Duplication   Size   Complexity  
B getPageDataTotal() 0 33 8
A getPageDataLocations() 0 19 4
A __construct() 0 13 1
A findPageDataResourcesByPropertiesAndComponent() 0 17 3
A create() 0 8 1
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\Metadata\Factory;
15
16
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
17
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
18
use Doctrine\Common\Collections\ArrayCollection;
19
use Doctrine\Common\Proxy\Proxy;
20
use Doctrine\ORM\EntityManager;
21
use Doctrine\Persistence\ManagerRegistry;
22
use Silverback\ApiComponentsBundle\Entity\Core\ComponentInterface;
23
use Silverback\ApiComponentsBundle\Entity\Core\PageDataInterface;
24
use Silverback\ApiComponentsBundle\Metadata\ComponentUsageMetadata;
25
use Silverback\ApiComponentsBundle\Metadata\PageDataPropertyMetadata;
26
use Silverback\ApiComponentsBundle\Repository\Core\ComponentPositionRepository;
27
use Symfony\Component\PropertyAccess\PropertyAccess;
28
use Symfony\Component\PropertyAccess\PropertyAccessor;
29
30
/**
31
 * @author Daniel West <[email protected]>
32
 */
33
class ComponentUsageMetadataFactory
34
{
35
    private ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory;
36
    private ResourceMetadataFactoryInterface $resourceMetadataFactory;
37
    private PageDataMetadataFactoryInterface $pageDataMetadataFactory;
38
    private ComponentPositionRepository $componentPositionRepository;
39
    private ManagerRegistry $managerRegistry;
40
    private PropertyAccessor $propertyAccessor;
41
42
    public function __construct(
43
        ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory,
44
        ResourceMetadataFactoryInterface $resourceMetadataFactory,
45
        PageDataMetadataFactoryInterface $pageDataMetadataFactory,
46
        ComponentPositionRepository $componentPositionRepository,
47
        ManagerRegistry $managerRegistry
48
    ) {
49
        $this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
50
        $this->resourceMetadataFactory = $resourceMetadataFactory;
51
        $this->pageDataMetadataFactory = $pageDataMetadataFactory;
52
        $this->componentPositionRepository = $componentPositionRepository;
53
        $this->managerRegistry = $managerRegistry;
54
        $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
55
    }
56
57
    public function create(ComponentInterface $component): ComponentUsageMetadata
58
    {
59
        $componentPositions = $this->componentPositionRepository->findByComponent($component);
60
        $componentPositionCount = \count($componentPositions);
61
62
        $pageDataCount = $this->getPageDataTotal($component);
63
64
        return new ComponentUsageMetadata($componentPositionCount, $pageDataCount);
65
    }
66
67
    private function getPageDataTotal(ComponentInterface $component): ?int
68
    {
69
        // we want the SHORT NAME
70
        $resourceClass = \get_class($component);
71
        if ($component instanceof Proxy) {
72
            $em = $this->managerRegistry->getManagerForClass($resourceClass);
73
            if (!$em) {
74
                return null;
75
            }
76
            $resourceClass = $em->getClassMetadata($resourceClass)->getName();
77
        }
78
        $apiPlatformMetadata = $this->resourceMetadataFactory->create($resourceClass);
79
        $resourceShortName = $apiPlatformMetadata->getShortName();
80
81
        $pageDataLocations = $this->getPageDataLocations($resourceShortName);
0 ignored issues
show
Bug introduced by
It seems like $resourceShortName can also be of type null; however, parameter $resourceShortName of Silverback\ApiComponents...:getPageDataLocations() does only seem to accept string, 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

81
        $pageDataLocations = $this->getPageDataLocations(/** @scrutinizer ignore-type */ $resourceShortName);
Loading history...
82
        $pageDataCount = 0;
83
        foreach ($pageDataLocations as $pageDataClassName => $properties) {
84
            $pageDataResources = $this->findPageDataResourcesByPropertiesAndComponent($pageDataClassName, $properties, $component);
85
            if (!$pageDataResources) {
86
                continue;
87
            }
88
            $componentInDataCount = 0;
89
            foreach ($pageDataResources as $pageDataResource) {
90
                foreach ($properties as $property) {
91
                    if ($this->propertyAccessor->getValue($pageDataResource, $property) === $component) {
92
                        ++$componentInDataCount;
93
                    }
94
                }
95
            }
96
            $pageDataCount += $componentInDataCount;
97
        }
98
99
        return $pageDataCount;
100
    }
101
102
    private function findPageDataResourcesByPropertiesAndComponent(string $pageDataClassName, ArrayCollection $properties, ComponentInterface $component): ?array
103
    {
104
        $em = $this->managerRegistry->getManagerForClass($pageDataClassName);
105
        if (!$em instanceof EntityManager) {
106
            return null;
107
        }
108
        $qb = $em->createQueryBuilder();
109
        $expr = $qb->expr();
110
        $qb
111
            ->select('pd')
112
            ->from($pageDataClassName, 'pd')
113
            ->setParameter('component', $component);
114
        foreach ($properties as $property) {
115
            $qb->orWhere($expr->eq('pd.' . $property, ':component'));
116
        }
117
118
        return $qb->getQuery()->getResult();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $qb->getQuery()->getResult() could return the type integer which is incompatible with the type-hinted return array|null. Consider adding an additional type-check to rule them out.
Loading history...
119
    }
120
121
    private function getPageDataLocations(string $resourceShortName): array
122
    {
123
        $pageDataLocations = [];
124
        foreach ($this->resourceNameCollectionFactory->create() as $pageDataResourceClass) {
125
            $reflectionClass = new \ReflectionClass($pageDataResourceClass);
126
            if (!$reflectionClass->implementsInterface(PageDataInterface::class)) {
127
                continue;
128
            }
129
130
            $pageDataMetadata = $this->pageDataMetadataFactory->create($pageDataResourceClass);
131
            $resourceProperties = $pageDataMetadata->findPropertiesByComponentClass($resourceShortName);
132
            if ($resourceProperties->count() > 0) {
133
                $pageDataLocations[$pageDataResourceClass] = $resourceProperties->map(static function (PageDataPropertyMetadata $metadata) {
134
                    return $metadata->getProperty();
135
                });
136
            }
137
        }
138
139
        return $pageDataLocations;
140
    }
141
}
142