Completed
Push — 4.0 ( 2e611e...21f434 )
by David
06:39
created

BeanDescriptor   D

Complexity

Total Complexity 71

Size/Duplication

Total Lines 673
Duplicated Lines 1.04 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 11
Bugs 0 Features 1
Metric Value
wmc 71
c 11
b 0
f 1
lcom 1
cbo 12
dl 7
loc 673
rs 4.908

18 Methods

Rating   Name   Duplication   Size   Complexity  
D getPropertiesForTable() 0 68 14
B generateBeanConstructor() 0 38 4
B generateDirectForeignKeysCode() 0 61 6
A getFilters() 0 21 2
A generatePivotTableCode() 0 12 2
C getPivotTableDescriptors() 0 40 7
A getPivotTableCode() 0 59 1
B generateJsonSerialize() 0 52 4
A generatePhpCode() 0 48 3
A __construct() 7 8 1
A initBeanPropertyDescriptors() 0 4 1
A isPartOfForeignKey() 0 13 4
A getBeanPropertyDescriptors() 0 4 1
A getConstructorProperties() 0 8 1
A getExposedProperties() 0 8 1
A getProperties() 0 21 4
A generateFindByDaoCode() 0 14 3
D generateFindByDaoCodeForIndex() 0 91 12

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BeanDescriptor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BeanDescriptor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Mouf\Database\TDBM\Utils;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Doctrine\DBAL\Schema\Index;
7
use Doctrine\DBAL\Schema\Schema;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
10
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
11
use Mouf\Database\TDBM\TDBMException;
12
use Mouf\Database\TDBM\TDBMSchemaAnalyzer;
13
14
/**
15
 * This class represents a bean.
16
 */
17
class BeanDescriptor
18
{
19
    /**
20
     * @var Table
21
     */
22
    private $table;
23
24
    /**
25
     * @var SchemaAnalyzer
26
     */
27
    private $schemaAnalyzer;
28
29
    /**
30
     * @var Schema
31
     */
32
    private $schema;
33
34
    /**
35
     * @var AbstractBeanPropertyDescriptor[]
36
     */
37
    private $beanPropertyDescriptors = [];
38
39
    /**
40
     * @var TDBMSchemaAnalyzer
41
     */
42
    private $tdbmSchemaAnalyzer;
43
44 View Code Duplication
    public function __construct(Table $table, SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
45
    {
46
        $this->table = $table;
47
        $this->schemaAnalyzer = $schemaAnalyzer;
48
        $this->schema = $schema;
49
        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
50
        $this->initBeanPropertyDescriptors();
51
    }
52
53
    private function initBeanPropertyDescriptors()
54
    {
55
        $this->beanPropertyDescriptors = $this->getProperties($this->table);
56
    }
57
58
    /**
59
     * Returns the foreign-key the column is part of, if any. null otherwise.
60
     *
61
     * @param Table  $table
62
     * @param Column $column
63
     *
64
     * @return ForeignKeyConstraint|null
65
     */
66
    private function isPartOfForeignKey(Table $table, Column $column)
67
    {
68
        $localColumnName = $column->getName();
69
        foreach ($table->getForeignKeys() as $foreignKey) {
70
            foreach ($foreignKey->getColumns() as $columnName) {
71
                if ($columnName === $localColumnName) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $columnName (integer) and $localColumnName (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
72
                    return $foreignKey;
73
                }
74
            }
75
        }
76
77
        return;
78
    }
79
80
    /**
81
     * @return AbstractBeanPropertyDescriptor[]
82
     */
83
    public function getBeanPropertyDescriptors()
84
    {
85
        return $this->beanPropertyDescriptors;
86
    }
87
88
    /**
89
     * Returns the list of columns that are not nullable and not autogenerated for a given table and its parent.
90
     *
91
     * @return AbstractBeanPropertyDescriptor[]
92
     */
93
    public function getConstructorProperties()
94
    {
95
        $constructorProperties = array_filter($this->beanPropertyDescriptors, function (AbstractBeanPropertyDescriptor $property) {
96
           return $property->isCompulsory();
97
        });
98
99
        return $constructorProperties;
100
    }
101
102
    /**
103
     * Returns the list of properties exposed as getters and setters in this class.
104
     *
105
     * @return AbstractBeanPropertyDescriptor[]
106
     */
107
    public function getExposedProperties()
108
    {
109
        $exposedProperties = array_filter($this->beanPropertyDescriptors, function (AbstractBeanPropertyDescriptor $property) {
110
            return $property->getTable()->getName() == $this->table->getName();
111
        });
112
113
        return $exposedProperties;
114
    }
115
116
    /**
117
     * Returns the list of properties for this table (including parent tables).
118
     *
119
     * @param Table $table
120
     *
121
     * @return AbstractBeanPropertyDescriptor[]
122
     */
123
    private function getProperties(Table $table)
124
    {
125
        $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
126
        if ($parentRelationship) {
127
            $parentTable = $this->schema->getTable($parentRelationship->getForeignTableName());
128
            $properties = $this->getProperties($parentTable);
129
            // we merge properties by overriding property names.
130
            $localProperties = $this->getPropertiesForTable($table);
131
            foreach ($localProperties as $name => $property) {
132
                // We do not override properties if this is a primary key!
133
                if ($property->isPrimaryKey()) {
134
                    continue;
135
                }
136
                $properties[$name] = $property;
137
            }
138
        } else {
139
            $properties = $this->getPropertiesForTable($table);
140
        }
141
142
        return $properties;
143
    }
144
145
    /**
146
     * Returns the list of properties for this table (ignoring parent tables).
147
     *
148
     * @param Table $table
149
     *
150
     * @return AbstractBeanPropertyDescriptor[]
151
     */
152
    private function getPropertiesForTable(Table $table)
153
    {
154
        $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
155
        if ($parentRelationship) {
156
            $ignoreColumns = $parentRelationship->getLocalColumns();
157
        } else {
158
            $ignoreColumns = [];
159
        }
160
161
        $beanPropertyDescriptors = [];
162
163
        foreach ($table->getColumns() as $column) {
164
            if (array_search($column->getName(), $ignoreColumns) !== false) {
165
                continue;
166
            }
167
168
            $fk = $this->isPartOfForeignKey($table, $column);
169
            if ($fk !== null) {
170
                // Check that previously added descriptors are not added on same FK (can happen with multi key FK).
171
                foreach ($beanPropertyDescriptors as $beanDescriptor) {
172
                    if ($beanDescriptor instanceof ObjectBeanPropertyDescriptor && $beanDescriptor->getForeignKey() === $fk) {
173
                        continue 2;
174
                    }
175
                }
176
                // Check that this property is not an inheritance relationship
177
                $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
178
                if ($parentRelationship === $fk) {
179
                    continue;
180
                }
181
182
                $beanPropertyDescriptors[] = new ObjectBeanPropertyDescriptor($table, $fk, $this->schemaAnalyzer);
183
            } else {
184
                $beanPropertyDescriptors[] = new ScalarBeanPropertyDescriptor($table, $column);
185
            }
186
        }
187
188
        // Now, let's get the name of all properties and let's check there is no duplicate.
189
        /** @var $names AbstractBeanPropertyDescriptor[] */
190
        $names = [];
191
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
192
            $name = $beanDescriptor->getUpperCamelCaseName();
193
            if (isset($names[$name])) {
194
                $names[$name]->useAlternativeName();
195
                $beanDescriptor->useAlternativeName();
196
            } else {
197
                $names[$name] = $beanDescriptor;
198
            }
199
        }
200
201
        // Final check (throw exceptions if problem arises)
202
        $names = [];
203
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
204
            $name = $beanDescriptor->getUpperCamelCaseName();
205
            if (isset($names[$name])) {
206
                throw new TDBMException('Unsolvable name conflict while generating method name');
207
            } else {
208
                $names[$name] = $beanDescriptor;
209
            }
210
        }
211
212
        // Last step, let's rebuild the list with a map:
213
        $beanPropertyDescriptorsMap = [];
214
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
215
            $beanPropertyDescriptorsMap[$beanDescriptor->getLowerCamelCaseName()] = $beanDescriptor;
216
        }
217
218
        return $beanPropertyDescriptorsMap;
219
    }
220
221
    public function generateBeanConstructor()
222
    {
223
        $constructorProperties = $this->getConstructorProperties();
224
225
        $constructorCode = '    /**
226
     * The constructor takes all compulsory arguments.
227
     *
228
%s
229
     */
230
    public function __construct(%s) {
231
%s%s
232
    }
233
    ';
234
235
        $paramAnnotations = [];
236
        $arguments = [];
237
        $assigns = [];
238
        $parentConstructorArguments = [];
239
240
        foreach ($constructorProperties as $property) {
241
            $className = $property->getClassName();
242
            if ($className) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
243
                $arguments[] = $className.' '.$property->getVariableName();
244
            } else {
245
                $arguments[] = $property->getVariableName();
246
            }
247
            $paramAnnotations[] = $property->getParamAnnotation();
248
            if ($property->getTable()->getName() === $this->table->getName()) {
249
                $assigns[] = $property->getConstructorAssignCode();
250
            } else {
251
                $parentConstructorArguments[] = $property->getVariableName();
252
            }
253
        }
254
255
        $parentConstrutorCode = sprintf("        parent::__construct(%s);\n", implode(', ', $parentConstructorArguments));
256
257
        return sprintf($constructorCode, implode("\n", $paramAnnotations), implode(', ', $arguments), $parentConstrutorCode, implode("\n", $assigns));
258
    }
259
260
    public function generateDirectForeignKeysCode()
261
    {
262
        $fks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($this->table->getName());
263
264
        $fksByTable = [];
265
266
        foreach ($fks as $fk) {
267
            $fksByTable[$fk->getLocalTableName()][] = $fk;
268
        }
269
270
        /* @var $fksByMethodName ForeignKeyConstraint[] */
271
        $fksByMethodName = [];
272
273
        foreach ($fksByTable as $tableName => $fksForTable) {
274
            if (count($fksForTable) > 1) {
275
                foreach ($fksForTable as $fk) {
276
                    $methodName = 'get'.TDBMDaoGenerator::toCamelCase($fk->getLocalTableName()).'By';
277
278
                    $camelizedColumns = array_map(['Mouf\\Database\\TDBM\\Utils\\TDBMDaoGenerator', 'toCamelCase'], $fk->getLocalColumns());
279
280
                    $methodName .= implode('And', $camelizedColumns);
281
282
                    $fksByMethodName[$methodName] = $fk;
283
                }
284
            } else {
285
                $methodName = 'get'.TDBMDaoGenerator::toCamelCase($fksForTable[0]->getLocalTableName());
286
                $fksByMethodName[$methodName] = $fksForTable[0];
287
            }
288
        }
289
290
        $code = '';
291
292
        foreach ($fksByMethodName as $methodName => $fk) {
293
            $getterCode = '    /**
294
     * Returns the list of %s pointing to this bean via the %s column.
295
     *
296
     * @return %s[]|ResultIterator
297
     */
298
    public function %s()
299
    {
300
        return $this->tdbmService->findObjects(%s, %s, %s);
301
    }
302
303
';
304
305
            list($sql, $parametersCode) = $this->getFilters($fk);
306
307
            $beanClass = TDBMDaoGenerator::getBeanNameFromTableName($fk->getLocalTableName());
308
            $code .= sprintf($getterCode,
309
                $beanClass,
310
                implode(', ', $fk->getColumns()),
311
                $beanClass,
312
                $methodName,
313
                var_export($fk->getLocalTableName(), true),
314
                $sql,
315
                $parametersCode
316
            );
317
        }
318
319
        return $code;
320
    }
321
322
    private function getFilters(ForeignKeyConstraint $fk)
323
    {
324
        $sqlParts = [];
325
        $counter = 0;
326
        $parameters = [];
327
328
        $pkColumns = $this->table->getPrimaryKeyColumns();
329
330
        foreach ($fk->getLocalColumns() as $columnName) {
331
            $paramName = 'tdbmparam'.$counter;
332
            $sqlParts[] = $fk->getLocalTableName().'.'.$columnName.' = :'.$paramName;
333
334
            $pkColumn = $pkColumns[$counter];
335
            $parameters[] = sprintf('%s => $this->get(%s, %s)', var_export($paramName, true), var_export($pkColumn, true), var_export($this->table->getName(), true));
336
            ++$counter;
337
        }
338
        $sql = "'".implode(' AND ', $sqlParts)."'";
339
        $parametersCode = '[ '.implode(', ', $parameters).' ]';
340
341
        return [$sql, $parametersCode];
342
    }
343
344
    /**
345
     * Generate code section about pivot tables.
346
     *
347
     * @return string
348
     */
349
    public function generatePivotTableCode()
350
    {
351
        $finalDescs = $this->getPivotTableDescriptors();
352
353
        $code = '';
354
355
        foreach ($finalDescs as $desc) {
356
            $code .= $this->getPivotTableCode($desc['name'], $desc['table'], $desc['localFK'], $desc['remoteFK']);
357
        }
358
359
        return $code;
360
    }
361
362
    private function getPivotTableDescriptors()
363
    {
364
        $descs = [];
365
        foreach ($this->schemaAnalyzer->detectJunctionTables() as $table) {
366
            // There are exactly 2 FKs since this is a pivot table.
367
            $fks = array_values($table->getForeignKeys());
368
369
            if ($fks[0]->getForeignTableName() === $this->table->getName()) {
370
                $localFK = $fks[0];
371
                $remoteFK = $fks[1];
372
            } elseif ($fks[1]->getForeignTableName() === $this->table->getName()) {
373
                $localFK = $fks[1];
374
                $remoteFK = $fks[0];
375
            } else {
376
                continue;
377
            }
378
379
            $descs[$remoteFK->getForeignTableName()][] = [
380
                'table' => $table,
381
                'localFK' => $localFK,
382
                'remoteFK' => $remoteFK,
383
            ];
384
        }
385
386
        $finalDescs = [];
387
        foreach ($descs as $descArray) {
388
            if (count($descArray) > 1) {
389
                foreach ($descArray as $desc) {
390
                    $desc['name'] = TDBMDaoGenerator::toCamelCase($desc['remoteFK']->getForeignTableName()).'By'.TDBMDaoGenerator::toCamelCase($desc['table']->getName());
391
                    $finalDescs[] = $desc;
392
                }
393
            } else {
394
                $desc = $descArray[0];
395
                $desc['name'] = TDBMDaoGenerator::toCamelCase($desc['remoteFK']->getForeignTableName());
396
                $finalDescs[] = $desc;
397
            }
398
        }
399
400
        return $finalDescs;
401
    }
402
403
    public function getPivotTableCode($name, Table $table, ForeignKeyConstraint $localFK, ForeignKeyConstraint $remoteFK)
0 ignored issues
show
Unused Code introduced by
The parameter $localFK is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
404
    {
405
        $singularName = TDBMDaoGenerator::toSingular($name);
406
        $remoteBeanName = TDBMDaoGenerator::getBeanNameFromTableName($remoteFK->getForeignTableName());
407
        $variableName = '$'.TDBMDaoGenerator::toVariableName($remoteBeanName);
408
409
        $str = '    /**
410
     * Returns the list of %s associated to this bean via the %s pivot table.
411
     *
412
     * @return %s[]
413
     */
414
    public function get%s() {
415
        return $this->_getRelationships(%s);
416
    }
417
';
418
419
        $getterCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $name, var_export($remoteFK->getLocalTableName(), true));
420
421
        $str = '    /**
422
     * Adds a relationship with %s associated to this bean via the %s pivot table.
423
     *
424
     * @param %s %s
425
     */
426
    public function add%s(%s %s) {
427
        return $this->addRelationship(%s, %s);
428
    }
429
';
430
431
        $adderCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
432
433
        $str = '    /**
434
     * Deletes the relationship with %s associated to this bean via the %s pivot table.
435
     *
436
     * @param %s %s
437
     */
438
    public function remove%s(%s %s) {
439
        return $this->_removeRelationship(%s, %s);
440
    }
441
';
442
443
        $removerCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
444
445
        $str = '    /**
446
     * Returns whether this bean is associated with %s via the %s pivot table.
447
     *
448
     * @param %s %s
449
     * @return bool
450
     */
451
    public function has%s(%s %s) {
452
        return $this->hasRelationship(%s, %s);
453
    }
454
';
455
456
        $hasCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
457
458
        $code = $getterCode.$adderCode.$removerCode.$hasCode;
459
460
        return $code;
461
    }
462
463
    public function generateJsonSerialize()
464
    {
465
        $tableName = $this->table->getName();
466
        $parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);
467
        if ($parentFk !== null) {
468
            $initializer = '$array = parent::jsonSerialize();';
469
        } else {
470
            $initializer = '$array = [];';
471
        }
472
473
        $str = '
474
    /**
475
     * Serializes the object for JSON encoding
476
     *
477
     * @param bool $stopRecursion Parameter used internally by TDBM to stop embedded objects from embedding other objects.
478
     * @return array
479
     */
480
    public function jsonSerialize($stopRecursion = false)
481
    {
482
        %s
483
%s
484
%s
485
        return $array;
486
    }
487
';
488
489
        $propertiesCode = '';
490
        foreach ($this->beanPropertyDescriptors as $beanPropertyDescriptor) {
491
            $propertiesCode .= $beanPropertyDescriptor->getJsonSerializeCode();
492
        }
493
494
        // Many to many relationships:
495
496
        $descs = $this->getPivotTableDescriptors();
497
498
        $many2manyCode = '';
499
500
        foreach ($descs as $desc) {
501
            $remoteFK = $desc['remoteFK'];
502
            $remoteBeanName = TDBMDaoGenerator::getBeanNameFromTableName($remoteFK->getForeignTableName());
503
            $variableName = '$'.TDBMDaoGenerator::toVariableName($remoteBeanName);
504
505
            $many2manyCode .= '        if (!$stopRecursion) {
506
            $array[\''.lcfirst($desc['name']).'\'] = array_map(function('.$remoteBeanName.' '.$variableName.') {
507
                return '.$variableName.'->jsonSerialize(true);
508
            }, $this->get'.$desc['name'].'());
509
        }
510
        ';
511
        }
512
513
        return sprintf($str, $initializer, $propertiesCode, $many2manyCode);
514
    }
515
516
    /**
517
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
518
     *
519
     * @param string $beannamespace The namespace of the bean
520
     */
521
    public function generatePhpCode($beannamespace)
522
    {
523
        $baseClassName = TDBMDaoGenerator::getBaseBeanNameFromTableName($this->table->getName());
524
        $className = TDBMDaoGenerator::getBeanNameFromTableName($this->table->getName());
525
        $tableName = $this->table->getName();
526
527
        $parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);
528
        if ($parentFk !== null) {
529
            $extends = TDBMDaoGenerator::getBeanNameFromTableName($parentFk->getForeignTableName());
530
            $use = '';
531
        } else {
532
            $extends = 'AbstractTDBMObject';
533
            $use = "use Mouf\\Database\\TDBM\\AbstractTDBMObject;\n\n";
534
        }
535
536
        $str = "<?php
537
namespace {$beannamespace};
538
539
use Mouf\\Database\\TDBM\\ResultIterator;
540
$use
541
/*
542
 * This file has been automatically generated by TDBM.
543
 * DO NOT edit this file, as it might be overwritten.
544
 * If you need to perform changes, edit the $className class instead!
545
 */
546
547
/**
548
 * The $baseClassName class maps the '$tableName' table in database.
549
 */
550
class $baseClassName extends $extends implements \\JsonSerializable
551
{
552
";
553
554
        $str .= $this->generateBeanConstructor();
555
556
        foreach ($this->getExposedProperties() as $property) {
557
            $str .= $property->getGetterSetterCode();
558
        }
559
560
        $str .= $this->generateDirectForeignKeysCode();
561
        $str .= $this->generatePivotTableCode();
562
        $str .= $this->generateJsonSerialize();
563
564
        $str .= '}
565
';
566
567
        return $str;
568
    }
569
570
    /**
571
     * @param string $beanNamespace
572
     * @param string $beanClassName
573
     *
574
     * @return array first element: list of used beans, second item: PHP code as a string
575
     */
576
    public function generateFindByDaoCode($beanNamespace, $beanClassName)
577
    {
578
        $code = '';
579
        $usedBeans = [];
580
        foreach ($this->table->getIndexes() as $index) {
581
            if (!$index->isPrimary()) {
582
                list($usedBeansForIndex, $codeForIndex) = $this->generateFindByDaoCodeForIndex($index, $beanNamespace, $beanClassName);
583
                $code .= $codeForIndex;
584
                $usedBeans = array_merge($usedBeans, $usedBeansForIndex);
585
            }
586
        }
587
588
        return [$usedBeans, $code];
589
    }
590
591
    /**
592
     * @param Index  $index
593
     * @param string $beanNamespace
594
     * @param string $beanClassName
595
     *
596
     * @return array first element: list of used beans, second item: PHP code as a string
597
     */
598
    private function generateFindByDaoCodeForIndex(Index $index, $beanNamespace, $beanClassName)
599
    {
600
        $columns = $index->getColumns();
601
        $usedBeans = [];
602
603
        /*
604
         * The list of elements building this index (expressed as columns or foreign keys)
605
         * @var AbstractBeanPropertyDescriptor[]
606
         */
607
        $elements = [];
608
609
        foreach ($columns as $column) {
610
            $fk = $this->isPartOfForeignKey($this->table, $this->table->getColumn($column));
611
            if ($fk !== null) {
612
                if (!in_array($fk, $elements)) {
613
                    $elements[] = new ObjectBeanPropertyDescriptor($this->table, $fk, $this->schemaAnalyzer);
614
                }
615
            } else {
616
                $elements[] = new ScalarBeanPropertyDescriptor($this->table, $this->table->getColumn($column));
617
            }
618
        }
619
620
        // If the index is actually only a foreign key, let's bypass it entirely.
621
        if (count($elements) === 1 && $elements[0] instanceof ObjectBeanPropertyDescriptor) {
622
            return [[], ''];
623
        }
624
625
        $methodNameComponent = [];
626
        $functionParameters = [];
627
        $first = true;
628
        foreach ($elements as $element) {
629
            $methodNameComponent[] = $element->getUpperCamelCaseName();
630
            $functionParameter = $element->getClassName();
631
            if ($functionParameter) {
632
                $usedBeans[] = $beanNamespace.'\\'.$functionParameter;
633
                $functionParameter .= ' ';
634
            }
635
            $functionParameter .= $element->getVariableName();
636
            if ($first) {
637
                $first = false;
638
            } else {
639
                $functionParameter .= ' = null';
640
            }
641
            $functionParameters[] = $functionParameter;
642
        }
643
        if ($index->isUnique()) {
644
            $methodName = 'findOneBy'.implode('And', $methodNameComponent);
645
            $calledMethod = 'findOne';
646
        } else {
647
            $methodName = 'findBy'.implode('And', $methodNameComponent);
648
            $calledMethod = 'find';
649
        }
650
        $functionParametersString = implode(', ', $functionParameters);
651
652
        $count = 0;
653
654
        $params = [];
655
        $filterArrayCode = '';
656
        $commentArguments[] = '';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$commentArguments was never initialized. Although not strictly required by PHP, it is generally a good practice to add $commentArguments = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
657
        foreach ($elements as $element) {
658
            $params[] = $element->getParamAnnotation();
659
            if ($element instanceof ScalarBeanPropertyDescriptor) {
660
                $filterArrayCode .= '            '.var_export($element->getColumnName(), true).' => '.$element->getVariableName().",\n";
661
            } else {
662
                ++$count;
663
                $filterArrayCode .= '            '.$count.' => '.$element->getVariableName().",\n";
664
            }
665
            $commentArguments[] = substr($element->getVariableName(), 1);
666
        }
667
        $paramsString = implode("\n", $params);
668
669
        $code = "
670
    /**
671
     * Get a list of $beanClassName filtered by ".implode(', ', $commentArguments).".
672
     *
673
$paramsString
674
     * @param mixed \$orderBy The order string
675
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
676
     * @param string \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
677
     * @return {$beanClassName}[]|ResultIterator|ResultArray
678
     */
679
    public function $methodName($functionParametersString, \$orderBy = null, array \$additionalTablesFetch = array(), \$mode = null)
680
    {
681
        \$filter = [
682
".$filterArrayCode."        ];
683
        return \$this->$calledMethod(\$filter, [], \$orderBy, \$additionalTablesFetch, \$mode);
684
    }
685
";
686
687
        return [$usedBeans, $code];
688
    }
689
}
690