Completed
Push — master ( c6433c...a8c688 )
by Gerrit
38:24
created

GenericEntityCreateController   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 92.31%

Importance

Changes 0
Metric Value
dl 0
loc 183
c 0
b 0
f 0
wmc 13
lcom 1
cbo 5
ccs 60
cts 65
cp 0.9231
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 40 3
C createEntity() 0 92 10
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 *
8
 * @license GPL-3.0
9
 *
10
 * @author Gerrit Addiks <[email protected]>
11
 */
12
13
namespace Addiks\SymfonyGenerics\Controllers;
14
15
use Addiks\SymfonyGenerics\Controllers\ControllerHelperInterface;
16
use Addiks\SymfonyGenerics\Services\ArgumentCompilerInterface;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\Serializer\SerializerInterface;
20
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
21
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
22
use Webmozart\Assert\Assert;
23
use XSLTProcessor;
24
use DOMDocument;
25
use ReflectionClass;
26
use ReflectionMethod;
27
use ReflectionParameter;
28
use Psr\Container\ContainerInterface;
29
use ErrorException;
30
31
final class GenericEntityCreateController
32
{
33
34
    /**
35
     * @var ControllerHelperInterface
36
     */
37
    private $controllerHelper;
38
39
    /**
40
     * @var ContainerInterface
41
     */
42
    private $container;
43
44
    /**
45
     * @var string
46
     */
47
    private $entityClass;
48
49
    /**
50
     * @var array<string, array<string, mixed>>
51
     */
52
    private $calls = array();
53
54
    /**
55
     * @var string|null
56
     */
57
    private $factory = null;
58
59
    /**
60
     * @var array<string, mixed>|null
61
     */
62
    private $constructArguments;
63
64
    /**
65
     * @var ArgumentCompilerInterface
66
     */
67
    private $argumentBuilder;
68
69
    /**
70
     * @var string
71
     */
72
    private $successResponse;
73
74
    /**
75
     * @var string|null
76
     */
77
    private $authorizationAttribute;
78
79 16
    public function __construct(
80
        ControllerHelperInterface $controllerHelper,
81
        ArgumentCompilerInterface $argumentBuilder,
82
        ContainerInterface $container,
83
        array $options
84
    ) {
85 16
        Assert::null($this->controllerHelper);
86 16
        Assert::keyExists($options, 'entity-class');
87 15
        Assert::true(class_exists($options['entity-class']));
88
89 14
        $options = array_merge([
90 14
            'calls' => [],
91
            'success-response' => "object created",
92
            'factory' => null,
93
            'authorization-attribute' => null,
94 14
        ], $options);
95
96 14
        $this->controllerHelper = $controllerHelper;
97 14
        $this->argumentBuilder = $argumentBuilder;
98 14
        $this->container = $container;
99 14
        $this->entityClass = $options['entity-class'];
100 14
        $this->successResponse = $options['success-response'];
101 14
        $this->factory = $options['factory'];
102 14
        $this->authorizationAttribute = $options['authorization-attribute'];
103
104 14
        foreach ($options['calls'] as $methodName => $arguments) {
105
            /** @var array $arguments */
106
107 5
            Assert::isArray($arguments);
108
109 4
            if ($methodName === 'construct') {
110 1
                $this->constructArguments = $arguments;
111
112
            } else {
113 3
                Assert::true(method_exists($this->entityClass, $methodName));
114
115 2
                $this->calls[$methodName] = $arguments;
116
            }
117
        }
118 11
    }
119
120 10
    public function createEntity(Request $request): Response
121
    {
122 10
        $classReflection = new ReflectionClass($this->entityClass);
123
124
        /** @var ReflectionMethod $constructorReflection */
125 10
        $constructorReflection = $classReflection->getConstructor();
126
127
        /** @var array<int, mixed> $constructArguments */
128 10
        $constructArguments = array();
129
130 10
        if (!empty($this->constructArguments)) {
131 1
            $constructArguments = $this->argumentBuilder->buildCallArguments(
132 1
                $constructorReflection,
133 1
                $this->constructArguments,
134 1
                $request
135
            );
136
        }
137
138
        /** @var object|null $entity */
139 10
        $entity = null;
140
141 10
        if (!empty($this->factory)) {
142 7
            if (is_int(strpos($this->factory, '::'))) {
143 7
                [$factoryClass, $factoryMethod] = explode('::', $this->factory, 2);
0 ignored issues
show
Bug introduced by
The variable $factoryClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $factoryMethod does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
144
145 7
                if (!empty($factoryClass)) {
146 6
                    if ($factoryClass[0] == '@') {
147
                        /** @var string $factoryServiceId */
148 6
                        $factoryServiceId = substr($factoryClass, 1);
149
150
                        /** @var object|null $factoryObject */
151 6
                        $factoryObject = $this->container->get($factoryServiceId);
152
153 6
                        Assert::methodExists($factoryObject, $factoryMethod, sprintf(
154 6
                            "Did not find service with id '%s' that has a method '%s' to use as factory for '%s'!",
155 6
                            $factoryServiceId,
156 6
                            $factoryMethod,
157 6
                            $this->entityClass
158
                        ));
159
160
                        # Create by factory-service-object
161 3
                        $entity = call_user_func_array([$factoryObject, $factoryMethod], $constructArguments);
162
163
                    } else {
164
                        # Create by static factory-method of other class
165 4
                        $entity = call_user_func_array($this->factory, $constructArguments);
166
                    }
167
                }
168
169
            } elseif (method_exists($this->entityClass, $this->factory)) {
170
                # Create by static factory method on entity class
171
                $entity = call_user_func_array(
172
                    sprintf("%s::%s", $this->entityClass, $this->factory),
173
                    $constructArguments
174
                );
175
176
            } elseif (function_exists($this->factory)) {
177
                # Create by factory function
178 4
                $entity = call_user_func_array($this->factory, $constructArguments);
179
            }
180
181
        } else {
182
            # Create by calling the constructor directly
183 3
            $entity = $classReflection->newInstanceArgs($constructArguments);
184
        }
185
186 7
        Assert::isInstanceOf($entity, $this->entityClass);
187
188 5
        foreach ($this->calls as $methodName => $callArgumentConfiguration) {
189
            /** @var array $callArgumentConfiguration */
190
191
            /** @var ReflectionMethod $methodReflection */
192 1
            $methodReflection = $classReflection->getMethod($methodName);
193
194 1
            $callArguments = $this->argumentBuilder->buildCallArguments(
195 1
                $methodReflection,
196 1
                $callArgumentConfiguration,
197 1
                $request
198
            );
199
200 1
            $methodReflection->invoke($entity, $callArguments);
201
        }
202
203 5
        if (!empty($this->authorizationAttribute)) {
204 1
            $this->controllerHelper->denyAccessUnlessGranted($this->authorizationAttribute, $entity);
205
        }
206
207 4
        $this->controllerHelper->persistEntity($entity);
208 4
        $this->controllerHelper->flushORM();
209
210 4
        return new Response($this->successResponse, 200);
211
    }
212
213
}
214