Passed
Push — master ( 384538...4f7dc7 )
by Anton
02:27
created

RelationSchema   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 132
rs 10
c 0
b 0
f 0
wmc 17

6 Methods

Rating   Name   Duplication   Size   Complexity  
A withContext() 0 13 1
A compute() 0 9 2
A packSchema() 0 20 3
A getOptions() 0 3 1
B getLoadMethod() 0 16 7
A getPrimary() 0 9 3
1
<?php
2
/**
3
 * Cycle ORM Schema Builder.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
declare(strict_types=1);
9
10
namespace Cycle\Schema\Relation;
11
12
use Cycle\ORM\Relation;
13
use Cycle\Schema\Definition\Entity;
14
use Cycle\Schema\Exception\RegistryException;
15
use Cycle\Schema\Registry;
16
use Cycle\Schema\RelationInterface;
17
18
/**
19
 * Defines relation options, renders needed columns and other options.
20
 */
21
abstract class RelationSchema implements RelationInterface
22
{
23
    // relation rendering options
24
    public const INDEX_CREATE     = 1001;
25
    public const FK_CREATE        = 1002;
26
    public const FK_ACTION        = 1003;
27
    public const INVERSE          = 1005;
28
    public const MORPH_KEY_LENGTH = 1009;
29
30
    // options to be excluded from generated schema (helpers)
31
    protected const EXCLUDE = [self::FK_CREATE, self::FK_ACTION, self::INDEX_CREATE];
32
33
    // exported relation type
34
    protected const RELATION_TYPE = null;
35
36
    // name of all required relation options
37
    protected const RELATION_SCHEMA = [];
38
39
    /** @var string */
40
    protected $source;
41
42
    /** @var string */
43
    protected $target;
44
45
    /** @var OptionSchema */
46
    protected $options;
47
48
    /**
49
     * @inheritdoc
50
     */
51
    public function withContext(string $name, string $source, string $target, OptionSchema $options): RelationInterface
52
    {
53
        $relation = clone $this;
54
        $relation->source = $source;
55
        $relation->target = $target;
56
57
        $relation->options = $options->withTemplate(static::RELATION_SCHEMA)->withContext([
58
            'relation'    => $name,
59
            'source:role' => $source,
60
            'target:role' => $target,
61
        ]);
62
63
        return $relation;
64
    }
65
66
    /**
67
     * @param Registry $registry
68
     */
69
    public function compute(Registry $registry)
70
    {
71
        $this->options = $this->options->withContext([
72
            'source:primaryKey' => $this->getPrimary($registry->getEntity($this->source))
73
        ]);
74
75
        if ($registry->hasEntity($this->target)) {
76
            $this->options = $this->options->withContext([
77
                'target:primaryKey' => $this->getPrimary($registry->getEntity($this->target))
78
            ]);
79
        }
80
    }
81
82
    /**
83
     * @return array
84
     */
85
    public function packSchema(): array
86
    {
87
        $schema = [];
88
89
        foreach (static::RELATION_SCHEMA as $option => $template) {
90
            if (in_array($option, static::EXCLUDE)) {
91
                continue;
92
            }
93
94
            $schema[$option] = $this->options->get($option);
95
        }
96
97
        // load option is not required in schema
98
        unset($schema[Relation::LOAD]);
99
100
        return [
101
            Relation::TYPE   => static::RELATION_TYPE,
102
            Relation::TARGET => $this->target,
103
            Relation::LOAD   => $this->getLoadMethod(),
104
            Relation::SCHEMA => $schema
105
        ];
106
    }
107
108
    /**
109
     * @return int|null
110
     */
111
    protected function getLoadMethod(): ?int
112
    {
113
        if (!$this->options->has(Relation::LOAD)) {
114
            return null;
115
        }
116
117
        switch ($this->options->get(Relation::LOAD)) {
118
            case 'eager':
119
            case Relation::LOAD_EAGER:
120
                return Relation::LOAD_EAGER;
121
            case 'promise':
122
            case 'lazy':
123
            case Relation::LOAD_PROMISE:
124
                return Relation::LOAD_PROMISE;
125
            default:
126
                return null;
127
        }
128
    }
129
130
    /**
131
     * @return OptionSchema
132
     */
133
    protected function getOptions(): OptionSchema
134
    {
135
        return $this->options;
136
    }
137
138
    /**
139
     * @param Entity $entity
140
     * @return string
141
     *
142
     * @throws RegistryException
143
     */
144
    protected function getPrimary(Entity $entity): string
145
    {
146
        foreach ($entity->getFields() as $name => $field) {
147
            if ($field->isPrimary()) {
148
                return $name;
149
            }
150
        }
151
152
        throw new RegistryException("Entity `{$entity->getRole()}` must have defined primary key");
153
    }
154
}