Manager   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 16
eloc 36
c 4
b 0
f 0
dl 0
loc 272
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A setRelation() 0 18 3
A setType() 0 7 2
A __get() 0 14 3
A buildType() 0 15 3
A clear() 0 5 3
A getTypes() 0 3 1
1
<?php
2
/**
3
 *
4
 * This file is part of the Aura project for PHP.
5
 *
6
 * @package Aura.Marshal
7
 *
8
 * @license https://opensource.org/licenses/mit-license.php MIT
9
 *
10
 */
11
namespace Aura\Marshal;
12
13
use Aura\Marshal\Type\Builder as TypeBuilder;
14
use Aura\Marshal\Relation\Builder as RelationBuilder;
15
use Aura\Marshal\Type\GenericType;
16
17
/**
18
 *
19
 * A manager for the types in the domain model.
20
 *
21
 * @package Aura.Marshal
22
 *
23
 */
24
class Manager
25
{
26
    /**
27
     *
28
     * A builder for type objects.
29
     *
30
     * @var TypeBuilder
31
     *
32
     */
33
    protected $type_builder;
34
35
    /**
36
     *
37
     * A builder for relation objects.
38
     *
39
     * @var RelationBuilder
40
     *
41
     */
42
    protected $relation_builder;
43
44
    /**
45
     *
46
     * An array of type definition arrays, which are converted to type
47
     * objects as you request them.
48
     *
49
     * @var array<string, GenericType|array<string, mixed>>
50
     *
51
     */
52
    protected $types;
53
54
    /**
55
     *
56
     * Constructor.
57
     *
58
     * The $types definition array looks like this:
59
     *
60
     *      $types = [
61
     *
62
     *          // the name to use for this type in the manager
63
     *          'some_type_name' => [
64
     *
65
     *              // the identity field in each entity
66
     *              'identity_field' => 'id',
67
     *
68
     *              // fields to index against
69
     *              'index_fields' => ['field1', 'field2'],
70
     *
71
     *              // a entity builder for the type
72
     *              'entity_builder' => new \Aura\Domain\EntityBuilder,
73
     *
74
     *              // a collection builder for the type
75
     *              'collection_builder' => new \Aura\Domain\CollectionBuilder,
76
     *
77
     *              // relationship definitions
78
     *              'relation_names' => [
79
     *                  // discussed below
80
     *              ],
81
     *          ),
82
     *
83
     *          'next_type_name' => [
84
     *              // ...
85
     *          ],
86
     *
87
     *          // ...
88
     *      );
89
     *
90
     * The relationship definitions portion looks like this:
91
     *
92
     *      $relation_names = [
93
     *
94
     *          'name_for_relation_1' => [
95
     *
96
     *              // the relationship to the native (parent) type: the parent
97
     *              // belongs_to, has_one, has_many, or has_many_through
98
     *              // of the foreign type. required.
99
     *              'relationship' => 'has_many',
100
     *
101
     *              // the name of the foreign (related) type in the manager.
102
     *              // optional; by default, uses the relation name as the
103
     *              // foreign type.
104
     *              'foreign_type' => 'another_type_name',
105
     *
106
     *              // the name of the native (parent) entity field to use
107
     *              // when matching foreign (related) entities. required.
108
     *              'native_field' => 'native_field_name',
109
     *
110
     *              // the name of the foreign (related) entity field to use
111
     *              // when matching the native (parent) entity. required.
112
     *              'foreign_field' => 'foreign_field_name',
113
     *
114
     *              // -------------------------------------------------------
115
     *              // if you have a has_many_through relationship, add the
116
     *              // following three keys to set up the association mapping.
117
     *
118
     *              // the name of the type through which the native
119
     *              // and foreign types are mapped to each other.
120
     *              'through_type' => 'mapping_type_name',
121
     *
122
     *              // in the "through" entity, the name of the field that
123
     *              // maps to the 'native_field' value
124
     *              'through_native_field' => 'mapping_native_field_name',
125
     *
126
     *              // in the "through" entity, the name of the field that
127
     *              // maps to the 'foreign_field' value
128
     *              'through_foreign_field' => 'mapping_foreign_field_name',
129
     *          ),
130
     *
131
     *          'name_for_relation_2' => [
132
     *              // ...
133
     *          ],
134
     *
135
     *          // ...
136
     *      );
137
     *
138
     * @param TypeBuilder $type_builder A builder for type objects.
139
     *
140
     * @param RelationBuilder $relation_builder A builder for relation objects.
141
     *
142
     * @param array<string, array<string, mixed>> $types Type definitions.
143
     *
144
     */
145
    public function __construct(
146
        TypeBuilder $type_builder,
147
        RelationBuilder $relation_builder,
148
        array $types = []
149
    ) {
150
        $this->type_builder     = $type_builder;
151
        $this->relation_builder = $relation_builder;
152
        $this->types            = $types;
153
    }
154
155
    /**
156
     *
157
     * Sets one type in the manager.
158
     *
159
     * @param string $name The name to use for the type.
160
     *
161
     * @param array<string, mixed> $info An array of type definition information.
162
     *
163
     * @return void
164
     *
165
     */
166
    public function setType($name, array $info)
167
    {
168
        if (isset($this->types[$name])) {
169
            throw new Exception("Type '$name' is already in the manager.");
170
        }
171
172
        $this->types[$name] = $info;
173
    }
174
175
    /**
176
     *
177
     * Sets a one relation for a type in the manager.
178
     *
179
     * @param string $type The type to set the relation on.
180
     *
181
     * @param string $name The name for the relation.
182
     *
183
     * @param array<string, mixed> $info The relation information.
184
     *
185
     * @return void
186
     *
187
     */
188
    public function setRelation($type, $name, $info)
189
    {
190
        if (! isset($this->types[$type])) {
191
            throw new Exception("Type '$type' is not in the manager.");
192
        }
193
194
        if ($this->types[$type] instanceof GenericType) {
195
            // set on a type instance
196
            $relation = $this->relation_builder->newInstance(
197
                $type,
198
                $name,
199
                $info,
200
                $this
201
            );
202
            $this->types[$type]->setRelation($name, $relation);
203
        } else {
204
            // set the relation name on a type definition
205
            $this->types[$type]['relation_names'][$name] = $info;
206
        }
207
    }
208
209
    /**
210
     *
211
     * Gets a type by name, creating a type object for it as needed.
212
     *
213
     * @param string $name The type name to retrieve.
214
     *
215
     * @return GenericType
216
     *
217
     */
218
    public function __get($name)
219
    {
220
        if (! isset($this->types[$name])) {
221
            throw new Exception("Type '$name' not in the manager.");
222
        }
223
224
        if (! $this->types[$name] instanceof GenericType) {
225
            $this->buildType($name);
226
        }
227
228
        /** @var GenericType $type */
229
        $type = $this->types[$name];
230
231
        return $type;
232
    }
233
234
    /**
235
     *
236
     * Builds a type object from a type definition.
237
     *
238
     * The build process happens in two stages:
239
     *
240
     * 1. Use the $type_builder to create the type object.
241
     *
242
     * 2. Add relationships from the type definition.
243
     *
244
     * The two-stage process helps avoid race conditions where a type may
245
     * have relationships to other type object that might not be in the
246
     * manager yet.
247
     *
248
     * @param string $name The type name to build.
249
     *
250
     * @return void
251
     *
252
     */
253
    protected function buildType($name)
254
    {
255
        /**
256
         * Instantiate and retain the type object. if we don't do this before
257
         * building related fields, then we enter a race condition.
258
         * 
259
         * @var array<string, mixed> $info
260
         */
261
        $info = $this->types[$name];
262
        $this->types[$name] = $this->type_builder->newInstance($info);
263
264
        // add the related fields to the type
265
        if (isset($info['relation_names'])) {
266
            foreach ($info['relation_names'] as $relname => $relinfo) {
267
                $this->setRelation($name, $relname, $relinfo);
268
            }
269
        }
270
    }
271
272
    /**
273
     *
274
     * Returns the names of all types in the manager.
275
     *
276
     * @return string[]
277
     *
278
     */
279
    public function getTypes()
280
    {
281
        return array_keys($this->types);
282
    }
283
284
    /**
285
     *
286
     * Unsets all entities in all types in the manager.
287
     *
288
     * @return void
289
     *
290
     */
291
    public function clear()
292
    {
293
        foreach ($this->types as $type) {
294
            if ($type instanceof GenericType) {
295
                $type->clear();
296
            }
297
        }
298
    }
299
}
300