Completed
Branch feature/pre-split (a42d1e)
by Anton
03:22
created

ODM::define()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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