Issues (20)

src/Relation/HasMany.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * Cycle ORM Schema Builder.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Schema\Relation;
13
14
use Cycle\ORM\Relation;
15
use Cycle\Schema\Exception\RelationException;
16
use Cycle\Schema\InversableInterface;
17
use Cycle\Schema\Registry;
18
use Cycle\Schema\Relation\Traits\FieldTrait;
19
use Cycle\Schema\Relation\Traits\ForeignKeyTrait;
20
use Cycle\Schema\RelationInterface;
21
22
final class HasMany extends RelationSchema implements InversableInterface
23
{
24
    use FieldTrait;
25
    use ForeignKeyTrait;
26
27
    // internal relation type
28
    protected const RELATION_TYPE = Relation::HAS_MANY;
29
30
    // relation schema options
31
    protected const RELATION_SCHEMA = [
32
        // save with parent
33
        Relation::CASCADE            => true,
34
35
        // do not pre-load relation by default
36
        Relation::LOAD               => Relation::LOAD_PROMISE,
37
38
        // not nullable by default
39
        Relation::NULLABLE           => false,
40
41
        // custom where condition
42
        Relation::WHERE              => [],
43
44
        // link to parent entity primary key by default
45
        Relation::INNER_KEY          => '{source:primaryKey}',
46
47
        // default field name for inner key
48
        Relation::OUTER_KEY          => '{source:role}_{innerKey}',
49
50
        // rendering options
51
        RelationSchema::INDEX_CREATE => true,
52
        RelationSchema::FK_CREATE    => true,
53
        RelationSchema::FK_ACTION    => 'CASCADE'
54
    ];
55
56
    /**
57
     * @param Registry $registry
58
     */
59
    public function compute(Registry $registry): void
60
    {
61
        parent::compute($registry);
62
63
        $source = $registry->getEntity($this->source);
64
        $target = $registry->getEntity($this->target);
65
66
        // create target outer field
67
        $this->ensureField(
68
            $target,
69
            $this->options->get(Relation::OUTER_KEY),
70
            $this->getField($source, Relation::INNER_KEY),
71
            $this->options->get(Relation::NULLABLE)
0 ignored issues
show
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

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