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

PivotTableMethodsDescriptor::useAlternativeName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
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
        if ($jsonFormat = $this->findRemoteAnnotation(Annotation\JsonFormat::class)) {
212
            $method = $jsonFormat->method ?? 'get' . ucfirst($jsonFormat->property);
213
            $format = "$method()";
214
        } else {
215
            $stopRecursion = $this->findRemoteAnnotation(Annotation\JsonRecursive::class) ? '' : 'true';
216
            $format = "jsonSerialize($stopRecursion)";
217
        }
218
        $isIncluded = $this->findRemoteAnnotation(Annotation\JsonInclude::class) !== null;
219
        $jsonKey = $this->findRemoteAnnotation(Annotation\JsonKey::class);
220
        $index = $jsonKey ? $jsonKey->key : lcfirst($this->getPluralName());
221
        $class = $this->getBeanClassName();
222
        $getter = $this->getName();
223
        $code = <<<PHP
224
\$array['$index'] = array_map(function ($class \$object) {
225
    return \$object->$format;
226
}, \$this->$getter());
227
PHP;
228
        if (!$isIncluded) {
229
            $code = preg_replace('(\n)', '\0    ', $code);
230
            $code = <<<PHP
231
if (!\$stopRecursion) {
232
    $code
233
};
234
PHP;
235
        }
236
        return $code;
237
    }
238
239
    /**
240
     * @return Table
241
     */
242
    public function getPivotTable(): Table
243
    {
244
        return $this->pivotTable;
245
    }
246
247
    /**
248
     * @return ForeignKeyConstraint
249
     */
250
    public function getLocalFk(): ForeignKeyConstraint
251
    {
252
        return $this->localFk;
253
    }
254
255
    /**
256
     * @return ForeignKeyConstraint
257
     */
258
    public function getRemoteFk(): ForeignKeyConstraint
259
    {
260
        return $this->remoteFk;
261
    }
262
263
    private function findLocalAnnotation(string $type)
264
    {
265
        foreach ($this->getLocalAnnotations() as $annotations) {
266
            $annotation = $annotations->findAnnotation($type);
267
            if ($annotation !== null) {
268
                return $annotation;
269
            }
270
        }
271
        return null;
272
    }
273
274
    private function findRemoteAnnotation(string $type)
275
    {
276
        foreach ($this->getRemoteAnnotations() as $annotations) {
277
            $annotation = $annotations->findAnnotation($type);
278
            if ($annotation !== null) {
279
                return $annotation;
280
            }
281
        }
282
        return null;
283
    }
284
285
    private function getLocalAnnotations()
286
    {
287
        if ($this->localAnnotations === null) {
288
            $this->localAnnotations = $this->getFkAnnotations($this->localFk);
289
        }
290
        return $this->localAnnotations;
291
    }
292
293
    private function getRemoteAnnotations()
294
    {
295
        if ($this->remoteAnnotations === null) {
296
            $this->remoteAnnotations = $this->getFkAnnotations($this->remoteFk);
297
        }
298
        return $this->remoteAnnotations;
299
    }
300
301
    private function getFkAnnotations(ForeignKeyConstraint $fk)
302
    {
303
        $annotations = [];
304
        foreach ($this->getFkColumns($fk) as $column) {
305
            $annotations[] = $this->annotationParser->getColumnAnnotations($column, $fk->getLocalTable());
306
        }
307
        return $annotations;
308
    }
309
310
    /**
311
     * @param ForeignKeyConstraint $fk
312
     * @return Column[]
313
     */
314
    private function getFkColumns(ForeignKeyConstraint $fk)
315
    {
316
        $table = $fk->getLocalTable();
317
        $columns = [];
318
        foreach ($fk->getUnquotedLocalColumns() as $column) {
319
            $columns[] = $table->getColumn($column);
320
        }
321
        return $columns;
322
    }
323
}
324