Passed
Branch dev_2x (3e8772)
by Adrian
01:42
created

ColumnObserver::observeBaseEntity()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 36
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 26
c 1
b 0
f 0
dl 0
loc 36
rs 8.8817
cc 6
nc 12
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\CodeGenerator\Observer;
5
6
use DateTime;
7
use Nette\PhpGenerator\ClassType;
8
use Sirius\Orm\Blueprint\Column;
9
use Sirius\Orm\Blueprint\Mapper;
10
use Sirius\Orm\Blueprint\Relation;
11
use Sirius\Orm\Helpers\Str;
12
13
class ColumnObserver extends Base
14
{
15
    /**
16
     * @var Column
17
     */
18
    protected $column;
19
20
    public function with(Column $column)
21
    {
22
        $clone         = clone($this);
23
        $clone->column = $column;
24
25
        return $clone;
26
    }
27
28
    public function observe(string $key, $object)
29
    {
30
        if ($key == $this->column->getMapper()->getName() . '_mapper_config') {
31
            return $this->observeMapperConfig($object);
32
        }
33
        if ($key == $this->column->getMapper()->getName() . '_base_entity') {
34
            return $this->observeBaseEntity($object);
35
        }
36
37
        return $object;
38
    }
39
40
    public function __toString()
41
    {
42
        return sprintf('Observer for column %s of mapper %s', $this->column->getName(), $this->column->getMapper()->getName());
43
    }
44
45
    public function observeMapperConfig(array $config)
46
    {
47
        $config['columns'][]                       = $this->column->getName();
48
        $config['casts'][$this->column->getName()] = $this->getAttributeCastForConfig();
49
        if ($this->column->getAttributeName() && $this->column->getAttributeName() != $this->column->getName()) {
50
            $config['columnAttributeMap'][$this->column->getName()] = $this->column->getAttributeName();
51
        }
52
53
        return $config;
54
    }
55
56
    public function observeBaseEntity(ClassType $class)
57
    {
58
        $name = $this->column->getAttributeName() ?: $this->column->getName();
59
        $type = $this->getAttributeTypeForEntityClass();
60
61
        if (class_exists($type)) {
62
            /** @scrutinizer ignore-deprecated */ $class->getNamespace()->addUse($type);
63
            $type = basename($type);
64
        }
65
66
        if (($body = $this->getCastMethodBody($this->column->getType()))) {
67
            $cast = $class->addMethod(Str::methodName($name . ' Attribute', 'cast'));
68
            $cast->setVisibility(ClassType::VISIBILITY_PROTECTED);
69
            $cast->addParameter('value');
70
            $cast->addBody($body);
71
        }
72
73
        if ($this->column->getMapper()->getEntityStyle() === Mapper::ENTITY_STYLE_PROPERTIES) {
74
            $type .= $this->column->getNullable() ? '|null' : '';
75
            $class->addComment(sprintf('@property %s $%s', $type, $name));
76
        } else {
77
            $setter = $class->addMethod(Str::methodName($name, 'set'));
78
            $setter->setVisibility(ClassType::VISIBILITY_PUBLIC);
79
            $setter->addParameter('value')
80
                    ->setType($type)
81
                   ->setNullable($this->attributeIsNullable());
82
            $setter->addBody('$this->set(\'' . $name . '\', $value);');
83
84
            $getter = $class->addMethod(Str::methodName($name, 'get'));
85
            $getter->setVisibility(ClassType::VISIBILITY_PUBLIC);
86
            $getter->addBody('return $this->get(\'' . $name . '\');');
87
            $getter->setReturnType($type)
88
                   ->setReturnNullable($this->attributeIsNullable());
89
        }
90
91
        return $class;
92
    }
93
94
    private function getCastMethodBody(string $type)
95
    {
96
        switch ($type) {
97
            case Column::TYPE_FLOAT:
98
                return 'return $value === null ? $value : floatval($value);';
99
100
            case Column::TYPE_JSON:
101
                return 'return $value === null ? $value : (is_array($value) ? $value : \json_decode($value, true));';
102
103
            case Column::TYPE_INTEGER:
104
            case Column::TYPE_BIG_INTEGER:
105
            case Column::TYPE_SMALL_INTEGER:
106
            case Column::TYPE_TINY_INTEGER:
107
                return 'return $value === null ? $value : intval($value);';
108
109
            case Column::TYPE_DECIMAL:
110
                return 'return $value === null ? $value : round((float)$value, ' . $this->column->getPrecision() . ');';
111
112
            case Column::TYPE_DATETIME:
113
                return 'return !$value ? null : (($value instanceof DateTime) ? $value : new DateTime($value));';
114
115
            default:
116
                return null;
117
        }
118
    }
119
120
    private function getAttributeTypeForEntityClass()
121
    {
122
        if ($this->column->getAttributeType()) {
123
            return $this->column->getAttributeType();
124
        }
125
        $map = $this->getColumnTypeCastMap();
126
127
        return $map[$this->column->getType()] ?: 'mixed';
128
    }
129
130
    private function getAttributeCastForConfig()
131
    {
132
        if ($this->column->getAttributeCast()) {
133
            return $this->column->getAttributeCast();
134
        }
135
        $map = $this->getColumnTypeCastMap();
136
137
        return $map[$this->column->getType()] ?: 'mixed';
138
    }
139
140
    private function getColumnTypeCastMap()
141
    {
142
        return [
143
            Column::TYPE_BOOLEAN       => 'bool',
144
            Column::TYPE_VARCHAR       => 'string',
145
            Column::TYPE_TEXT          => 'string',
146
            Column::TYPE_JSON          => 'array',
147
            Column::TYPE_INTEGER       => 'int',
148
            Column::TYPE_BIG_INTEGER   => 'int',
149
            Column::TYPE_SMALL_INTEGER => 'int',
150
            Column::TYPE_TINY_INTEGER  => 'int',
151
            Column::TYPE_FLOAT         => 'float',
152
            Column::TYPE_DECIMAL       => 'float',
153
            Column::TYPE_DATE          => DateTime::class,
154
            Column::TYPE_DATETIME      => DateTime::class,
155
            Column::TYPE_TIMESTAMP     => DateTime::class,
156
        ];
157
    }
158
159
    /**
160
     * @return bool
161
     */
162
    protected function attributeIsNullable(): bool
163
    {
164
        if ($this->column->getNullable() || $this->column->getAutoIncrement()) {
165
            return true;
166
        }
167
168
        $relations = $this->column->getMapper()->getRelations();
169
        /** @var Relation $relation */
170
        foreach ($relations as $relation) {
171
            if ($relation->getNativeKey() === $this->column->getName()) {
172
                return true;
173
            }
174
        }
175
176
        return false;
177
    }
178
}
179