Passed
Pull Request — master (#132)
by Dorian
05:05
created

PivotTableMethodsDescriptor::findLocalAnnotation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace TheCodingMachine\TDBM\Utils;
5
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
8
use Doctrine\DBAL\Schema\Table;
9
use function sprintf;
10
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
11
use TheCodingMachine\TDBM\Utils\Annotation\Annotations;
12
use Zend\Code\Generator\DocBlock\Tag\ParamTag;
13
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
14
use Zend\Code\Generator\MethodGenerator;
15
use Zend\Code\Generator\ParameterGenerator;
16
17
class PivotTableMethodsDescriptor implements MethodDescriptorInterface
18
{
19
    /**
20
     * @var Table
21
     */
22
    private $pivotTable;
23
24
    private $useAlternateName = false;
25
26
    /**
27
     * @var ForeignKeyConstraint
28
     */
29
    private $localFk;
30
31
    /**
32
     * @var ForeignKeyConstraint
33
     */
34
    private $remoteFk;
35
    /**
36
     * @var NamingStrategyInterface
37
     */
38
    private $namingStrategy;
39
    /**
40
     * @var string
41
     */
42
    private $beanNamespace;
43
    /**
44
     * @var AnnotationParser
45
     */
46
    private $annotationParser;
47
48
    /**
49
     * @var array
50
     */
51
    private $localAnnotations;
52
    /**
53
     * @var array
54
     */
55
    private $remoteAnnotations;
56
57
    /**
58
     * @param Table $pivotTable The pivot table
59
     * @param ForeignKeyConstraint $localFk
60
     * @param ForeignKeyConstraint $remoteFk
61
     * @param NamingStrategyInterface $namingStrategy
62
     */
63
    public function __construct(Table $pivotTable, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk, NamingStrategyInterface $namingStrategy, string $beanNamespace, AnnotationParser $annotationParser)
64
    {
65
        $this->pivotTable = $pivotTable;
66
        $this->localFk = $localFk;
67
        $this->remoteFk = $remoteFk;
68
        $this->namingStrategy = $namingStrategy;
69
        $this->beanNamespace = $beanNamespace;
70
        $this->annotationParser = $annotationParser;
71
    }
72
73
    /**
74
     * Requests the use of an alternative name for this method.
75
     */
76
    public function useAlternativeName(): void
77
    {
78
        $this->useAlternateName = true;
79
    }
80
81
    /**
82
     * Returns the name of the method to be generated.
83
     *
84
     * @return string
85
     */
86
    public function getName() : string
87
    {
88
        if (!$this->useAlternateName) {
89
            return 'get'.TDBMDaoGenerator::toCamelCase($this->remoteFk->getForeignTableName());
90
        } else {
91
            return 'get'.TDBMDaoGenerator::toCamelCase($this->remoteFk->getForeignTableName()).'By'.TDBMDaoGenerator::toCamelCase($this->pivotTable->getName());
92
        }
93
    }
94
95
    /**
96
     * Returns the name of the class that will be returned by the getter (short name).
97
     *
98
     * @return string
99
     */
100
    public function getBeanClassName(): string
101
    {
102
        return $this->namingStrategy->getBeanClassName($this->remoteFk->getForeignTableName());
103
    }
104
105
    /**
106
     * Returns the plural name.
107
     *
108
     * @return string
109
     */
110
    private function getPluralName() : string
111
    {
112
        if (!$this->useAlternateName) {
113
            return TDBMDaoGenerator::toCamelCase($this->remoteFk->getForeignTableName());
114
        } else {
115
            return TDBMDaoGenerator::toCamelCase($this->remoteFk->getForeignTableName()).'By'.TDBMDaoGenerator::toCamelCase($this->pivotTable->getName());
116
        }
117
    }
118
119
    /**
120
     * Returns the singular name.
121
     *
122
     * @return string
123
     */
124
    private function getSingularName() : string
125
    {
126
        if (!$this->useAlternateName) {
127
            return TDBMDaoGenerator::toCamelCase(TDBMDaoGenerator::toSingular($this->remoteFk->getForeignTableName()));
128
        } else {
129
            return TDBMDaoGenerator::toCamelCase(TDBMDaoGenerator::toSingular($this->remoteFk->getForeignTableName())).'By'.TDBMDaoGenerator::toCamelCase($this->pivotTable->getName());
130
        }
131
    }
132
133
    /**
134
     * Returns the code of the method.
135
     *
136
     * @return MethodGenerator[]
137
     */
138
    public function getCode() : array
139
    {
140
        $singularName = $this->getSingularName();
141
        $pluralName = $this->getPluralName();
142
        $remoteBeanName = $this->getBeanClassName();
143
        $variableName = TDBMDaoGenerator::toVariableName($remoteBeanName);
144
        $fqcnRemoteBeanName = '\\'.$this->beanNamespace.'\\'.$remoteBeanName;
145
        $pluralVariableName = $variableName.'s';
146
147
        $getter = new MethodGenerator('get'.$pluralName);
148
        $getter->setDocBlock(sprintf('Returns the list of %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
149
        $getter->getDocBlock()->setTag(new ReturnTag([ $fqcnRemoteBeanName.'[]' ]));
150
        $getter->setReturnType('array');
151
        $getter->setBody(sprintf('return $this->_getRelationships(%s);', var_export($this->remoteFk->getLocalTableName(), true)));
152
153
154
        $adder = new MethodGenerator('add'.$singularName);
155
        $adder->setDocBlock(sprintf('Adds a relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
156
        $adder->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
157
        $adder->setReturnType('void');
158
        $adder->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
159
        $adder->setBody(sprintf('$this->addRelationship(%s, $%s);', var_export($this->remoteFk->getLocalTableName(), true), $variableName));
160
161
        $remover = new MethodGenerator('remove'.$singularName);
162
        $remover->setDocBlock(sprintf('Deletes the relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
163
        $remover->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
164
        $remover->setReturnType('void');
165
        $remover->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
166
        $remover->setBody(sprintf('$this->_removeRelationship(%s, $%s);', var_export($this->remoteFk->getLocalTableName(), true), $variableName));
167
168
        $has = new MethodGenerator('has'.$singularName);
169
        $has->setDocBlock(sprintf('Returns whether this bean is associated with %s via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
170
        $has->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
171
        $has->getDocBlock()->setTag(new ReturnTag([ 'bool' ]));
172
        $has->setReturnType('bool');
173
        $has->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
174
        $has->setBody(sprintf('return $this->hasRelationship(%s, $%s);', var_export($this->remoteFk->getLocalTableName(), true), $variableName));
175
176
        $setter = new MethodGenerator('set'.$pluralName);
177
        $setter->setDocBlock(sprintf('Sets all relationships with %s associated to this bean via the %s pivot table.
178
Exiting relationships will be removed and replaced by the provided relationships.', $remoteBeanName, $this->pivotTable->getName()));
179
        $setter->getDocBlock()->setTag(new ParamTag($pluralVariableName, [ $fqcnRemoteBeanName.'[]' ]));
180
        $setter->getDocBlock()->setTag(new ReturnTag([ 'void' ]));
181
        $setter->setReturnType('void');
182
        $setter->setParameter(new ParameterGenerator($pluralVariableName, 'array'));
183
        $setter->setBody(sprintf('$this->setRelationships(%s, $%s);', var_export($this->remoteFk->getLocalTableName(), true), $pluralVariableName));
184
185
        return [ $getter, $adder, $remover, $has, $setter ];
186
    }
187
188
    /**
189
     * Returns an array of classes that needs a "use" for this method.
190
     *
191
     * @return string[]
192
     */
193
    public function getUsedClasses() : array
194
    {
195
        return [$this->getBeanClassName()];
196
    }
197
198
    /**
199
     * Returns the code to past in jsonSerialize.
200
     *
201
     * @return string
202
     */
203
    public function getJsonSerializeCode() : string
204
    {
205
        if ($this->findRemoteAnnotation(Annotation\JsonIgnore::class) ||
206
            $this->findLocalAnnotation(Annotation\JsonInclude::class) ||
207
            $this->findLocalAnnotation(Annotation\JsonRecursive::class)) {
208
            return '';
209
        }
210
211
        /** @var Annotation\JsonFormat $jsonFormat */
212
        if ($jsonFormat = $this->findRemoteAnnotation(Annotation\JsonFormat::class)) {
213
            $method = $jsonFormat->method ?? 'get' . ucfirst($jsonFormat->property);
214
            $format = "$method()";
215
        } else {
216
            $stopRecursion = $this->findRemoteAnnotation(Annotation\JsonRecursive::class) ? '' : 'true';
217
            $format = "jsonSerialize($stopRecursion)";
218
        }
219
        $isIncluded = $this->findRemoteAnnotation(Annotation\JsonInclude::class) !== null;
220
        /** @var Annotation\JsonKey $jsonKey */
221
        $jsonKey = $this->findRemoteAnnotation(Annotation\JsonKey::class);
222
        $index = $jsonKey ? $jsonKey->key : lcfirst($this->getPluralName());
0 ignored issues
show
introduced by
$jsonKey is of type TheCodingMachine\TDBM\Utils\Annotation\JsonKey, thus it always evaluated to true.
Loading history...
223
        $class = $this->getBeanClassName();
224
        $getter = $this->getName();
225
        $code = <<<PHP
226
\$array['$index'] = array_map(function ($class \$object) {
227
    return \$object->$format;
228
}, \$this->$getter());
229
PHP;
230
        if (!$isIncluded) {
231
            $code = preg_replace('(\n)', '\0    ', $code);
232
            $code = <<<PHP
233
if (!\$stopRecursion) {
234
    $code
235
};
236
PHP;
237
        }
238
        return $code;
239
    }
240
241
    /**
242
     * @return Table
243
     */
244
    public function getPivotTable(): Table
245
    {
246
        return $this->pivotTable;
247
    }
248
249
    /**
250
     * @return ForeignKeyConstraint
251
     */
252
    public function getLocalFk(): ForeignKeyConstraint
253
    {
254
        return $this->localFk;
255
    }
256
257
    /**
258
     * @return ForeignKeyConstraint
259
     */
260
    public function getRemoteFk(): ForeignKeyConstraint
261
    {
262
        return $this->remoteFk;
263
    }
264
265
    private function findLocalAnnotation(string $type): ?object
266
    {
267
        foreach ($this->getLocalAnnotations() as $annotations) {
268
            $annotation = $annotations->findAnnotation($type);
269
            if ($annotation !== null) {
270
                return $annotation;
271
            }
272
        }
273
        return null;
274
    }
275
276
    private function findRemoteAnnotation(string $type): ?object
277
    {
278
        foreach ($this->getRemoteAnnotations() as $annotations) {
279
            $annotation = $annotations->findAnnotation($type);
280
            if ($annotation !== null) {
281
                return $annotation;
282
            }
283
        }
284
        return null;
285
    }
286
287
    /**
288
     * @return Annotations[]
289
     */
290
    private function getLocalAnnotations(): array
291
    {
292
        if ($this->localAnnotations === null) {
293
            $this->localAnnotations = $this->getFkAnnotations($this->localFk);
294
        }
295
        return $this->localAnnotations;
296
    }
297
298
    /**
299
     * @return Annotations[]
300
     */
301
    private function getRemoteAnnotations(): array
302
    {
303
        if ($this->remoteAnnotations === null) {
304
            $this->remoteAnnotations = $this->getFkAnnotations($this->remoteFk);
305
        }
306
        return $this->remoteAnnotations;
307
    }
308
309
    /**
310
     * @param ForeignKeyConstraint $fk
311
     * @return Annotations[]
312
     */
313
    private function getFkAnnotations(ForeignKeyConstraint $fk): array
314
    {
315
        $annotations = [];
316
        foreach ($this->getFkColumns($fk) as $column) {
317
            $annotations[] = $this->annotationParser->getColumnAnnotations($column, $fk->getLocalTable());
318
        }
319
        return $annotations;
320
    }
321
322
    /**
323
     * @param ForeignKeyConstraint $fk
324
     * @return Column[]
325
     */
326
    private function getFkColumns(ForeignKeyConstraint $fk): array
327
    {
328
        $table = $fk->getLocalTable();
329
        $columns = [];
330
        foreach ($fk->getUnquotedLocalColumns() as $column) {
331
            $columns[] = $table->getColumn($column);
332
        }
333
        return $columns;
334
    }
335
}
336