Completed
Branch feature/split-orm (60a911)
by Anton
03:15
created

DocumentSchema::getCollection()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 0
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM\Schemas;
8
9
use Doctrine\Common\Inflector\Inflector;
10
use Spiral\Models\Reflections\ReflectionEntity;
11
use Spiral\ODM\Configs\MutatorsConfig;
12
use Spiral\ODM\Document;
13
use Spiral\ODM\DocumentEntity;
14
use Spiral\ODM\Entities\DocumentInstantiator;
15
use Spiral\ODM\Exceptions\SchemaException;
16
17
class DocumentSchema implements SchemaInterface
18
{
19
    /**
20
     * @var ReflectionEntity
21
     */
22
    private $reflection;
23
24
    /**
25
     * @var MutatorsConfig
26
     */
27
    private $mutators;
28
29
    /**
30
     * @param ReflectionEntity $reflection
31
     */
32
    public function __construct(ReflectionEntity $reflection, MutatorsConfig $config)
33
    {
34
        $this->reflection = $reflection;
35
        $this->mutators = $config;
36
    }
37
38
    /**
39
     * @return string
40
     */
41
    public function getClass(): string
42
    {
43
        return $this->reflection->getName();
44
    }
45
46
    /**
47
     * @return ReflectionEntity
48
     */
49
    public function getReflection(): ReflectionEntity
50
    {
51
        return $this->reflection;
52
    }
53
54
    /**
55
     * @return string
56
     */
57
    public function getInstantiator(): string
58
    {
59
        return $this->reflection->getConstant('INSTANTIATOR') ?? DocumentInstantiator::class;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function isEmbedded(): bool
66
    {
67
        return !$this->reflection->isSubclassOf(Document::class)
68
            && $this->reflection->isSubclassOf(DocumentEntity::class);
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function getDatabase()
75
    {
76
        if ($this->isEmbedded()) {
77
            throw new SchemaException(
78
                "Unable to get database name for embedded model {$this->reflection}"
79
            );
80
        }
81
82
        $database = $this->reflection->getConstant('DATABASE');
83
        if (empty($database)) {
84
            //Empty database to be used
85
            return null;
86
        }
87
88
        return $database;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function getCollection(): string
95
    {
96
        if ($this->isEmbedded()) {
97
            throw new SchemaException(
98
                "Unable to get collection name for embedded model {$this->reflection}"
99
            );
100
        }
101
102
        $collection = $this->reflection->getConstant('COLLECTION');
103
        if (empty($collection)) {
104
            //Generate collection using short class name
105
            $collection = Inflector::camelize($this->reflection->getShortName());
106
            $collection = Inflector::pluralize($collection);
107
        }
108
109
        return $collection;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function getIndexes(): array
116
    {
117
        //todo: create indexes (keep collections)
118
        return [];
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function packSchema(SchemaBuilder $builder): array
125
    {
126
        return [
127
            //Instantion options and behaviour (if any)
128
            DocumentEntity::SH_INSTANTIATION => $this->instantiationOptions($builder),
129
130
            //Default entity state (builder is needed to resolve recursive defaults)
131
            DocumentEntity::SH_DEFAULTS      => $this->buildDefaults($builder),
132
133
            //Entity behaviour
134
            DocumentEntity::SH_HIDDEN        => $this->reflection->getHidden(),
135
            DocumentEntity::SH_SECURED       => $this->reflection->getSecured(),
136
            DocumentEntity::SH_FILLABLE      => $this->reflection->getFillable(),
137
138
            //Mutators can be altered based on ODM\SchemasConfig
139
            DocumentEntity::SH_MUTATORS      => $this->buildMutators(),
140
141
            //Document behaviours (we can mix them with accessors due potential inheritance)
142
            DocumentEntity::SH_COMPOSITIONS  => $this->buildCompositions($builder),
143
            DocumentEntity::SH_AGGREGATIONS  => $this->buildAggregations($builder),
144
        ];
145
    }
146
147
    /**
148
     * Define instantiator specific options (usually needed to resolve class inheritance). Might
149
     * return null if associated instantiator is unknown to DocumentSchema.
150
     *
151
     * @param SchemaBuilder $builder
152
     *
153
     * @return mixed
154
     */
155
    protected function instantiationOptions(SchemaBuilder $builder)
156
    {
157
        if ($this->getInstantiator() != DocumentInstantiator::class) {
158
            //Unable to define options for non default inheritance based instantiator
159
            return null;
160
        }
161
162
        //Let's define a way how to separate one model from another based on given fields
163
        $helper = new InheritanceDefinition($this, $builder->getSchemas());
164
165
        return $helper->makeDefinition();
166
    }
167
168
    /**
169
     * Entity default values.
170
     *
171
     * @param SchemaBuilder $builder
172
     *
173
     * @return array
174
     */
175
    protected function buildDefaults(SchemaBuilder $builder): array
0 ignored issues
show
Unused Code introduced by
The parameter $builder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
176
    {
177
        //User defined default values
178
        $userDefined = $this->reflection->getProperty('defaults');
179
180
        //We need mutators to normalize default values
181
        $mutators = $this->buildMutators();
182
183
        $defaults = [];
184
        foreach ($this->getFields() as $field => $type) {
185
            $default = is_array($type) ? [] : null;
186
187
            if (array_key_exists($field, $userDefined)) {
188
                //No merge to keep fields order intact
189
                $default = $userDefined[$field];
190
            }
191
192
            if (array_key_exists($field, $defaults)) {
193
                //Default value declared in model schema
194
                $default = $defaults[$field];
195
            }
196
197
            //Let's process default value using associated setter
198
            if (isset($mutators[DocumentEntity::MUTATOR_SETTER][$field])) {
199
                try {
200
                    $setter = $mutators[DocumentEntity::MUTATOR_SETTER][$field];
201
                    $default = call_user_func($setter, $default);
202
                } catch (\ErrorException $exception) {
0 ignored issues
show
Bug introduced by
The class ErrorException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
203
                    //Unable to generate default value, use null or empty array as fallback
204
                }
205
            }
206
207
            //todo: default accessors
208
            //if (isset($accessors[$field])) {
1 ignored issue
show
Unused Code Comprehensibility introduced by
85% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
209
            //    $default = $this->accessorDefaults($accessors[$field], $type, $default);
1 ignored issue
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
210
            //}
211
212
            //todo: compositions
213
            //Using composition to resolve default value
214
            //if (!empty($this->getCompositions()[$field])) {
1 ignored issue
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
215
            //    $default = $this->compositionDefaults($field, $default);
1 ignored issue
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
216
            //}
217
218
            //Registering default values
219
            $defaults[$field] = $default;
220
        }
221
222
        return $defaults;
223
    }
224
225
    /**
226
     * Generate set of mutators associated with entity fields using user defined and automatic
227
     * mutators.
228
     *
229
     * @see MutatorsConfig
230
     * @return array
231
     */
232
    protected function buildMutators(): array
233
    {
234
        $mutators = $this->reflection->getMutators();
235
236
        //Trying to resolve mutators based on field type
237
        foreach ($this->getFields() as $field => $type) {
238
            //Resolved mutators
239
            $resolved = [];
240
241
            if (
242
                is_array($type)
243
                && is_scalar($type[0])
244
                && $filter = $this->mutators->getMutators('array::' . $type[0])
245
            ) {
246
                //Mutator associated to array with specified type
247
                $resolved += $filter;
248
            } elseif (is_array($type) && $filter = $this->mutators->getMutators('array')) {
249
                //Default array mutator
250
                $resolved += $filter;
251
            } elseif (!is_array($type) && $filter = $this->mutators->getMutators($type)) {
252
                //Mutator associated with type directly
253
                $resolved += $filter;
254
            }
255
256
            //Merging mutators and default mutators
257
            foreach ($resolved as $mutator => $filter) {
258
                if (!array_key_exists($field, $mutators[$mutator])) {
259
                    $mutators[$mutator][$field] = $filter;
260
                }
261
            }
262
        }
263
264
        //Some mutators may be described using aliases (for shortness)
1 ignored issue
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
265
//        $mutators = $this->normalizeMutators($mutators);
266
//
267
//        //Every composition is counted as field accessor :)
268
//        foreach ($this->getCompositions() as $field => $composition) {
269
//            $mutators[AbstractEntity::MUTATOR_ACCESSOR][$field] = [
270
//                $composition['type'] == ODM::CMP_MANY ? Compositor::class : ODM::CMP_ONE,
271
//                $composition['class'],
272
//            ];
273
//        }
274
275
        return $mutators;
276
    }
277
278
    protected function buildCompositions(SchemaBuilder $builder): array
0 ignored issues
show
Unused Code introduced by
The parameter $builder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
279
    {
280
        return [];
281
    }
282
283
    protected function buildAggregations(SchemaBuilder $builder): array
0 ignored issues
show
Unused Code introduced by
The parameter $builder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
284
    {
285
286
        return [];
287
    }
288
289
    /**
290
     * Get every embedded entity field (excluding declarations of aggregations).
291
     *
292
     * @return array
293
     */
294
    protected function getFields(): array
295
    {
296
        return $this->reflection->getFields();
297
    }
298
}