1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* components |
4
|
|
|
* |
5
|
|
|
* @author Wolfy-J |
6
|
|
|
*/ |
7
|
|
|
namespace Spiral\ORM\Schemas; |
8
|
|
|
|
9
|
|
|
use Spiral\Core\FactoryInterface; |
10
|
|
|
use Spiral\ORM\Configs\RelationsConfig; |
11
|
|
|
use Spiral\ORM\Exceptions\DefinitionException; |
12
|
|
|
use Spiral\ORM\Schemas\Definitions\RelationDefinition; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Subsection of SchemaBuilder used to configure tables and columns defined by model to model |
16
|
|
|
* relations. |
17
|
|
|
*/ |
18
|
|
|
class RelationManager |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* @invisible |
22
|
|
|
* @var RelationsConfig |
23
|
|
|
*/ |
24
|
|
|
protected $config; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @invisible |
28
|
|
|
* @var FactoryInterface |
29
|
|
|
*/ |
30
|
|
|
protected $factory; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Set of relation definitions. |
34
|
|
|
* |
35
|
|
|
* @var RelationInterface[] |
36
|
|
|
*/ |
37
|
|
|
private $relations = []; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @param RelationsConfig $config |
41
|
|
|
* @param FactoryInterface $factory |
42
|
|
|
*/ |
43
|
|
|
public function __construct(RelationsConfig $config, FactoryInterface $factory) |
44
|
|
|
{ |
45
|
|
|
$this->config = $config; |
46
|
|
|
$this->factory = $factory; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Registering new relation definition. |
51
|
|
|
* |
52
|
|
|
* @param RelationDefinition $definition Relation options (definition). |
53
|
|
|
* |
54
|
|
|
* @throws DefinitionException |
55
|
|
|
*/ |
56
|
|
|
public function registerRelation(RelationDefinition $definition) |
57
|
|
|
{ |
58
|
|
|
if (!$this->config->hasRelation($definition->getType())) { |
59
|
|
|
throw new DefinitionException(sprintf( |
60
|
|
|
"Undefined relation type '%s' in '%s'.'%s'", |
61
|
|
|
$definition->getType(), |
62
|
|
|
$definition->sourceContext()->getClass(), |
63
|
|
|
$definition->getName() |
64
|
|
|
)); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
$class = $this->config->relationClass( |
68
|
|
|
$definition->getType(), |
69
|
|
|
RelationsConfig::SCHEMA_CLASS |
70
|
|
|
); |
71
|
|
|
|
72
|
|
|
//Creating relation schema |
73
|
|
|
$relation = $this->factory->make($class, compact('definition')); |
74
|
|
|
|
75
|
|
|
//Equavalent (low)? |
|
|
|
|
76
|
|
|
|
77
|
|
|
$this->relations[] = $relation; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Create inverse relations where needed. |
82
|
|
|
* |
83
|
|
|
* @throws DefinitionException |
84
|
|
|
*/ |
85
|
|
|
public function inverseRelations() |
86
|
|
|
{ |
87
|
|
|
/** |
88
|
|
|
* Inverse process is relation specific. |
89
|
|
|
*/ |
90
|
|
|
foreach ($this->relations as $relation) { |
91
|
|
|
$definition = $relation->getDefinition(); |
92
|
|
|
|
93
|
|
|
if ($definition->needInversion()) { |
94
|
|
|
if (!$relation instanceof InversableRelationInterface) { |
95
|
|
|
throw new DefinitionException(sprintf( |
96
|
|
|
"Unable to inverse relation '%s'.'%s', relation schema '%s' non inversable", |
97
|
|
|
$definition->sourceContext()->getClass(), |
98
|
|
|
$definition->getName(), |
99
|
|
|
get_class($relation) |
100
|
|
|
)); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
//Let's perform inversion |
104
|
|
|
$this->registerRelation($relation->inverseDefinition($definition->getInverse())); |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
//todo: normalize relations? |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Declare set of tables for each relation. Method must return Generator of AbstractTable |
113
|
|
|
* sequentially (attention, non sequential processing will cause collision issues between |
114
|
|
|
* tables). |
115
|
|
|
* |
116
|
|
|
* @param SchemaBuilder $builder |
117
|
|
|
* |
118
|
|
|
* @return \Generator |
119
|
|
|
*/ |
120
|
|
|
public function declareTables(SchemaBuilder $builder): \Generator |
121
|
|
|
{ |
122
|
|
|
foreach ($this->relations as $relation) { |
123
|
|
|
foreach ($relation->declareTables($builder) as $table) { |
124
|
|
|
yield $table; |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Pack relation schemas for specific model class in order to be saved in memory. |
131
|
|
|
* |
132
|
|
|
* @param string $class |
133
|
|
|
* |
134
|
|
|
* @return array |
135
|
|
|
*/ |
136
|
|
|
public function packRelations(string $class): array |
137
|
|
|
{ |
138
|
|
|
$result = []; |
139
|
|
|
foreach ($this->relations as $relation) { |
140
|
|
|
$definition = $relation->getDefinition(); |
141
|
|
|
|
142
|
|
|
if ($definition->sourceContext()->getClass() == $class) { |
143
|
|
|
$result[$definition->getName()] = $relation->packRelation(); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $result; |
148
|
|
|
} |
149
|
|
|
} |
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.