FieldTrait::ensureField()   B
last analyzed

Complexity

Conditions 7
Paths 17

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
eloc 24
c 3
b 0
f 0
nc 17
nop 4
dl 0
loc 38
ccs 19
cts 19
cp 1
crap 7
rs 8.6026
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Relation\Traits;
6
7
use Cycle\ORM\Relation;
8
use Cycle\Schema\Definition\Entity;
9
use Cycle\Schema\Definition\Field;
10
use Cycle\Schema\Definition\Map\FieldMap;
11
use Cycle\Schema\Exception\FieldException;
12
use Cycle\Schema\Exception\RegistryException;
13
use Cycle\Schema\Exception\RelationException;
14
use Cycle\Schema\Relation\OptionSchema;
15
use Cycle\Schema\Table\Column;
16
17
trait FieldTrait
18
{
19 4
    protected function getField(Entity $entity, int $field): Field
20
    {
21
        try {
22 4
            return $entity->getFields()->get($this->getOptions()->get($field));
23 2
        } catch (FieldException $e) {
24 2
            throw new RelationException(
25 2
                sprintf(
26 2
                    'Field `%s`.`%s` does not exists, referenced by `%s`',
27 2
                    $entity->getRole() ?? 'unknown',
28 2
                    $this->getOptions()->get($field),
29 2
                    $this->source,
30
                ),
31 2
                $e->getCode(),
32
                $e,
33
            );
34
        }
35
    }
36
37 902
    protected function getFields(Entity $entity, int $option): FieldMap
38
    {
39 902
        $fields = new FieldMap();
40 902
        $keys = (array) $this->getOptions()->get($option);
41
42 902
        foreach ($keys as $key) {
43
            try {
44 902
                $fields->set($key, $entity->getFields()->get($key));
45 2
            } catch (FieldException $e) {
46 2
                throw new RelationException(
47 2
                    sprintf(
48 2
                        'Field `%s`.`%s` does not exists, referenced by `%s`.',
49 2
                        $entity->getRole() ?? 'unknown',
50
                        $key,
51 2
                        $this->source,
52
                    ),
53 2
                    $e->getCode(),
54
                    $e,
55
                );
56
            }
57
        }
58
59 900
        return $fields;
60
    }
61
62 880
    protected function createRelatedFields(
63
        Entity $source,
64
        int $sourceKey,
65
        Entity $target,
66
        int $targetKey,
67
    ): void {
68 880
        $sourceFields = $this->getFields($source, $sourceKey);
69 880
        $targetColumns = (array) $this->options->get($targetKey);
70
71 880
        $sourceFieldNames = $sourceFields->getNames();
72
73 880
        if (count($targetColumns) !== count($sourceFieldNames)) {
74 32
            throw new RegistryException(
75 32
                sprintf(
76
                    'Inconsistent amount of related fields. '
77 32
                    . 'Source entity: `%s`; keys: `%s`. Target entity: `%s`; keys: `%s`.',
78 32
                    $source->getRole() ?? 'unknown',
79 32
                    implode('`, `', $this->getFields($source, $sourceKey)->getColumnNames()),
80 32
                    $target->getRole() ?? 'unknown',
81 32
                    implode('`, `', $targetColumns),
82
                ),
83
            );
84
        }
85
86 848
        $fields = array_combine($targetColumns, $sourceFieldNames);
87
88 848
        foreach ($fields as $targetColumn => $sourceFieldName) {
89 848
            $sourceField = $sourceFields->get($sourceFieldName);
90 848
            $this->ensureField(
91
                $target,
92
                $targetColumn,
93
                $sourceField,
94 848
                $this->options->get(Relation::NULLABLE),
95
            );
96
        }
97 848
    }
98
99
    /**
100
     * This method tries to replace column names with property names in relations
101
     */
102 784
    protected function normalizeContextFields(
103
        Entity $source,
104
        Entity $target,
105
        array $keys = ['innerKey', 'outerKey'],
106
    ): void {
107 784
        foreach ($keys as $key) {
108 784
            $options = $this->options->getOptions();
109
110 784
            if (!isset($options[$key])) {
111 784
                continue;
112
            }
113
114 160
            $columns = (array) $options[$key];
115
116 160
            foreach ($columns as $i => $column) {
117 160
                $entity = $key === 'innerKey' ? $source : $target;
118
119 160
                if ($entity->getFields()->hasColumn($column)) {
120
                    $columns[$i] = $entity->getFields()->getKeyByColumnName($column);
121
                }
122
            }
123
124 160
            $this->options = $this->options->withOptions([
0 ignored issues
show
Bug Best Practice introduced by
The property options does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
125 160
                $key => $columns,
126
            ]);
127
        }
128 784
    }
129
130 952
    /**
131
     * @param non-empty-string $fieldName
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
132
     */
133 952
    protected function ensureField(Entity $target, string $fieldName, Field $outerField, bool $nullable = false): void
134
    {
135 952
        // ensure that field will be indexed in memory for fast references
136
        $outerField->setReferenced(true);
137 58
138
        if ($target->getFields()->has($fieldName)) {
139
            // field already exists and defined by the user
140 950
            return;
141 950
        }
142 950
143 950
        $field = new Field();
144
        $field->setEntityClass($target->getClass());
145 950
        $field->setColumn($fieldName);
146 950
        $field->setTypecast($outerField->getTypecast());
147 946
        // Copy attributes from outer to target
148 946
        foreach ($outerField->getAttributes() as $k => $v) {
149 452
            $field->getAttributes()->set($k, $v);
150 130
        }
151 130
152
        switch ($outerField->getType()) {
153 362
            case 'primary':
154
                $field->setType('int');
155
                break;
156 950
            case 'bigPrimary':
157 260
                $field->setType('bigint');
158
                break;
159
            case 'smallPrimary':
160 950
                $field->setType('smallint');
161 950
                break;
162
            default:
163
                $field->setType($outerField->getType());
164
        }
165
166
        if ($nullable) {
167
            $field->getOptions()->set(Column::OPT_NULLABLE, true);
168
        }
169
170
        $target->getFields()->set($fieldName, $field);
171
    }
172
173
    abstract protected function getOptions(): OptionSchema;
174
}
175