Completed
Pull Request — master (#21)
by Pavel
22:01
created

CrudsEntitiesConfigurator::normalize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
namespace ScayTrase\Api\Cruds\DependencyInjection;
4
5
use Doctrine\Common\Persistence\ObjectManager;
6
use Doctrine\ORM\EntityRepository;
7
use ScayTrase\Api\Cruds\Controller\CountController;
8
use ScayTrase\Api\Cruds\Controller\CreateController;
9
use ScayTrase\Api\Cruds\Controller\DeleteController;
10
use ScayTrase\Api\Cruds\Controller\ReadController;
11
use ScayTrase\Api\Cruds\Controller\SearchController;
12
use ScayTrase\Api\Cruds\Controller\UpdateController;
13
use ScayTrase\Api\Cruds\Criteria\NestedCriteriaConfigurator;
14
use ScayTrase\Api\Cruds\ReflectionConstructorFactory;
15
use Symfony\Component\DependencyInjection\ChildDefinition;
16
use Symfony\Component\DependencyInjection\DefinitionDecorator;
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
18
use Symfony\Component\DependencyInjection\Definition;
19
use Symfony\Component\DependencyInjection\Reference;
20
21
final class CrudsEntitiesConfigurator
22
{
23
    /** @var  ContainerBuilder */
24
    private $container;
25
26
    /**
27
     * CrudsEntitiesConfigurator constructor.
28
     *
29
     * @param ContainerBuilder $container
30
     */
31
    public function __construct(ContainerBuilder $container)
32
    {
33
        $this->container = $container;
34
    }
35
36
    public function processEntityConfiguration($name, $config)
37
    {
38
        $class = $config['class'];
39
        $actions = $config['actions'];
40
        $prefix = $config['prefix'];
41
        $manager = $config['manager'];
42
        $repository = $config['repository'];
43
        $mount = $config['mount'];
44
45 View Code Duplication
        if (null === $manager) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
46
            $manager = 'cruds.class_' . $class . '.object_manager';
47
            $managerDef = new Definition(ObjectManager::class);
48
            $managerDef->setPublic(false);
49
            $managerDef->setFactory([new Reference('doctrine'), 'getManagerForClass']);
50
            $managerDef->setArguments([$class]);
51
            $this->container->setDefinition($manager, $managerDef);
52
        }
53
        $manager = new Reference($this->filterReference($manager));
54
55 View Code Duplication
        if (null === $repository) {
56
            $repository = 'cruds.class_' . $class . '.entity_repository';
57
            $repositoryDef = new Definition(EntityRepository::class);
58
            $repositoryDef->setPublic(false);
59
            $repositoryDef->setFactory([$manager, 'getRepository']);
60
            $repositoryDef->setArguments([$class]);
61
            $this->container->setDefinition($repository, $repositoryDef);
62
        }
63
64
        $repository = new Reference($this->filterReference($repository));
65
66
        foreach ($actions as $action => $actionConfig) {
67
            if (!$actionConfig['enabled']) {
68
                continue;
69
            }
70
71
            $actionConfig['name'] = $name;
72
            $actionConfig['class'] = $class;
73
            $actionConfig['mount'] = $mount;
74
            $actionConfig['repository'] = $repository;
75
            $actionConfig['path'] = $prefix . $actionConfig['path'];
76
            $actionConfig['manager'] = $manager;
77
            $actionConfig['prefix'] = $prefix;
78
            $function = new \ReflectionMethod($this, 'register' . ucfirst($action) . 'Action');
79
            $args = [];
80
81
            foreach ($function->getParameters() as $parameter) {
82
                if (array_key_exists($parameter->getName(), $actionConfig)) {
83
                    $args[] = $actionConfig[$parameter->getName()];
84
                } else {
85
                    $args[] = $parameter->getDefaultValue();
86
                }
87
            }
88
            $function->invokeArgs($this, $args);
89
        }
90
    }
91
92
    public function registerCreateAction($mount, $name, $class, $factory, $processor, $path, $manager)
93
    {
94
        $actionName = 'create';
95
        $controllerId = $this->generateControllerId($name, $actionName);
96
97 View Code Duplication
        if (null === $factory) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
            $factory = $controllerId . '.entity_factory';
99
100
            if (class_exists(ChildDefinition::class)) {
101
                $factoryDef = new ChildDefinition('cruds.factory.reflection');
102
            } else {
103
                $factoryDef = new DefinitionDecorator('cruds.factory.reflection');
104
            }
105
106
            $factoryDef->setArguments([$class, []]);
107
            $factoryDef->setPublic(false);
108
            $this->container->setDefinition($factory, $factoryDef);
109
        }
110
111
        $factory = new Reference($this->filterReference($factory));
112
113
        if (null === $processor) {
114
            $processor = 'cruds.processor.property_access';
115
        }
116
117
        $processor = new Reference($this->filterReference($processor));
118
119
        $definition = new Definition(CreateController::class);
120
        $definition->setPublic(true);
121
        $definition->setArguments(
122
            [
123
                $processor,
124
                $manager,
125
                $factory,
126
                $this->getEvm(),
127
            ]
128
        );
129
        $definition->setPublic(true);
130
131
132
        $this->container->setDefinition($controllerId, $definition);
133
134
        $action = $controllerId . ':' . CreateController::ACTION;
135
        $this->registerRoute(
136
            $mount,
137
            $name,
138
            $actionName,
139
            $path,
140
            $action,
141
            ['POST'],
142
            ['class' => $class, 'arguments' => ['data']]
143
        );
144
    }
145
146
    public function registerReadAction($mount, $name, $path, $repository, $class)
147
    {
148
        $definition = new Definition(ReadController::class);
149
        $definition->setPublic(true);
150
        $definition->setArguments(
151
            [
152
                $repository,
153
                $this->getEvm(),
154
            ]
155
        );
156
157
        $actionName = 'read';
158
        $controllerId = $this->generateControllerId($name, $actionName);
159
        $this->container->setDefinition($controllerId, $definition);
160
161
        $action = $controllerId . ':' . ReadController::ACTION;
162
        $this->registerRoute(
163
            $mount,
164
            $name,
165
            $actionName,
166
            $path,
167
            $action,
168
            ['GET', 'POST'],
169
            ['class' => $class, 'arguments' => ['identifier']]
170
        );
171
    }
172
173
    public function registerUpdateAction($mount, $name, $path, $repository, $processor, $manager, $class)
174
    {
175
        if (null === $processor) {
176
            $processor = new Reference('cruds.processor.property_access');
177
        } else {
178
            $processor = new Reference($this->filterReference($processor));
179
        }
180
181
        $definition = new Definition(UpdateController::class);
182
        $definition->setPublic(true);
183
        $definition->setArguments(
184
            [
185
                $repository,
186
                $processor,
187
                $manager,
188
                $this->getEvm(),
189
            ]
190
        );
191
192
        $actionName = 'update';
193
        $controllerId = $this->generateControllerId($name, $actionName);
194
        $this->container->setDefinition($controllerId, $definition);
195
196
        $action = $controllerId . ':' . UpdateController::ACTION;
197
        $this->registerRoute(
198
            $mount,
199
            $name,
200
            $actionName,
201
            $path,
202
            $action,
203
            ['POST', 'PATCH'],
204
            ['class' => $class, 'arguments' => ['identifier', 'data']]
205
        );
206
    }
207
208
    public function registerDeleteAction($mount, $name, $path, $repository, $manager, $class)
209
    {
210
        $definition = new Definition(DeleteController::class);
211
        $definition->setPublic(true);
212
        $definition->setPublic(true);
213
        $definition->setArguments(
214
            [
215
                $repository,
216
                $manager,
217
                $this->getEvm(),
218
            ]
219
        );
220
221
        $actionName = 'delete';
222
        $controllerId = $controllerId = $this->generateControllerId($name, $actionName);
223
        $this->container->setDefinition($controllerId, $definition);
224
225
        $action = $controllerId . ':' . DeleteController::ACTION;
226
        $definition->setPublic(true);
227
        $this->registerRoute(
228
            $mount,
229
            $name,
230
            $actionName,
231
            $path,
232
            $action,
233
            ['POST', 'DELETE'],
234
            ['class' => $class, 'arguments' => ['identifier']]
235
        );
236
    }
237
238
    public function registerSearchAction(
239
        string $mount,
240
        string $name,
241
        string $path,
242
        string $class,
243
        Reference $repository,
244
        $criteria,
245
        string $count_path,
246
        string $prefix
247
    ) {
248
249
        if (is_array($criteria)) {
250
            $filterArray = [];
251
            foreach ($criteria as $filter => $reference) {
252
                $filterArray[$filter] = new Reference($this->filterReference($reference));
253
            }
254
            $criteriaConfigurator = new Definition(NestedCriteriaConfigurator::class);
255
            $criteriaConfigurator->setArguments([$filterArray]);
256
        } else {
257
            $criteriaConfigurator = new Reference($this->filterReference($criteria));
258
        }
259
260
        $definition = new Definition(SearchController::class);
261
        $definition->setPublic(true);
262
        $definition->setArguments(
263
            [
264
                $class,
265
                $repository,
266
                $criteriaConfigurator,
267
                $this->getEvm(),
268
            ]
269
        );
270
271
        $actionName = 'search';
272
        $controllerId = $this->generateControllerId($name, $actionName);
273
        $this->container->setDefinition($controllerId, $definition);
274
275
        $action = $controllerId . ':' . SearchController::ACTION;
276
        $this->registerRoute(
277
            $mount,
278
            $name,
279
            $actionName,
280
            $path,
281
            $action,
282
            ['GET', 'POST'],
283
            ['class' => $class, 'arguments' => ['criteria', 'order', 'limit', 'offset']]
284
        );
285
286
        $definition = new Definition(CountController::class);
287
        $definition->setPublic(true);
288
        $definition->setArguments(
289
            [
290
                $class,
291
                $repository,
292
                $criteriaConfigurator,
293
                $this->getEvm(),
294
            ]
295
        );
296
297
        $actionName = 'count';
298
        $controllerId = $this->generateControllerId($name, $actionName);
299
        $this->container->setDefinition($controllerId, $definition);
300
301
        $action = $controllerId . ':' . CountController::ACTION;
302
        $this->registerRoute(
303
            $mount,
304
            $name,
305
            $actionName,
306
            $prefix . $count_path,
307
            $action,
308
            ['GET', 'POST'],
309
            ['class' => $class, 'arguments' => ['criteria']]
310
        );
311
    }
312
313
    private function filterReference(string $reference): string
314
    {
315
        return ltrim($reference, '@');
316
    }
317
318
    /**
319
     * @return Reference
320
     */
321
    private function getEvm(): Reference
322
    {
323
        return new Reference('event_dispatcher');
324
    }
325
326
    private function generateControllerId(string $name, string $actionName): string
327
    {
328
        return $this->normalize('cruds.generated_controller.' . $name . '.' . $actionName);
329
    }
330
331
    /**
332
     * @param string $name
333
     *
334
     * @return string
335
     */
336
    private function normalize(string $name): string
337
    {
338
        return str_replace('-', '_', $name);
339
    }
340
341
    /**
342
     * @param string $mount
343
     * @param string $name
344
     * @param string $actionName
345
     * @param string $path
346
     * @param string $action
347
     * @param array $methods
348
     * @param array $options
349
     *
350
     * @return Definition
351
     * @throws \InvalidArgumentException
352
     */
353
    private function registerRoute(
354
        string $mount,
355
        string $name,
356
        string $actionName,
357
        string $path,
358
        string $action,
359
        array $methods,
360
        array $options = []
361
    ): Definition {
362
        return $this->getLoaderDefinition()->addMethodCall(
363
            'addRoute',
364
            [
365
                $mount,
366
                $this->normalize('cruds.routing.' . $name . '.' . $actionName),
367
                $path,
368
                $action,
369
                $methods,
370
                array_replace(
371
                    [
372
                        'action' => $actionName,
373
                        'mount' => $mount,
374
                    ],
375
                    $options
376
                ),
377
            ]
378
        );
379
    }
380
381
    private function getLoaderDefinition(): Definition
382
    {
383
        return $this->container->getDefinition('cruds.api.router_loader');
384
    }
385
}
386