Completed
Pull Request — master (#16)
by Arnaud
48:07 queued 29:02
created

ORMRequirementsProvider::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace LAG\SmokerBundle\Bridge\Doctrine\ORM\RequirementsProvider;
4
5
use LAG\SmokerBundle\Contracts\DataProvider\DataProviderInterface;
6
use LAG\SmokerBundle\Contracts\Requirements\Mapping\MappingResolverInterface;
7
use LAG\SmokerBundle\Contracts\Requirements\Provider\RequirementsProviderInterface;
8
use LAG\SmokerBundle\Exception\Exception;
9
use LAG\SmokerBundle\Exception\Url\UnsupportedUrlException;
10
use Symfony\Component\OptionsResolver\OptionsResolver;
11
use Symfony\Component\PropertyAccess\PropertyAccess;
12
use Symfony\Component\Routing\RouterInterface;
13
use Traversable;
14
15
class ORMRequirementsProvider implements RequirementsProviderInterface
16
{
17
    private $name = 'default';
18
19
    /**
20
     * @var RouterInterface
21
     */
22
    protected $router;
23
24
    /**
25
     * @var MappingResolverInterface
26
     */
27
    protected $mappingResolver;
28
29
    /**
30
     * @var DataProviderInterface
31
     */
32
    protected $dataProvider;
33
34
    /**
35
     * RequirementsProvider constructor.
36
     *
37
     * @param MappingResolverInterface $mappingResolver
38
     * @param RouterInterface          $router
39
     * @param DataProviderInterface    $dataProvider
40
     */
41
    public function __construct(
42
        MappingResolverInterface $mappingResolver,
43
        RouterInterface $router,
44
        DataProviderInterface $dataProvider
45
    ) {
46
        $this->mappingResolver = $mappingResolver;
47
        $this->router = $router;
48
        $this->dataProvider = $dataProvider;
49
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getName(): string
55
    {
56
        return $this->name;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function supports(string $routeName): bool
63
    {
64
        $mapping = $this->mappingResolver->resolve($routeName);
65
66
        if ([] === $mapping) {
67
            return false;
68
        }
69
70
        return $this->name === $mapping['provider'];
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public function getRequirementsData(string $routeName, array $options = []): Traversable
77
    {
78
        $options = $this->resolveOptions($options);
79
        $mapping = $this->mappingResolver->resolve($routeName);
80
81
        if ([] === $mapping) {
82
            throw new UnsupportedUrlException($routeName, $this->name);
83
        }
84
85
        if ($this->name !== $mapping['provider']) {
86
            throw new Exception('The provider "'.$this->name.'" does not support the route "'.$routeName.'"');
87
        }
88
89
        if (!key_exists('where', $mapping['options'])) {
90
            $mapping['options']['where'] = [];
91
        }
92
93
        if (!is_array($mapping['options']['where'])) {
94
            $mapping['options']['where'] = [
95
                $mapping['options']['where'],
96
            ];
97
        }
98
        // Allow optional dynamic criteria to find specific entities
99
        $mapping['options']['where'] = array_merge($mapping['options']['where'], $options['where']);
100
101
        $entities = $this
102
            ->dataProvider
103
            ->getData($mapping['entity'], $mapping['options'])
104
        ;
105
106
        foreach ($entities as $row) {
107
            $values = $this->processRow($row, $routeName, $mapping);
108
109
            yield $values;
110
        }
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function getRequirements(string $routeName): array
117
    {
118
        $route = $this
119
            ->router
120
            ->getRouteCollection()
121
            ->get($routeName)
122
        ;
123
        $requirements = $route->getRequirements();
124
125
        if (0 === count($requirements)) {
126
            $matches = [];
127
            preg_match_all('/{(.*?)}/', $route->getPath(), $matches);
128
129
            $requirements = array_flip($matches[1]);
130
        }
131
132
        return $requirements;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function getDataProvider(): DataProviderInterface
139
    {
140
        return $this->dataProvider;
141
    }
142
143
    /**
144
     * @param array  $row
145
     * @param string $routeName
146
     * @param array  $mapping
147
     *
148
     * @return array
149
     *
150
     * @throws Exception
151
     */
152
    private function processRow(array $row, string $routeName, array $mapping): array
153
    {
154
        $requirements = $this->getRequirements($routeName);
155
        $entity = $row[0];
156
        $values = [];
157
        $accessor = PropertyAccess::createPropertyAccessor();
158
159
        foreach ($requirements as $name => $requirement) {
160
            if (!key_exists($name, $mapping['requirements'])) {
161
                throw new Exception(sprintf(
162
                    'The requirement "%s" for the route "%s" is not provided',
163
                    $name,
164
                    $routeName
165
                ));
166
            }
167
            $property = $mapping['requirements'][$name];
168
169
            if ('@' === substr($property, 0, 1)) {
170
                $values[$name] = substr($property, 1);
171
            } else {
172
                $values[$name] = $accessor->getValue($entity, $property);
173
            }
174
        }
175
        $identifiers = $this->dataProvider->getIdentifier($mapping['entity']);
176
        $values['_identifiers'] = [];
177
178
        foreach ($identifiers as $identifier) {
179
            $values['_identifiers'][$identifier] = $accessor->getValue($entity, $identifier);
180
        }
181
182
        return $values;
183
    }
184
185
    private function resolveOptions(array $options): array
186
    {
187
        $resolver = new OptionsResolver();
188
        $resolver
189
            ->setDefaults([
190
                'where' => [],
191
            ])
192
            ->setAllowedTypes('where', 'array')
193
        ;
194
195
        return $resolver->resolve($options);
196
    }
197
}
198