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

MapperBaseGenerator   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 13
eloc 105
c 0
b 0
f 0
dl 0
loc 226
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm\CodeGenerator;
5
6
use Nette\PhpGenerator\ClassType;
7
use Nette\PhpGenerator\Dumper;
8
use Nette\PhpGenerator\PhpNamespace;
9
use Sirius\Orm\Action\Delete;
10
use Sirius\Orm\Action\Insert;
11
use Sirius\Orm\Action\Update;
12
use Sirius\Orm\Blueprint\Mapper;
13
use Sirius\Orm\Blueprint\Relation;
14
use Sirius\Orm\Connection;
15
use Sirius\Orm\Entity\ClassMethodsHydrator;
16
use Sirius\Orm\Entity\GenericHydrator;
17
use Sirius\Orm\Entity\StateEnum;
18
use Sirius\Orm\Exception\FailedActionException;
19
use Sirius\Orm\MapperConfig;
20
use Sirius\Sql\Bindings;
21
22
class MapperBaseGenerator
23
{
24
    /**
25
     * @var ClassType
26
     */
27
    protected $class;
28
29
    /**
30
     * @var PhpNamespace
31
     */
32
    protected $namespace;
33
34
    /**
35
     * @var Mapper
36
     */
37
    protected $mapper;
38
    /**
39
     * @var Dumper
40
     */
41
    protected $dumper;
42
43
    public function __construct(Mapper $mapper)
44
    {
45
        $this->dumper    = new Dumper();
46
        $this->mapper    = $mapper;
47
        $this->namespace = new PhpNamespace($mapper->getNamespace());
48
        $this->class     = new ClassType($mapper->getClassName() . 'Base', $this->namespace);
49
        $this->class->setAbstract(true);
50
    }
51
52
    public function getClass()
53
    {
54
        $this->build();
55
56
        return $this->class;
57
    }
58
59
    protected function build()
60
    {
61
        $this->namespace->addUse(\Sirius\Orm\Mapper::class);
62
        $this->namespace->addUse(FailedActionException::class);
63
64
        $this->class->setExtends('Mapper');
65
66
        $this->class->addComment(sprintf('@method %s where($column, $value, $condition)', $this->mapper->getQueryClass()));
67
        $this->class->addComment(sprintf('@method %s orderBy(string $expr, string ...$exprs)', $this->mapper->getQueryClass()));
68
69
        $this->addInitMethod();
70
        $this->addInitRelationsMethod();
71
        $this->addFindMethod();
72
        $this->addNewQueryMethod();
73
        $this->addNewSubselectQueryMethod();
74
        $this->addSaveMethod();
75
        $this->addDeleteMethod();
76
77
        foreach ($this->mapper->getTraits() as $trait) {
78
            $this->class->addTrait($trait);
79
        }
80
81
        $this->class = $this->mapper->getOrm()->applyObservers($this->mapper->getName() . '_base_mapper', $this->class);
82
    }
83
84
    protected function addInitMethod()
85
    {
86
        $this->namespace->addUse(MapperConfig::class);
87
88
        $method = $this->class->addMethod('init')->setVisibility(ClassType::VISIBILITY_PROTECTED);
89
90
        $body = '$this->mapperConfig = MapperConfig::fromArray(';
91
92
        $config = [
93
            'entityClass'        => $this->mapper->getEntityNamespace() . '\\' . $this->mapper->getEntityClass(),
94
            'primaryKey'         => $this->mapper->getPrimaryKey(),
95
            'table'              => $this->mapper->getTable(),
96
            'tableAlias'         => $this->mapper->getTableAlias(),
97
            'guards'             => $this->mapper->getGuards(),
98
            'columns'            => [],
99
            'columnAttributeMap' => [],
100
            'casts'              => []
101
        ];
102
103
        $config = $this->mapper->getOrm()->applyObservers($this->mapper->getName() . '_mapper_config', $config);
104
105
        $body .= $this->dumper->dump($config);
106
107
        $body .= ');' . PHP_EOL;
108
109
        if ($this->mapper->getEntityStyle() == Mapper::ENTITY_STYLE_PROPERTIES) {
110
            $this->namespace->addUse(GenericHydrator::class);
111
            $body .= '$this->hydrator     = new GenericHydrator($this->orm->getCastingManager());' . PHP_EOL;
112
        } else {
113
            $this->namespace->addUse(ClassMethodsHydrator::class);
114
            $body .= '$this->hydrator     = new ClassMethodsHydrator($this->orm->getCastingManager());' . PHP_EOL;
115
        }
116
        $body .= '$this->hydrator->setMapper($this);' . PHP_EOL;
117
118
        $body .= PHP_EOL;
119
120
        $body .= '$this->initRelations();';
121
122
        $method->setBody($body);
123
124
        return $method;
125
    }
126
127
    protected function addInitRelationsMethod()
128
    {
129
        $method = $this->class->addMethod('initRelations')->setVisibility(ClassType::VISIBILITY_PROTECTED);
130
131
        $body = '';
132
133
        /** @var Relation $relation */
134
        foreach ($this->mapper->getRelations() as $name => $relation) {
135
            $body .= '$this->addRelation(\'' . $name . '\', ' . $this->dumper->dump($relation->toArray(), 4) . ');' . PHP_EOL;
136
            $body .= PHP_EOL;
137
        }
138
139
        $method->setBody($body);
140
141
        return $method;
142
    }
143
144
    protected function addFindMethod()
145
    {
146
        $this->namespace->addUse($this->mapper->getEntityNamespace() . '\\' . $this->mapper->getEntityClass());
147
        $method = $this->class->addMethod('find')
148
                              ->setReturnNullable(true)
149
                              ->setReturnType($this->mapper->getEntityClass());
150
        $method->addParameter('pk');
151
        $method->addParameter('load', [])->setType('array');
152
        $method->setBody('return $this->newQuery()->find($pk, $load);');
153
    }
154
155
    protected function addNewQueryMethod()
156
    {
157
        $method = $this->class->addMethod('newQuery')
158
                              ->setReturnType($this->mapper->getQueryClass());
159
        $method->addBody(sprintf('$query = new %s($this->getReadConnection(), $this);', $this->mapper->getQueryClass()));
160
        $method->addBody('return $this->behaviours->apply($this, __FUNCTION__, $query);');
161
    }
162
163
    protected function addNewSubselectQueryMethod()
164
    {
165
        $method = $this->class->addMethod('newSubselectQuery')
166
                              ->setReturnType($this->mapper->getQueryClass());
167
        /** @scrutinizer ignore-deprecated */ $this->class->getNamespace()->addUse(Connection::class, null, $connectionAlias);
168
        $method->addParameter('connection')
169
               ->setType($connectionAlias);
170
171
        /** @scrutinizer ignore-deprecated */ $this->class->getNamespace()->addUse(Bindings::class, null, $bindingsAlias);
172
        $method->addParameter('bindings')
173
               ->setType($bindingsAlias);
174
175
        $method->addParameter('indent')
176
               ->setType('string');
177
178
        $method->addBody(sprintf('$query = new %s($this->getReadConnection(), $this, $bindings, $indent);', $this->mapper->getQueryClass()));
179
        $method->addBody('return $this->behaviours->apply($this, __FUNCTION__, $query);');
180
    }
181
182
    protected function addSaveMethod()
183
    {
184
        $this->namespace->addUse(Insert::class, 'InsertAction');
185
        $this->namespace->addUse(Update::class, 'UpdateAction');
186
        $this->namespace->addUse(StateEnum::class);
187
188
        $method = $this->class->addMethod('save')
189
                              ->setReturnType('bool');
190
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
191
        $method->addParameter('withRelations', false);
192
        $method->setBody('
193
$action = $this->newSaveAction($entity, [\'relations\' => $withRelations]);
194
195
$this->connectionLocator->lockToWrite(true);
196
$this->getWriteConnection()->beginTransaction();
197
try {
198
    $action->run();
199
    $this->getWriteConnection()->commit();
200
    $this->connectionLocator->lockToWrite(false);
201
202
    return true;
203
} catch (FailedActionException $e) {
204
    $this->getWriteConnection()->rollBack();
205
    $this->connectionLocator->lockToWrite(false);
206
    throw $e;
207
}
208
        ');
209
210
        $method = $this->class->addMethod('newSaveAction')->setReturnType('UpdateAction');
211
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
212
        $method->addParameter('options');
213
        $method->setBody('
214
if ( ! $this->getHydrator()->getPk($entity) || $entity->getState() == StateEnum::NEW) {
215
    $action = new InsertAction($this, $entity, $options);
216
} else {
217
    $action = new UpdateAction($this, $entity, $options);
218
}
219
220
return $this->behaviours->apply($this, __FUNCTION__, $action);
221
        ');
222
    }
223
224
    protected function addDeleteMethod()
225
    {
226
        $method = $this->class->addMethod('delete')
227
                              ->setReturnType('bool');
228
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
229
        $method->addParameter('withRelations', false);
230
        $method->setBody('
231
$action = $this->newDeleteAction($entity, [\'relations\' => $withRelations]);
232
233
$this->connectionLocator->lockToWrite(true);
234
$this->getWriteConnection()->beginTransaction();
235
try {
236
    $action->run();
237
    $this->getWriteConnection()->commit();
238
239
    return true;
240
} catch (\Exception $e) {
241
    $this->getWriteConnection()->rollBack();
242
    throw $e;
243
}
244
        ');
245
246
        $this->namespace->addUse(Delete::class, 'DeleteAction');
247
        $method = $this->class->addMethod('newDeleteAction')->setReturnType('DeleteAction');
248
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
249
        $method->addParameter('options');
250
        $method->setBody('
251
$action = new DeleteAction($this, $entity, $options);
252
253
return $this->behaviours->apply($this, __FUNCTION__, $action);
254
        ');
255
    }
256
}
257