Completed
Branch master (195c43)
by
unknown
04:51
created

SchemaContainer::getFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Neomerx\JsonApi\Schema;
4
5
/**
6
 * Copyright 2015-2019 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Closure;
22
use Neomerx\JsonApi\Contracts\Factories\FactoryInterface;
23
use Neomerx\JsonApi\Contracts\Schema\SchemaContainerInterface;
24
use Neomerx\JsonApi\Contracts\Schema\SchemaInterface;
25
use Neomerx\JsonApi\Exceptions\InvalidArgumentException;
26
use function Neomerx\JsonApi\I18n\format as _;
27
28
/**
29
 * @package Neomerx\JsonApi
30
 */
31
class SchemaContainer implements SchemaContainerInterface
32
{
33
    /**
34
     * Message code.
35
     */
36
    const MSG_INVALID_MODEL_TYPE = 'Invalid model type.';
37
38
    /**
39
     * Message code.
40
     */
41
    const MSG_INVALID_SCHEME = 'Schema for type `%s` must be non-empty string, callable or SchemaInterface instance.';
42
43
    /**
44
     * Message code.
45
     */
46
    const MSG_TYPE_REUSE_FORBIDDEN = 'Type should not be used more than once to register a schema (`%s`).';
47
48
    /**
49
     * @var array
50
     */
51
    private $providerMapping = [];
52
53
    /**
54
     * @var SchemaInterface[]
55
     */
56
    private $createdProviders = [];
57
58
    /**
59
     * @var array
60
     */
61
    private $resType2JsonType = [];
62
63
    /**
64
     * @var FactoryInterface
65
     */
66
    private $factory;
67
68
    /**
69
     * @param FactoryInterface $factory
70
     * @param iterable         $schemas
71
     */
72 90
    public function __construct(FactoryInterface $factory, iterable $schemas)
73
    {
74 90
        $this->factory = $factory;
75 90
        $this->registerCollection($schemas);
76 88
    }
77
78
    /**
79
     * Register provider for resource type.
80
     *
81
     * @param string         $type
82
     * @param string|Closure $schema
83
     *
84
     * @return void
85
     *
86
     * @SuppressWarnings(PHPMD.StaticAccess)
87
     * @SuppressWarnings(PHPMD.ElseExpression)
88
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
89
     */
90 73
    public function register(string $type, $schema): void
91
    {
92 73
        if (empty($type) === true || \class_exists($type) === false) {
93 1
            throw new InvalidArgumentException(_(static::MSG_INVALID_MODEL_TYPE));
94
        }
95
96
        $isOk = (
97
            (
98 72
                \is_string($schema) === true &&
99 72
                empty($schema) === false &&
100 72
                \class_exists($schema) === true &&
101 46
                \in_array(SchemaInterface::class, \class_implements($schema)) === true
102
            ) ||
103 40
            \is_callable($schema) ||
104 72
            $schema instanceof SchemaInterface
105
        );
106 72
        if ($isOk === false) {
107 1
            throw new InvalidArgumentException(_(static::MSG_INVALID_SCHEME, $type));
108
        }
109
110 71
        if ($this->hasProviderMapping($type) === true) {
111 1
            throw new InvalidArgumentException(_(static::MSG_TYPE_REUSE_FORBIDDEN, $type));
112
        }
113
114 71
        if ($schema instanceof SchemaInterface) {
115 2
            $this->setProviderMapping($type, \get_class($schema));
116 2
            $this->setResourceToJsonTypeMapping($schema->getType(), $type);
117 2
            $this->setCreatedProvider($type, $schema);
118
        } else {
119 70
            $this->setProviderMapping($type, $schema);
120
        }
121 71
    }
122
123
    /**
124
     * Register providers for resource types.
125
     *
126
     * @param iterable $schemas
127
     *
128
     * @return void
129
     */
130 90
    public function registerCollection(iterable $schemas): void
131
    {
132 90
        foreach ($schemas as $type => $schema) {
133 72
            $this->register($type, $schema);
134
        }
135 88
    }
136
137
    /**
138
     * @inheritdoc
139
     */
140 63
    public function getSchema($resource): SchemaInterface
141
    {
142 63
        \assert($this->hasSchema($resource));
143
144 63
        $resourceType = $this->getResourceType($resource);
145
146 63
        return $this->getSchemaByType($resourceType);
147
    }
148
149
    /**
150
     * @inheritdoc
151
     */
152 72
    public function hasSchema($resourceObject): bool
153
    {
154 72
        return \is_object($resourceObject) === true &&
155 72
            $this->hasProviderMapping($this->getResourceType($resourceObject)) === true;
156
    }
157
158
    /**
159
     * @inheritdoc
160
     *
161
     * @SuppressWarnings(PHPMD.StaticAccess)
162
     * @SuppressWarnings(PHPMD.ElseExpression)
163
     */
164 63
    protected function getSchemaByType(string $type): SchemaInterface
165
    {
166 63
        if ($this->hasCreatedProvider($type) === true) {
167 31
            return $this->getCreatedProvider($type);
168
        }
169
170 62
        $classNameOrCallable = $this->getProviderMapping($type);
171 62
        if (\is_string($classNameOrCallable) === true) {
172 34
            $schema = $this->createSchemaFromClassName($classNameOrCallable);
173
        } else {
174 37
            \assert(\is_callable($classNameOrCallable) === true);
175 37
            $schema = $this->createSchemaFromCallable($classNameOrCallable);
176
        }
177 62
        $this->setCreatedProvider($type, $schema);
178
179
        /** @var SchemaInterface $schema */
180
181 62
        $this->setResourceToJsonTypeMapping($schema->getType(), $type);
182
183 62
        return $schema;
184
    }
185
186
    /**
187
     * @param string $type
188
     *
189
     * @return bool
190
     */
191 73
    protected function hasProviderMapping(string $type): bool
192
    {
193 73
        return isset($this->providerMapping[$type]);
194
    }
195
196
    /**
197
     * @param string $type
198
     *
199
     * @return mixed
200
     */
201 62
    protected function getProviderMapping(string $type)
202
    {
203 62
        return $this->providerMapping[$type];
204
    }
205
206
    /**
207
     * @param string         $type
208
     * @param string|Closure $schema
209
     *
210
     * @return void
211
     */
212 71
    protected function setProviderMapping(string $type, $schema): void
213
    {
214 71
        $this->providerMapping[$type] = $schema;
215 71
    }
216
217
    /**
218
     * @param string $type
219
     *
220
     * @return bool
221
     */
222 63
    protected function hasCreatedProvider(string $type): bool
223
    {
224 63
        return isset($this->createdProviders[$type]);
225
    }
226
227
    /**
228
     * @param string $type
229
     *
230
     * @return SchemaInterface
231
     */
232 31
    protected function getCreatedProvider(string $type): SchemaInterface
233
    {
234 31
        return $this->createdProviders[$type];
235
    }
236
237
    /**
238
     * @param string          $type
239
     * @param SchemaInterface $provider
240
     *
241
     * @return void
242
     */
243 63
    protected function setCreatedProvider(string $type, SchemaInterface $provider): void
244
    {
245 63
        $this->createdProviders[$type] = $provider;
246 63
    }
247
248
    /**
249
     * @param string $resourceType
250
     * @param string $jsonType
251
     *
252
     * @return void
253
     */
254 63
    protected function setResourceToJsonTypeMapping(string $resourceType, string $jsonType): void
255
    {
256 63
        $this->resType2JsonType[$resourceType] = $jsonType;
257 63
    }
258
259
    /**
260
     * @param object $resource
261
     *
262
     * @return string
263
     */
264 67
    protected function getResourceType($resource): string
265
    {
266 67
        \assert(
267 67
            \is_object($resource) === true,
268 67
            'Unable to get a type of the resource as it is not an object.'
269
        );
270
271 67
        return \get_class($resource);
272
    }
273
274
    /**
275
     * @param callable $callable
276
     *
277
     * @return SchemaInterface
278
     */
279 37
    protected function createSchemaFromCallable(callable $callable): SchemaInterface
280
    {
281 37
        $schema = \call_user_func($callable, $this->factory);
282
283 37
        return $schema;
284
    }
285
286
    /**
287
     * @param string $className
288
     *
289
     * @return SchemaInterface
290
     */
291 34
    protected function createSchemaFromClassName(string $className): SchemaInterface
292
    {
293 34
        $schema = new $className($this->factory);
294
295 34
        return $schema;
296
    }
297
}
298