Passed
Push — dev_2x ( fa57a5...f00e1a )
by Adrian
01:48
created

MapperBaseGenerator::addNewSubselectQueryMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 14
rs 9.8666
cc 1
nc 1
nop 0
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
        $this->class->getNamespace()->addUse(Connection::class, null, $connectionAlias);
0 ignored issues
show
Deprecated Code introduced by
The function Nette\PhpGenerator\ClassType::getNamespace() has been deprecated: an object can be in multiple namespaces ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

167
        /** @scrutinizer ignore-deprecated */ $this->class->getNamespace()->addUse(Connection::class, null, $connectionAlias);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
168
        $method->addParameter('connection')
169
               ->setType($connectionAlias);
170
        $this->class->getNamespace()->addUse(Bindings::class, null, $bindingsAlias);
0 ignored issues
show
Deprecated Code introduced by
The function Nette\PhpGenerator\ClassType::getNamespace() has been deprecated: an object can be in multiple namespaces ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

170
        /** @scrutinizer ignore-deprecated */ $this->class->getNamespace()->addUse(Bindings::class, null, $bindingsAlias);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
171
        $method->addParameter('bindings')
172
               ->setType($bindingsAlias);
173
        $method->addParameter('indent')
174
               ->setType('string');
175
        $method->addBody(sprintf('$query = new %s($this->getReadConnection(), $this, $bindings, $indent);', $this->mapper->getQueryClass()));
176
        $method->addBody('return $this->behaviours->apply($this, __FUNCTION__, $query);');
177
    }
178
179
    protected function addSaveMethod()
180
    {
181
        $this->namespace->addUse(Insert::class, 'InsertAction');
182
        $this->namespace->addUse(Update::class, 'UpdateAction');
183
        $this->namespace->addUse(StateEnum::class);
184
185
        $method = $this->class->addMethod('save')
186
                              ->setReturnType('bool');
187
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
188
        $method->addParameter('withRelations', false);
189
        $method->setBody('
190
$action = $this->newSaveAction($entity, [\'relations\' => $withRelations]);
191
192
$this->connectionLocator->lockToWrite(true);
193
$this->getWriteConnection()->beginTransaction();
194
try {
195
    $action->run();
196
    $this->getWriteConnection()->commit();
197
    $this->connectionLocator->lockToWrite(false);
198
199
    return true;
200
} catch (FailedActionException $e) {
201
    $this->getWriteConnection()->rollBack();
202
    $this->connectionLocator->lockToWrite(false);
203
    throw $e;
204
}
205
        ');
206
207
        $method = $this->class->addMethod('newSaveAction')->setReturnType('UpdateAction');
208
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
209
        $method->addParameter('options');
210
        $method->setBody('
211
if ( ! $this->getHydrator()->getPk($entity) || $entity->getState() == StateEnum::NEW) {
212
    $action = new InsertAction($this, $entity, $options);
213
} else {
214
    $action = new UpdateAction($this, $entity, $options);
215
}
216
217
return $this->behaviours->apply($this, __FUNCTION__, $action);
218
        ');
219
    }
220
221
    protected function addDeleteMethod()
222
    {
223
        $method = $this->class->addMethod('delete')
224
                              ->setReturnType('bool');
225
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
226
        $method->addParameter('withRelations', false);
227
        $method->setBody('
228
$action = $this->newDeleteAction($entity, [\'relations\' => $withRelations]);
229
230
$this->connectionLocator->lockToWrite(true);
231
$this->getWriteConnection()->beginTransaction();
232
try {
233
    $action->run();
234
    $this->getWriteConnection()->commit();
235
236
    return true;
237
} catch (\Exception $e) {
238
    $this->getWriteConnection()->rollBack();
239
    throw $e;
240
}
241
        ');
242
243
        $this->namespace->addUse(Delete::class, 'DeleteAction');
244
        $method = $this->class->addMethod('newDeleteAction')->setReturnType('DeleteAction');
245
        $method->addParameter('entity')->setType($this->mapper->getEntityClass());
246
        $method->addParameter('options');
247
        $method->setBody('
248
$action = new DeleteAction($this, $entity, $options);
249
250
return $this->behaviours->apply($this, __FUNCTION__, $action);
251
        ');
252
    }
253
}
254