Completed
Branch master (3a80e2)
by
unknown
02:59
created

Container::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php namespace Neomerx\JsonApi\Schema;
2
3
/**
4
 * Copyright 2015 [email protected] (www.neomerx.com)
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use \Closure;
20
use \InvalidArgumentException;
21
use \Psr\Log\LoggerAwareTrait;
22
use \Psr\Log\LoggerAwareInterface;
23
use \Neomerx\JsonApi\Factories\Exceptions;
24
use \Neomerx\JsonApi\I18n\Translator as T;
25
use \Neomerx\JsonApi\Contracts\Schema\ContainerInterface;
26
use \Neomerx\JsonApi\Contracts\Schema\SchemaFactoryInterface;
27
use \Neomerx\JsonApi\Contracts\Schema\SchemaProviderInterface;
28
29
/**
30
 * @package Neomerx\JsonApi
31
 */
32
class Container implements ContainerInterface, LoggerAwareInterface
33
{
34
    use LoggerAwareTrait;
35
36
    /**
37
     * @var array
38
     */
39
    private $providerMapping = [];
40
41
    /**
42
     * @var array
43
     */
44
    private $createdProviders = [];
45
46
    /**
47
     * @var array
48
     */
49
    private $resourceType2Type = [];
50
51
    /**
52
     * @var SchemaFactoryInterface
53
     */
54
    private $factory;
55
56
    /**
57
     * @param SchemaFactoryInterface $factory
58
     * @param array                  $schemas
59
     */
60 75
    public function __construct(SchemaFactoryInterface $factory, array $schemas = [])
61
    {
62 75
        $this->factory = $factory;
63 75
        $this->registerArray($schemas);
64 73
    }
65
66
    /**
67
     * Register provider for resource type.
68
     *
69
     * @param string         $type
70
     * @param string|Closure $schema
71
     *
72
     * @return void
73
     */
74 68
    public function register($type, $schema)
75
    {
76
        // Type must be non-empty string
77 68
        $isOk = (is_string($type) === true && empty($type) === false);
78 68
        if ($isOk === false) {
79 1
            throw new InvalidArgumentException(T::t('Type must be non-empty string.'));
80
        }
81
82 67
        $isOk = ((is_string($schema) === true && empty($schema) === false) || $schema instanceof Closure);
83 67
        if ($isOk === false) {
84 1
            throw new InvalidArgumentException(T::t(
85 1
                'Schema for type \'%s\' must be non-empty string or Closure.',
86 1
                [$type]
87 1
            ));
88
        }
89
90 66
        if (isset($this->providerMapping[$type]) === true) {
91 1
            throw new InvalidArgumentException(T::t(
92 1
                'Type should not be used more than once to register a schema (\'%s\').',
93 1
                [$type]
94 2
            ));
95
        }
96
97 66
        $this->providerMapping[$type] = $schema;
98 66
    }
99
100
    /**
101
     * Register providers for resource types.
102
     *
103
     * @param array $schemas
104
     *
105
     * @return void
106
     */
107 75
    public function registerArray(array $schemas)
108
    {
109 75
        foreach ($schemas as $type => $schema) {
110 68
            $this->register($type, $schema);
111 73
        }
112 73
    }
113
114
    /**
115
     * @inheritdoc
116
     */
117 57
    public function getSchema($resource)
118
    {
119 57
        $resourceType = $this->getResourceType($resource);
120
121 57
        return $this->getSchemaByType($resourceType);
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 59
    public function getSchemaByType($type)
128
    {
129 59
        is_string($type) === true ?: Exceptions::throwInvalidArgument('type', $type);
130
131 59
        if (isset($this->createdProviders[$type])) {
132 51
            return $this->createdProviders[$type];
133
        }
134
135 59
        if (isset($this->providerMapping[$type]) === false) {
136 2
            throw new InvalidArgumentException(T::t('Schema is not registered for type \'%s\'.', [$type]));
137
        }
138
139 59
        $classNameOrClosure = $this->providerMapping[$type];
140 59
        if ($classNameOrClosure instanceof Closure) {
141 27
            $this->createdProviders[$type] = ($schema = $this->createSchemaFromClosure($classNameOrClosure));
142 27
        } else {
143 42
            $this->createdProviders[$type] = ($schema = $this->createSchemaFromClassName($classNameOrClosure));
144
        }
145
146
        /** @var SchemaProviderInterface $schema */
147
148 59
        $this->resourceType2Type[$schema->getResourceType()] = $type;
149
150 59
        return $schema;
151
    }
152
153
    /**
154
     * @inheritdoc
155
     */
156 42
    public function getSchemaByResourceType($resourceType)
157
    {
158
        // Schema is not found among instantiated schemas for resource type $resourceType
159 42
        $isOk = (is_string($resourceType) === true && isset($this->resourceType2Type[$resourceType]) === true);
160
161
        // Schema might not be found if it hasn't been searched by type (not resource type) before.
162
        // We instantiate all schemas and then find one.
163 42
        if ($isOk === false) {
164 1
            foreach ($this->providerMapping as $type => $schema) {
165 1
                if (isset($this->createdProviders[$type]) === false) {
166
                    // it will instantiate the schema
167 1
                    $this->getSchemaByType($type);
168 1
                }
169 1
            }
170 1
        }
171
172
        // search one more time
173 42
        $isOk = (is_string($resourceType) === true && isset($this->resourceType2Type[$resourceType]) === true);
174
175 42
        if ($isOk === false) {
176 1
            throw new InvalidArgumentException(T::t(
177 1
                'Schema is not registered for resource type \'%s\'.',
178 1
                [$resourceType]
179 1
            ));
180
        }
181
182 42
        return $this->getSchemaByType($this->resourceType2Type[$resourceType]);
183
    }
184
185
    /**
186
     * @return SchemaFactoryInterface
187
     */
188 59
    protected function getFactory()
189
    {
190 59
        return $this->factory;
191
    }
192
193
    /**
194
     * @param object $resource
195
     *
196
     * @return string
197
     */
198 57
    protected function getResourceType($resource)
199
    {
200 57
        return get_class($resource);
201
    }
202
203
    /**
204
     * @param Closure $closure
205
     *
206
     * @return SchemaProviderInterface
207
     */
208 26
    protected function createSchemaFromClosure(Closure $closure)
209
    {
210 26
        $schema = $closure($this->getFactory());
211
212 26
        return $schema;
213
    }
214
215
    /**
216
     * @param string $className
217
     *
218
     * @return SchemaProviderInterface
219
     */
220 41
    protected function createSchemaFromClassName($className)
221
    {
222 41
        $schema = new $className($this->getFactory());
223
224 41
        return $schema;
225
    }
226
}
227