|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* components |
|
4
|
|
|
* |
|
5
|
|
|
* @author Wolfy-J |
|
6
|
|
|
*/ |
|
7
|
|
|
namespace Spiral\ORM; |
|
8
|
|
|
|
|
9
|
|
|
use Spiral\Core\Component; |
|
10
|
|
|
use Spiral\Core\Container\SingletonInterface; |
|
11
|
|
|
use Spiral\Core\FactoryInterface; |
|
12
|
|
|
use Spiral\Core\MemoryInterface; |
|
13
|
|
|
use Spiral\Database\DatabaseManager; |
|
14
|
|
|
use Spiral\Models\ActiveEntityInterface; |
|
15
|
|
|
use Spiral\ORM\Exceptions\ORMException; |
|
16
|
|
|
use Spiral\ORM\Exceptions\SchemaException; |
|
17
|
|
|
use Spiral\ORM\Schemas\SchemaBuilder; |
|
18
|
|
|
use Spiral\ORM\Schemas\SchemaLocator; |
|
19
|
|
|
|
|
20
|
|
|
class ORM extends Component implements ORMInterface, SingletonInterface |
|
21
|
|
|
{ |
|
22
|
|
|
/** |
|
23
|
|
|
* Memory section to store ORM schema. |
|
24
|
|
|
*/ |
|
25
|
|
|
const MEMORY = 'orm.schema'; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* Already created instantiators. |
|
29
|
|
|
* |
|
30
|
|
|
* @invisible |
|
31
|
|
|
* @var InstantiatorInterface[] |
|
32
|
|
|
*/ |
|
33
|
|
|
private $instantiators = []; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* ORM schema. |
|
37
|
|
|
* |
|
38
|
|
|
* @invisible |
|
39
|
|
|
* @var array |
|
40
|
|
|
*/ |
|
41
|
|
|
private $schema = []; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* @var DatabaseManager |
|
45
|
|
|
*/ |
|
46
|
|
|
protected $manager; |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* @var SchemaLocator |
|
50
|
|
|
*/ |
|
51
|
|
|
protected $locator; |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* @invisible |
|
55
|
|
|
* @var MemoryInterface |
|
56
|
|
|
*/ |
|
57
|
|
|
protected $memory; |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* @var FactoryInterface |
|
61
|
|
|
*/ |
|
62
|
|
|
protected $factory; |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* @param DatabaseManager $manager |
|
66
|
|
|
* @param SchemaLocator $locator |
|
67
|
|
|
* @param MemoryInterface $memory |
|
68
|
|
|
* @param FactoryInterface $factory |
|
69
|
|
|
*/ |
|
70
|
|
View Code Duplication |
public function __construct( |
|
|
|
|
|
|
71
|
|
|
DatabaseManager $manager, |
|
72
|
|
|
SchemaLocator $locator, |
|
73
|
|
|
MemoryInterface $memory, |
|
74
|
|
|
FactoryInterface $factory |
|
75
|
|
|
) { |
|
76
|
|
|
$this->manager = $manager; |
|
77
|
|
|
$this->locator = $locator; |
|
78
|
|
|
|
|
79
|
|
|
$this->memory = $memory; |
|
80
|
|
|
$this->factory = $factory; |
|
81
|
|
|
|
|
82
|
|
|
//Loading schema from memory (if any) |
|
83
|
|
|
$this->schema = $this->loadSchema(); |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* Create instance of ORM SchemaBuilder. |
|
88
|
|
|
* |
|
89
|
|
|
* @param bool $locate Set to true to automatically locate available records and record sources |
|
90
|
|
|
* sources in a project files (based on tokenizer scope). |
|
91
|
|
|
* |
|
92
|
|
|
* @return SchemaBuilder |
|
93
|
|
|
* |
|
94
|
|
|
* @throws SchemaException |
|
95
|
|
|
*/ |
|
96
|
|
View Code Duplication |
public function schemaBuilder(bool $locate = true): SchemaBuilder |
|
|
|
|
|
|
97
|
|
|
{ |
|
98
|
|
|
/** |
|
99
|
|
|
* @var SchemaBuilder $builder |
|
100
|
|
|
*/ |
|
101
|
|
|
$builder = $this->factory->make(SchemaBuilder::class, ['manager' => $this->manager]); |
|
102
|
|
|
|
|
103
|
|
|
if ($locate) { |
|
104
|
|
|
foreach ($this->locator->locateSchemas() as $schema) { |
|
105
|
|
|
$builder->addSchema($schema); |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
foreach ($this->locator->locateSources() as $class => $source) { |
|
109
|
|
|
$builder->addSource($class, $source); |
|
110
|
|
|
} |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
return $builder; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Specify behaviour schema for ORM to be used. |
|
118
|
|
|
* |
|
119
|
|
|
* @param SchemaBuilder $builder |
|
120
|
|
|
* @param bool $remember Set to true to remember packed schema in memory. |
|
121
|
|
|
*/ |
|
122
|
|
View Code Duplication |
public function setSchema(SchemaBuilder $builder, bool $remember = false) |
|
|
|
|
|
|
123
|
|
|
{ |
|
124
|
|
|
$this->schema = $builder->packSchema(); |
|
125
|
|
|
|
|
126
|
|
|
if ($remember) { |
|
127
|
|
|
$this->memory->saveData(static::MEMORY, $this->schema); |
|
128
|
|
|
} |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
/** |
|
132
|
|
|
* {@inheritdoc} |
|
133
|
|
|
*/ |
|
134
|
|
View Code Duplication |
public function define(string $class, int $property) |
|
|
|
|
|
|
135
|
|
|
{ |
|
136
|
|
|
if (empty($this->schema)) { |
|
137
|
|
|
//Update and remember |
|
138
|
|
|
$this->setSchema($this->schemaBuilder()->renderSchema(), true); |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
//Check value |
|
142
|
|
|
if (!isset($this->schema[$class])) { |
|
143
|
|
|
throw new ORMException("Undefined ORM schema item '{$class}', make sure schema is updated"); |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
if (!array_key_exists($property, $this->schema[$class])) { |
|
147
|
|
|
throw new ORMException("Undefined ORM schema property '{$class}'.'{$property}'"); |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
return $this->schema[$class][$property]; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
//other methods |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* {@inheritdoc} |
|
157
|
|
|
*/ |
|
158
|
|
|
public function instantiate( |
|
159
|
|
|
string $class, |
|
160
|
|
|
$fields = [], |
|
161
|
|
|
bool $filter = true, |
|
162
|
|
|
bool $cache = false |
|
163
|
|
|
): ActiveEntityInterface { |
|
164
|
|
|
//todo: cache |
|
165
|
|
|
return $this->instantiator($class)->instantiate($fields, $filter); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
//todo: __clone |
|
169
|
|
|
|
|
170
|
|
|
/** |
|
171
|
|
|
* Get object responsible for class instantiation. |
|
172
|
|
|
* |
|
173
|
|
|
* @param string $class |
|
174
|
|
|
* |
|
175
|
|
|
* @return InstantiatorInterface |
|
176
|
|
|
*/ |
|
177
|
|
View Code Duplication |
protected function instantiator(string $class): InstantiatorInterface |
|
|
|
|
|
|
178
|
|
|
{ |
|
179
|
|
|
if (isset($this->instantiators[$class])) { |
|
180
|
|
|
return $this->instantiators[$class]; |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
|
|
//Potential optimization |
|
184
|
|
|
$instantiator = $this->factory->make( |
|
185
|
|
|
$this->define($class, self::R_INSTANTIATOR), |
|
186
|
|
|
[ |
|
187
|
|
|
'class' => $class, |
|
188
|
|
|
'orm' => $this, |
|
189
|
|
|
'schema' => $this->define($class, self::R_SCHEMA) |
|
190
|
|
|
] |
|
191
|
|
|
); |
|
192
|
|
|
|
|
193
|
|
|
//Constructing instantiator and storing it in cache |
|
194
|
|
|
return $this->instantiators[$class] = $instantiator; |
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
/** |
|
198
|
|
|
* Load packed schema from memory. |
|
199
|
|
|
* |
|
200
|
|
|
* @return array |
|
201
|
|
|
*/ |
|
202
|
|
|
protected function loadSchema(): array |
|
203
|
|
|
{ |
|
204
|
|
|
return (array)$this->memory->loadData(static::MEMORY); |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.