Completed
Branch feature/split-orm (fa2f8e)
by Anton
03:35
created

ODM::schemaBuilder()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM;
8
9
use MongoDB\Collection;
10
use Spiral\Core\Component;
11
use Spiral\Core\Container\SingletonInterface;
12
use Spiral\Core\FactoryInterface;
13
use Spiral\Core\MemoryInterface;
14
use Spiral\ODM\Entities\DocumentSelector;
15
use Spiral\ODM\Entities\DocumentSource;
16
use Spiral\ODM\Exceptions\ODMException;
17
use Spiral\ODM\Schemas\SchemaBuilder;
18
use Spiral\ODM\Schemas\SchemaLocator;
19
20
/**
21
 * Provides supporting functionality for ODM classes such as selectors, instantiators and schema
22
 * builders.
23
 */
24
class ODM extends Component implements ODMInterface, SingletonInterface
25
{
26
    /**
27
     * Memory section to store ODM schema.
28
     */
29
    const MEMORY = 'odm.schema';
30
31
    /**
32
     * Already created instantiators.
33
     *
34
     * @invisible
35
     * @var InstantiatorInterface[]
36
     */
37
    private $instantiators = [];
38
39
    /**
40
     * ODM schema.
41
     *
42
     * @invisible
43
     * @var array
44
     */
45
    private $schema = [];
46
47
    /**
48
     * @var MongoManager
49
     */
50
    protected $manager;
51
52
    /**
53
     * @var SchemaLocator
54
     */
55
    protected $locator;
56
57
    /**
58
     * @invisible
59
     * @var MemoryInterface
60
     */
61
    protected $memory;
62
63
    /**
64
     * @var FactoryInterface
65
     */
66
    protected $factory;
67
68
    /**
69
     * ODM constructor.
70
     *
71
     * @param MongoManager     $manager
72
     * @param SchemaLocator    $locator
73
     * @param MemoryInterface  $memory
74
     * @param FactoryInterface $factory
75
     */
76
    public function __construct(
77
        MongoManager $manager,
78
        SchemaLocator $locator,
79
        MemoryInterface $memory,
80
        FactoryInterface $factory
81
    ) {
82
        $this->manager = $manager;
83
        $this->locator = $locator;
84
85
        $this->memory = $memory;
86
        $this->factory = $factory;
87
88
        //Loading schema from memory (if any)
89
        $this->schema = $this->loadSchema();
90
    }
91
92
    /**
93
     * Update ODM schema with automatic indexations.
94
     *
95
     * @param bool $locate Set to true to automatically locate available documents in a project
96
     *                     (based on tokenizer scope).
97
     *
98
     * @return SchemaBuilder
99
     */
100
    public function schemaBuilder(bool $locate = true): SchemaBuilder
101
    {
102
        $builder = $this->factory->make(SchemaBuilder::class, ['manager' => $this->manager]);
103
104
        if ($locate) {
105
            foreach ($this->locator->locateSchemas() as $schema) {
106
                $builder->addSchema($schema);
107
            }
108
        }
109
110
        return $builder;
111
    }
112
113
    /**
114
     * Specify behaviour schema for ODM to be used.
115
     *
116
     * @param SchemaBuilder $builder
117
     * @param bool          $remember Set to true to remember packed schema in memory.
118
     */
119
    public function setSchema(SchemaBuilder $builder, bool $remember = false)
120
    {
121
        $this->schema = $builder->packSchema();
122
123
        if ($remember) {
124
            $this->memory->saveData(static::MEMORY, $this->schema);
125
        }
126
    }
127
128
    /**
129
     * Get property from cached schema. Attention, ODM will automatically load schema if it's empty.
130
     *
131
     * Example:
132
     * $odm->getSchema(User::class, ODM::D_INSTANTIATOR);
133
     *
134
     * @param string $class
135
     * @param int    $property See ODM constants.
136
     *
137
     * @return mixed
138
     *
139
     * @throws ODMException
140
     */
141
    public function schema(string $class, int $property)
142
    {
143
        if (empty($this->schema)) {
144
            //Update and remember
145
            $this->setSchema($this->schemaBuilder(), true);
146
        }
147
148
        //Check value
149
        if (!isset($this->schema[$class])) {
150
            throw new ODMException("Undefined ODM schema item '{$class}', make sure schema is updated");
151
        }
152
153
        if (!array_key_exists($property, $this->schema[$class])) {
154
            throw new ODMException("Undefined ODM schema property '{$class}'.'{$property}'");
155
        }
156
157
        return $this->schema[$class][$property];
158
    }
159
160
    //---
161
162
    public function source(string $class): DocumentSource
0 ignored issues
show
Unused Code introduced by
The parameter $class 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...
163
    {
164
        //todo: implement
165
    }
166
167
168
    /**
169
     * Get DocumentSelector for a given class. Attention, due model inheritance selector WILL be
170
     * associated with parent class.
171
     *
172
     * Example:
173
     * Admin extends User
174
     * $odm->selector(Admin::class)->getClass() == User::class
175
     *
176
     * @param string $class
177
     *
178
     * @return DocumentSelector
179
     */
180
    public function selector(string $class): DocumentSelector
181
    {
182
        return new DocumentSelector(
183
            $this->collection($class),
184
            $this->schema($class, self::D_PRIMARY_CLASS),
185
            $this
186
        );
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function collection(string $class): Collection
193
    {
194
        return $this->manager->database(
195
            $this->schema($class, self::D_DATABASE)
196
        )->selectCollection(
197
            $this->schema($class, self::D_COLLECTION)
198
        );
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function instantiate(string $class, $fields = []): CompositableInterface
205
    {
206
        return $this->instantiator($class)->instantiate($fields);
207
    }
208
209
    /**
210
     * Get object responsible for class instantiation.
211
     *
212
     * @param string $class
213
     *
214
     * @return InstantiatorInterface
215
     */
216
    protected function instantiator(string $class): InstantiatorInterface
217
    {
218
        if (isset($this->instantiators[$class])) {
219
            return $this->instantiators[$class];
220
        }
221
222
        //Potential optimization
223
        $instantiator = $this->factory->make($this->schema($class, self::D_INSTANTIATOR), [
224
            'class'  => $class,
225
            'odm'    => $this,
226
            'schema' => $this->schema($class, self::D_SCHEMA)
227
        ]);
228
229
        //Constructing instantiator and storing it in cache
230
        return $this->instantiators[$class] = $instantiator;
231
    }
232
233
    /**
234
     * Load packed schema from memory.
235
     *
236
     * @return array
237
     */
238
    protected function loadSchema(): array
239
    {
240
        return (array)$this->memory->loadData(static::MEMORY);
241
    }
242
}