Completed
Branch feature/pre-split (ce4b6b)
by Anton
03:56
created

BelongsToSchema   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 84
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 84
rs 10
c 0
b 0
f 0
wmc 4
lcom 1
cbo 7

1 Method

Rating   Name   Duplication   Size   Complexity  
B declareTables() 0 42 4
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ORM\Schemas\Relations;
8
9
use Spiral\ORM\Exceptions\RelationSchemaException;
10
use Spiral\ORM\Record;
11
use Spiral\ORM\Schemas\Relations\Traits\ForeignsTrait;
12
use Spiral\ORM\Schemas\Relations\Traits\TablesTrait;
13
use Spiral\ORM\Schemas\Relations\Traits\TypecastTrait;
14
use Spiral\ORM\Schemas\SchemaBuilder;
15
16
/**
17
 * Declares that parent record belongs to some parent based on value in [inner] key. Basically this
18
 * relation is mirror copy of HasOne/HasMany relation.
19
 *
20
 * BelongsTo relations can not be inversed!
21
 *
22
 * Example, [Post has one User, relation name "author"], user primary key is "id":
23
 * - relation will create inner key "author_id" in "posts" table (or other table name), nullable by
24
 *   default
25
 * - relation will create index on column "author_id" in "posts" table if allowed
26
 * - relation will create foreign key "posts"."author_id" => "users"."id" if allowed
27
 */
28
class BelongsToSchema extends AbstractSchema
29
{
30
    use TablesTrait, TypecastTrait, ForeignsTrait;
31
32
    /**
33
     * Relation type.
34
     */
35
    const RELATION_TYPE = Record::BELONGS_TO;
36
37
    /**
38
     * Options needed in runtime.
39
     */
40
    const PACK_OPTIONS = [Record::INNER_KEY, Record::OUTER_KEY, Record::NULLABLE];
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    const OPTIONS_TEMPLATE = [
46
        //Outer key is primary key of related record by default
47
        Record::OUTER_KEY         => '{target:primaryKey}',
48
49
        //Inner key will be based on singular name of relation and outer key name
50
        Record::INNER_KEY         => '{relation:singular}_{option:outerKey}',
51
52
        //Set constraints (foreign keys) by default
53
        Record::CREATE_CONSTRAINT => true,
54
55
        //@link https://en.wikipedia.org/wiki/Foreign_key
56
        Record::CONSTRAINT_ACTION => 'CASCADE',
57
58
        //Relation allowed to create indexes in inner table
59
        Record::CREATE_INDEXES    => true,
60
61
        //We are going to make all relations nullable by default, so we can add fields to existed
62
        //tables without raising an exceptions
63
        Record::NULLABLE          => true,
64
    ];
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function declareTables(SchemaBuilder $builder): array
70
    {
71
        $sourceTable = $this->sourceTable($builder);
72
        $targetTable = $this->targetTable($builder);
73
74
        if (!$targetTable->hasColumn($this->option(Record::OUTER_KEY))) {
75
            throw new RelationSchemaException(sprintf("Outer key '%s'.'%s' (%s) does not exists",
76
                $targetTable->getName(),
77
                $this->option(Record::OUTER_KEY),
78
                $this->definition->getName()
79
            ));
80
        }
81
82
        //Column to be used as outer key
83
        $outerKey = $targetTable->column($this->option(Record::OUTER_KEY));
84
85
        //Column to be used as inner key
86
        $innerKey = $sourceTable->column($this->option(Record::INNER_KEY));
87
88
        //Syncing types
89
        $innerKey->setType($this->resolveType($outerKey));
90
91
        //If nullable
92
        $innerKey->nullable($this->option(Record::NULLABLE));
93
94
        //Do we need indexes?
95
        if ($this->option(Record::CREATE_INDEXES)) {
96
            $sourceTable->index([$innerKey->getName()]);
97
        }
98
99
        if ($this->option(Record::CREATE_CONSTRAINT)) {
100
            $this->createForeign(
101
                $sourceTable,
102
                $innerKey,
103
                $outerKey,
104
                $this->option(Record::CONSTRAINT_ACTION),
105
                $this->option(Record::CONSTRAINT_ACTION)
106
            );
107
        }
108
109
        return [$sourceTable];
110
    }
111
}