Passed
Push — master ( 33feb5...3937cf )
by Michael
07:47
created

JsonApiExtension::createRoutesDefinition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
crap 2
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Bundle\JsonApiBundle\DependencyInjection;
5
6
use Symfony\Component\Config\FileLocator;
7
use Symfony\Component\Config\Loader\LoaderInterface;
8
use Symfony\Component\DependencyInjection\Definition;
9
use Symfony\Component\DependencyInjection\DefinitionDecorator;
10
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
11
use Symfony\Component\DependencyInjection\Reference;
12
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
13
use Symfony\Component\DependencyInjection\ContainerBuilder;
14
15
class JsonApiExtension extends Extension
16
{
17
    const ALIAS = 'mrtn_json_api';
18
19
    /**
20
     * Configuration loader
21
     *
22
     * @var LoaderInterface
23
     */
24
    protected $loader;
25
26
    /**
27
     * JsonApiExtension constructor.
28
     *
29
     * @param LoaderInterface $loader
30
     */
31 6
    public function __construct(LoaderInterface $loader = null)
32
    {
33 6
        $this->loader = $loader;
34 6
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39 6
    public function load(array $configs, ContainerBuilder $container)
40
    {
41 6
        $configuration = new JsonApiConfiguration();
42 6
        $config = $this->processConfiguration($configuration, $configs);
43
44 6
        $loader = $this->loader ?? new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
45
46 6
        $loader->load('services.yml');
47 6
        $loader->load('hydrator.yml');
48 6
        $loader->load('mapper.yml');
49 6
        $loader->load('http_client.yml');
50
51 6
        $this->handleMappingDefinitionProvider($container);
52
53 6
        if (! empty($config['mappers'])) {
54 5
            $this->createMappers($config['mappers'], $container);
55
        }
56
57 6
        if (! empty($config['resource_clients'])) {
58 5
            $this->createResourceClients($config['resource_clients'], $container);
59
        }
60 6
    }
61
62
    /**
63
     * Handle mapping definition provider depends on environment
64
     *
65
     * @param ContainerBuilder $container
66
     */
67 6
    protected function handleMappingDefinitionProvider(ContainerBuilder $container)
68
    {
69 6
        $env = $container->getParameter('kernel.environment');
70
71 6
        if ($env === 'prod') {
72 3
            $container->setAlias(
73 3
                'mrtn_json_api.object_mapper.definition_provider',
74 3
                'mrtn_json_api.object_mapper.definition_provider.cached'
75
            );
76
77 3
            return;
78
        }
79
80 3
        $container->setAlias(
81 3
            'mrtn_json_api.object_mapper.definition_provider',
82 3
            'mrtn_json_api.object_mapper.definition_provider.annotation'
83
        );
84 3
    }
85
86
    /**
87
     * Create mappers
88
     *
89
     * @param array            $config
90
     * @param ContainerBuilder $container
91
     */
92 5
    protected function createMappers(array $config, ContainerBuilder $container)
93
    {
94 5
        $handlers = $this->findMappingHandlers($container);
95
96 5
        foreach ($config as $name => $mapperDefinition)
97
        {
98 5
            $mapper = new DefinitionDecorator('mrtn_json_api.object_mapper.abstract');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
99 5
            $mapper->addTag('mrtn_json_api.object_mapper', ['alias' => $name]);
100
101 5 View Code Duplication
            foreach ($mapperDefinition['handlers'] as $handlerName)
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...
102
            {
103 5
                if (! isset($handlers[$handlerName])) {
104
                    throw new \LogicException(sprintf('Mapping handler with name "%s" has not been registered as a service.', $handlerName));
105
                }
106
107 5
                $mapper->addMethodCall('addHandler', [
108 5
                    new Reference($handlers[$handlerName])
109
                ]);
110
            }
111
112 5
            $container->setDefinition('mrtn_json_api.object_mapper.' . $name, $mapper);
113
        }
114 5
    }
115
116
    /**
117
     * Find mapping handler registered in container
118
     *
119
     * @param  ContainerBuilder $container
120
     * @return array
121
     */
122 5
    protected function findMappingHandlers(ContainerBuilder $container): array
123
    {
124 5
        $handlers = $container->findTaggedServiceIds('mrtn_json_api.object_mapper.handler');
125
126 5
        $found = [];
127
128 5
        foreach ($handlers as $id => $tags)
129
        {
130 5
            foreach ($tags as $tag)
131
            {
132 5
                if (! isset($tag['alias'])) {
133
                    continue;
134
                }
135
136 5
                $found[$tag['alias']] = $id;
137
            }
138
        }
139
140 5
        return $found;
141
    }
142
143
    /**
144
     * Create resources-based clients
145
     *
146
     * @param array            $config
147
     * @param ContainerBuilder $container
148
     */
149 5
    protected function createResourceClients(array $config, ContainerBuilder $container)
150
    {
151 5
        $repositoryClass = $container->getParameter('mrtn_json_api.route_repository.class');
152 5
        $clientClass     = $container->getParameter('mrtn_json_api.resource_client.class');
153
154 5
        foreach ($config as $name => $definition)
155
        {
156 5
            $this->createEventDispatcherDecorator($container, $name);
157
158 5
            $routes = $this->createRoutesDefinition($definition['resources']);
159
160 5
            $repository = new Definition($repositoryClass, [$definition['base_url'], $routes]);
161 5
            $repository->setPublic(false);
162
163 5
            $client = new Definition($clientClass, [
164 5
                new Reference('mrtn_json_api.http_client.decorator.event_dispatcher.' . $name),
165 5
                new Reference('mrtn_json_api.route_repository.' . $name)
166
            ]);
167
168 5
            $container->setDefinition('mrtn_json_api.route_repository.' . $name, $repository);
169 5
            $container->setDefinition('mrtn_json_api.resource_client.' . $name, $client);
170
        }
171 5
    }
172
173
    /**
174
     * Create event dispatcher decorator for resource-based http-client
175
     *
176
     * @param ContainerBuilder $container
177
     * @param string           $name
178
     */
179 5
    protected function createEventDispatcherDecorator(ContainerBuilder $container, string $name)
180
    {
181 5
        $class = $container->getParameter('mrtn_json_api.http_client.decorator.event_dispatcher.class');
182
183 5
        $decorator = new Definition($class, [
184 5
            new Reference('mrtn_json_api.http_client'),
185 5
            new Reference('event_dispatcher'),
186 5
            sprintf('mrtn_json_api.resource_client.%s.request', $name),
187 5
            sprintf('mrtn_json_api.resource_client.%s.response', $name),
188 5
            sprintf('mrtn_json_api.resource_client.%s.exception', $name)
189
        ]);
190
191 5
        $decorator->setPublic(false);
192
193 5
        $container->setDefinition('mrtn_json_api.http_client.decorator.event_dispatcher.' . $name, $decorator);
194 5
    }
195
196
    /**
197
     * Create definition of routes for a route repository by a collection of endpoints
198
     *
199
     * @param  array $resources
200
     * @return array
201
     */
202 5
    protected function createRoutesDefinition(array $resources): array
203
    {
204 5
        $definition = [];
205
206 5
        foreach ($resources as $name => $resource)
207
        {
208 5
            $methods = array_keys($resource['methods']);
209 5
            $methods = array_map('strtoupper', $methods);
210
211 5
            $definition[$name] = [
212 5
                'path'    => trim($resource['path']),
213 5
                'methods' => array_map('trim', $methods)
214
            ];
215
        }
216
217 5
        return $definition;
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223 4
    public function getAlias()
224
    {
225 4
        return self::ALIAS;
226
    }
227
}