Passed
Push — master ( ff3639...8a879c )
by Anton
01:54
created

HasMany::inverseRelation()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 3
nop 2
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Cycle\Schema\Relation;
10
11
use Cycle\ORM\Relation;
12
use Cycle\Schema\Exception\RelationException;
13
use Cycle\Schema\InversableInterface;
14
use Cycle\Schema\Registry;
15
use Cycle\Schema\Relation\Traits\FieldTrait;
16
use Cycle\Schema\Relation\Traits\ForeignKeyTrait;
17
use Cycle\Schema\RelationInterface;
18
19
class HasMany extends RelationSchema implements InversableInterface
20
{
21
    use FieldTrait, ForeignKeyTrait;
22
23
    // internal relation type
24
    protected const RELATION_TYPE = Relation::HAS_MANY;
25
26
    // relation schema options
27
    protected const RELATION_SCHEMA = [
28
        // save with parent
29
        Relation::CASCADE            => true,
30
31
        // use outer entity constrain by default
32
        Relation::CONSTRAIN          => true,
33
34
        // custom where condition
35
        Relation::WHERE              => [],
36
37
        // not nullable by default
38
        Relation::NULLABLE           => false,
39
40
        // link to parent entity primary key by default
41
        Relation::INNER_KEY          => '{source:primaryKey}',
42
43
        // default field name for inner key
44
        Relation::OUTER_KEY          => '{source:role}_{innerKey}',
45
46
        // rendering options
47
        RelationSchema::INDEX_CREATE => true,
48
        RelationSchema::FK_CREATE    => true,
49
        RelationSchema::FK_ACTION    => 'CASCADE'
50
    ];
51
52
    /**
53
     * @param Registry $registry
54
     */
55
    public function compute(Registry $registry)
56
    {
57
        parent::compute($registry);
58
59
        $source = $registry->getEntity($this->source);
60
        $target = $registry->getEntity($this->target);
61
62
        // create target outer field
63
        $this->ensureField(
64
            $target,
65
            $this->options->get(Relation::OUTER_KEY),
66
            $this->getField($source, Relation::INNER_KEY),
67
            $this->options->get(Relation::NULLABLE)
0 ignored issues
show
Bug introduced by
It seems like $this->options->get(Cycle\ORM\Relation::NULLABLE) can also be of type string; however, parameter $nullable of Cycle\Schema\Relation\HasMany::ensureField() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
            /** @scrutinizer ignore-type */ $this->options->get(Relation::NULLABLE)
Loading history...
68
        );
69
    }
70
71
    /**
72
     * @param Registry $registry
73
     */
74
    public function render(Registry $registry)
75
    {
76
        $source = $registry->getEntity($this->source);
77
        $target = $registry->getEntity($this->target);
78
79
        $innerField = $this->getField($source, Relation::INNER_KEY);
80
        $outerField = $this->getField($target, Relation::OUTER_KEY);
81
82
        $table = $registry->getTableSchema($target);
83
84
        if ($this->options->get(self::INDEX_CREATE)) {
85
            $table->index([$outerField->getColumn()]);
86
        }
87
88
        if ($this->options->get(self::FK_CREATE)) {
89
            $this->createForeignKey($registry, $source, $target, $innerField, $outerField);
90
        }
91
    }
92
93
    /**
94
     * @param Registry $registry
95
     * @return array
96
     */
97
    public function inverseTargets(Registry $registry): array
98
    {
99
        return [
100
            $registry->getEntity($this->target)
101
        ];
102
    }
103
104
    /**
105
     * @param RelationInterface $relation
106
     * @param string            $into
107
     * @return RelationInterface
108
     *
109
     * @throws RelationException
110
     */
111
    public function inverseRelation(RelationInterface $relation, string $into): RelationInterface
112
    {
113
        if (!$relation instanceof BelongsTo && !$relation instanceof RefersTo) {
114
            throw new RelationException("HasMany relation can only be inversed into BelongsTo or RefersTo");
115
        }
116
117
        if (!empty($this->options->get(Relation::WHERE))) {
118
            throw new RelationException("Unable to inverse HasMany relation with where constrain");
119
        }
120
121
        return $relation->withContext(
122
            $into,
123
            $this->target,
124
            $this->source,
125
            $this->options->withOptions([
126
                Relation::INNER_KEY => $this->options->get(Relation::OUTER_KEY),
127
                Relation::OUTER_KEY => $this->options->get(Relation::INNER_KEY),
128
            ])
129
        );
130
    }
131
}