HasMany   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Test Coverage

Coverage 96.88%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
dl 0
loc 119
ccs 31
cts 32
cp 0.9688
rs 10
c 1
b 0
f 0
wmc 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A inverseRelation() 0 18 4
A inverseTargets() 0 4 1
A compute() 0 15 1
A render() 0 22 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Relation;
6
7
use Cycle\ORM\Relation;
8
use Cycle\Schema\Definition\Entity;
9
use Cycle\Schema\Exception\RelationException;
10
use Cycle\Schema\InversableInterface;
11
use Cycle\Schema\Registry;
12
use Cycle\Schema\Relation\Traits\FieldTrait;
13
use Cycle\Schema\Relation\Traits\ForeignKeyTrait;
14
use Cycle\Schema\RelationInterface;
15
16
final class HasMany extends RelationSchema implements InversableInterface
17
{
18
    use FieldTrait;
19
    use ForeignKeyTrait;
20
21
    // internal relation type
22
    protected const RELATION_TYPE = Relation::HAS_MANY;
23
24
    // relation schema options
25
    protected const RELATION_SCHEMA = [
26
        // save with parent
27
        Relation::CASCADE => true,
28
29
        // do not pre-load relation by default
30
        Relation::LOAD => Relation::LOAD_PROMISE,
31
32
        // not nullable by default
33
        Relation::NULLABLE => false,
34
35
        // custom where condition
36
        Relation::WHERE => [],
37
38
        // custom orderBy rules
39
        Relation::ORDER_BY => [],
40
41
        // link to parent entity primary key by default
42
        Relation::INNER_KEY => '{source:primaryKey}',
43
44
        // default field name for inner key
45
        Relation::OUTER_KEY => '{source:role}_{innerKey}',
46
47
        // default collection.
48
        Relation::COLLECTION_TYPE => null,
49
50
        // rendering options
51
        RelationSchema::INDEX_CREATE => true,
52
        RelationSchema::FK_CREATE => true,
53
        RelationSchema::FK_ACTION => 'CASCADE',
54
        RelationSchema::FK_ON_DELETE => null,
55
    ];
56
57
    public function compute(Registry $registry): void
58
    {
59 192
        parent::compute($registry);
60
61 192
        $source = $registry->getEntity($this->source);
62
        $target = $registry->getEntity($this->target);
63 176
64 176
        $this->normalizeContextFields($source, $target);
65
66 176
        // create target outer field
67
        $this->createRelatedFields(
68
            $source,
69 176
            Relation::INNER_KEY,
70
            $target,
71 176
            Relation::OUTER_KEY,
72
        );
73 176
    }
74
75 168
    public function render(Registry $registry): void
76
    {
77
        $source = $registry->getEntity($this->source);
78
        $target = $registry->getEntity($this->target);
79
80 32
        $targetTable = $registry->getTableSchema($target);
81
82 32
        $innerFields = $this->getFields($source, Relation::INNER_KEY);
83 32
        $outerFields = $this->getFields($target, Relation::OUTER_KEY);
84
85 32
        if ($this->options->get(self::INDEX_CREATE) && $outerFields->count() > 0) {
86
            $targetTable->index($outerFields->getColumnNames());
87 32
        }
88 32
89
        if ($this->options->get(self::FK_CREATE)) {
90 32
            $this->createForeignCompositeKey(
91 32
                $registry,
92
                $source,
93
                $target,
94 32
                $innerFields,
95 16
                $outerFields,
96
                $this->options->get(self::INDEX_CREATE),
97 32
            );
98
        }
99
    }
100
101
    /**
102
     *
103
     * @return Entity[]
104 48
     */
105
    public function inverseTargets(Registry $registry): array
106
    {
107 48
        return [
108
            $registry->getEntity($this->target),
109
        ];
110
    }
111
112
    /**
113
     *
114
     * @throws RelationException
115
     *
116
     */
117
    public function inverseRelation(RelationInterface $relation, string $into, ?int $load = null): RelationInterface
118
    {
119
        if (!$relation instanceof BelongsTo && !$relation instanceof RefersTo) {
120 48
            throw new RelationException('HasMany relation can only be inversed into BelongsTo or RefersTo.');
121
        }
122 48
123 16
        if (!empty($this->options->get(Relation::WHERE))) {
124
            throw new RelationException('Unable to inverse HasMany relation with where scope.');
125
        }
126 32
127
        return $relation->withContext(
128
            $into,
129
            $this->target,
130 32
            $this->source,
131 32
            $this->options->withOptions([
132 32
                Relation::LOAD => $load,
133 32
                Relation::INNER_KEY => $this->options->get(Relation::OUTER_KEY),
134 32
                Relation::OUTER_KEY => $this->options->get(Relation::INNER_KEY),
135 32
            ]),
136 32
        );
137 32
    }
138
}
139