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

RelationBuilder::inverseRelations()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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