Completed
Branch feature/pre-split (220ee1)
by Anton
03:19
created

ORM::getFactory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ORM;
8
9
use Interop\Container\ContainerInterface;
10
use Spiral\Core\Component;
11
use Spiral\Core\Container;
12
use Spiral\Core\Container\SingletonInterface;
13
use Spiral\Core\FactoryInterface;
14
use Spiral\Core\MemoryInterface;
15
use Spiral\Core\NullMemory;
16
use Spiral\Database\DatabaseManager;
17
use Spiral\Debug\Traits\LoggerTrait;
18
use Spiral\Models\ActiveEntityInterface;
19
use Spiral\ORM\Exceptions\ORMException;
20
use Spiral\ORM\Exceptions\SchemaException;
21
use Spiral\ORM\Schemas\LocatorInterface;
22
use Spiral\ORM\Schemas\NullLocator;
23
use Spiral\ORM\Schemas\SchemaBuilder;
24
use Spiral\ORM\Schemas\SchemaLocator;
25
26
class ORM extends Component implements ORMInterface, SingletonInterface
27
{
28
    use LoggerTrait;
29
30
    /**
31
     * Memory section to store ORM schema.
32
     */
33
    const MEMORY = 'orm.schema';
34
35
    /**
36
     * Already created instantiators.
37
     *
38
     * @invisible
39
     * @var InstantiatorInterface[]
40
     */
41
    private $instantiators = [];
42
43
    /**
44
     * ORM schema.
45
     *
46
     * @invisible
47
     * @var array
48
     */
49
    private $schema = [];
50
51
    /**
52
     * @var DatabaseManager
53
     */
54
    protected $manager;
55
56
    /**
57
     * @var SchemaLocator
58
     */
59
    protected $locator;
60
61
    /**
62
     * @invisible
63
     * @var MemoryInterface
64
     */
65
    protected $memory;
66
67
    /**
68
     * Container defines working scope for all Documents and DocumentEntities.
69
     *
70
     * @var ContainerInterface
71
     */
72
    protected $container;
73
74
    /**
75
     * @param DatabaseManager    $manager
76
     * @param LocatorInterface   $locator
77
     * @param MemoryInterface    $memory
78
     * @param ContainerInterface $container
79
     */
80
    public function __construct(
81
        DatabaseManager $manager,
82
        LocatorInterface $locator = null,
83
        MemoryInterface $memory = null,
84
        ContainerInterface $container = null
85
    ) {
86
        $this->manager = $manager;
87
88
        $this->locator = $locator ?? new NullLocator();
0 ignored issues
show
Documentation Bug introduced by
$locator ?? new \Spiral\ORM\Schemas\NullLocator() is of type object<Spiral\ORM\Schemas\LocatorInterface>, but the property $locator was declared to be of type object<Spiral\ORM\Schemas\SchemaLocator>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
89
        $this->memory = $memory ?? new NullMemory();
90
        $this->container = $container ?? new Container();
91
92
        //Loading schema from memory (if any)
93
        $this->schema = $this->loadSchema();
94
    }
95
96
    /**
97
     * Create instance of ORM SchemaBuilder.
98
     *
99
     * @param bool $locate Set to true to automatically locate available records and record sources
100
     *                     sources in a project files (based on tokenizer scope).
101
     *
102
     * @return SchemaBuilder
103
     *
104
     * @throws SchemaException
105
     */
106
    public function schemaBuilder(bool $locate = true): SchemaBuilder
107
    {
108
        /**
109
         * @var SchemaBuilder $builder
110
         */
111
        $builder = $this->getFactory()->make(SchemaBuilder::class, ['manager' => $this->manager]);
112
113
        if ($locate) {
114
            foreach ($this->locator->locateSchemas() as $schema) {
115
                $builder->addSchema($schema);
116
            }
117
118
            foreach ($this->locator->locateSources() as $class => $source) {
119
                $builder->addSource($class, $source);
120
            }
121
        }
122
123
        return $builder;
124
    }
125
126
    /**
127
     * Specify behaviour schema for ORM to be used. Attention, you have to call renderSchema()
128
     * prior to passing builder into this method.
129
     *
130
     * @param SchemaBuilder $builder
131
     * @param bool          $remember Set to true to remember packed schema in memory.
132
     */
133
    public function buildSchema(SchemaBuilder $builder, bool $remember = false)
134
    {
135
        $this->schema = $builder->packSchema();
136
137
        if ($remember) {
138
            $this->memory->saveData(static::MEMORY, $this->schema);
139
        }
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function define(string $class, int $property)
146
    {
147
        if (empty($this->schema)) {
148
            $this->buildSchema($this->schemaBuilder()->renderSchema(), true);
149
        }
150
151
        //Check value
152
        if (!isset($this->schema[$class])) {
153
            throw new ORMException("Undefined ORM schema item '{$class}', make sure schema is updated");
154
        }
155
156
        if (!array_key_exists($property, $this->schema[$class])) {
157
            throw new ORMException("Undefined ORM schema property '{$class}'.'{$property}'");
158
        }
159
160
        return $this->schema[$class][$property];
161
    }
162
163
    //other methods
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function make(
169
        string $class,
170
        $fields = [],
171
        bool $filter = true,
172
        bool $cache = false
173
    ): ActiveEntityInterface {
174
        //todo: cache
175
        return $this->instantiator($class)->make($fields, $filter);
176
    }
177
178
    //todo: __clone
179
180
    /**
181
     * Get object responsible for class instantiation.
182
     *
183
     * @param string $class
184
     *
185
     * @return InstantiatorInterface
186
     */
187
    protected function instantiator(string $class): InstantiatorInterface
188
    {
189
        if (isset($this->instantiators[$class])) {
190
            return $this->instantiators[$class];
191
        }
192
193
        //Potential optimization
194
        $instantiator = $this->getFactory()->make(
195
            $this->define($class, self::R_INSTANTIATOR),
196
            [
197
                'class'  => $class,
198
                'orm'    => $this,
199
                'schema' => $this->define($class, self::R_SCHEMA)
200
            ]
201
        );
202
203
        //Constructing instantiator and storing it in cache
204
        return $this->instantiators[$class] = $instantiator;
205
    }
206
207
    /**
208
     * Load packed schema from memory.
209
     *
210
     * @return array
211
     */
212
    protected function loadSchema(): array
213
    {
214
        return (array)$this->memory->loadData(static::MEMORY);
215
    }
216
217
    /**
218
     * Get ODM specific factory.
219
     *
220
     * @return FactoryInterface
221
     */
222
    protected function getFactory(): FactoryInterface
223
    {
224
        if ($this->container instanceof FactoryInterface) {
225
            return $this->container;
226
        }
227
228
        return $this->container->get(FactoryInterface::class);
229
    }
230
}