Completed
Pull Request — 4.0 (#66)
by David
05:19
created

BeanDescriptor::getPropertiesWithDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 5
nc 1
nop 0
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
    public function __construct(Table $table, SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
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 columns that have default values for a given table.
104
     *
105
     * @return AbstractBeanPropertyDescriptor[]
106
     */
107
    public function getPropertiesWithDefault()
108
    {
109
        $properties = $this->getPropertiesForTable($this->table);
110
        $defaultProperties = array_filter($properties, function (AbstractBeanPropertyDescriptor $property) {
111
            return $property->hasDefault();
112
        });
113
114
        return $defaultProperties;
115
    }
116
117
    /**
118
     * Returns the list of properties exposed as getters and setters in this class.
119
     *
120
     * @return AbstractBeanPropertyDescriptor[]
121
     */
122
    public function getExposedProperties()
123
    {
124
        $exposedProperties = array_filter($this->beanPropertyDescriptors, function (AbstractBeanPropertyDescriptor $property) {
125
            return $property->getTable()->getName() == $this->table->getName();
126
        });
127
128
        return $exposedProperties;
129
    }
130
131
    /**
132
     * Returns the list of properties for this table (including parent tables).
133
     *
134
     * @param Table $table
135
     *
136
     * @return AbstractBeanPropertyDescriptor[]
137
     */
138
    private function getProperties(Table $table)
139
    {
140
        $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
141
        if ($parentRelationship) {
142
            $parentTable = $this->schema->getTable($parentRelationship->getForeignTableName());
143
            $properties = $this->getProperties($parentTable);
144
            // we merge properties by overriding property names.
145
            $localProperties = $this->getPropertiesForTable($table);
146
            foreach ($localProperties as $name => $property) {
147
                // We do not override properties if this is a primary key!
148
                if ($property->isPrimaryKey()) {
149
                    continue;
150
                }
151
                $properties[$name] = $property;
152
            }
153
        } else {
154
            $properties = $this->getPropertiesForTable($table);
155
        }
156
157
        return $properties;
158
    }
159
160
    /**
161
     * Returns the list of properties for this table (ignoring parent tables).
162
     *
163
     * @param Table $table
164
     *
165
     * @return AbstractBeanPropertyDescriptor[]
166
     */
167
    private function getPropertiesForTable(Table $table)
168
    {
169
        $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
170
        if ($parentRelationship) {
171
            $ignoreColumns = $parentRelationship->getLocalColumns();
172
        } else {
173
            $ignoreColumns = [];
174
        }
175
176
        $beanPropertyDescriptors = [];
177
178
        foreach ($table->getColumns() as $column) {
179
            if (array_search($column->getName(), $ignoreColumns) !== false) {
180
                continue;
181
            }
182
183
            $fk = $this->isPartOfForeignKey($table, $column);
184
            if ($fk !== null) {
185
                // Check that previously added descriptors are not added on same FK (can happen with multi key FK).
186
                foreach ($beanPropertyDescriptors as $beanDescriptor) {
187
                    if ($beanDescriptor instanceof ObjectBeanPropertyDescriptor && $beanDescriptor->getForeignKey() === $fk) {
188
                        continue 2;
189
                    }
190
                }
191
                // Check that this property is not an inheritance relationship
192
                $parentRelationship = $this->schemaAnalyzer->getParentRelationship($table->getName());
193
                if ($parentRelationship === $fk) {
194
                    continue;
195
                }
196
197
                $beanPropertyDescriptors[] = new ObjectBeanPropertyDescriptor($table, $fk, $this->schemaAnalyzer);
198
            } else {
199
                $beanPropertyDescriptors[] = new ScalarBeanPropertyDescriptor($table, $column);
200
            }
201
        }
202
203
        // Now, let's get the name of all properties and let's check there is no duplicate.
204
        /** @var $names AbstractBeanPropertyDescriptor[] */
205
        $names = [];
206
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
207
            $name = $beanDescriptor->getUpperCamelCaseName();
208
            if (isset($names[$name])) {
209
                $names[$name]->useAlternativeName();
210
                $beanDescriptor->useAlternativeName();
211
            } else {
212
                $names[$name] = $beanDescriptor;
213
            }
214
        }
215
216
        // Final check (throw exceptions if problem arises)
217
        $names = [];
218
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
219
            $name = $beanDescriptor->getUpperCamelCaseName();
220
            if (isset($names[$name])) {
221
                throw new TDBMException('Unsolvable name conflict while generating method name');
222
            } else {
223
                $names[$name] = $beanDescriptor;
224
            }
225
        }
226
227
        // Last step, let's rebuild the list with a map:
228
        $beanPropertyDescriptorsMap = [];
229
        foreach ($beanPropertyDescriptors as $beanDescriptor) {
230
            $beanPropertyDescriptorsMap[$beanDescriptor->getLowerCamelCaseName()] = $beanDescriptor;
231
        }
232
233
        return $beanPropertyDescriptorsMap;
234
    }
235
236
    public function generateBeanConstructor()
237
    {
238
        $constructorProperties = $this->getConstructorProperties();
239
240
        $constructorCode = '    /**
241
     * The constructor takes all compulsory arguments.
242
     *
243
%s
244
     */
245
    public function __construct(%s) {
246
%s%s
247
%s    }
248
    ';
249
250
        $paramAnnotations = [];
251
        $arguments = [];
252
        $assigns = [];
253
        $parentConstructorArguments = [];
254
255
        foreach ($constructorProperties as $property) {
256
            $className = $property->getClassName();
257
            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...
258
                $arguments[] = $className.' '.$property->getVariableName();
259
            } else {
260
                $arguments[] = $property->getVariableName();
261
            }
262
            $paramAnnotations[] = $property->getParamAnnotation();
263
            if ($property->getTable()->getName() === $this->table->getName()) {
264
                $assigns[] = $property->getConstructorAssignCode();
265
            } else {
266
                $parentConstructorArguments[] = $property->getVariableName();
267
            }
268
        }
269
270
        $parentConstructorCode = sprintf("        parent::__construct(%s);\n", implode(', ', $parentConstructorArguments));
271
272
        $defaultAssigns = [];
273
        foreach ($this->getPropertiesWithDefault() as $property) {
274
            $defaultAssigns[] = $property->assignToDefaultCode();
275
        }
276
277
        return sprintf($constructorCode, implode("\n", $paramAnnotations), implode(', ', $arguments), $parentConstructorCode, implode("\n", $assigns), implode("\n", $defaultAssigns));
278
    }
279
280
    public function generateDirectForeignKeysCode()
281
    {
282
        $fks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($this->table->getName());
283
284
        $fksByTable = [];
285
286
        foreach ($fks as $fk) {
287
            $fksByTable[$fk->getLocalTableName()][] = $fk;
288
        }
289
290
        /* @var $fksByMethodName ForeignKeyConstraint[] */
291
        $fksByMethodName = [];
292
293
        foreach ($fksByTable as $tableName => $fksForTable) {
294
            if (count($fksForTable) > 1) {
295
                foreach ($fksForTable as $fk) {
296
                    $methodName = 'get'.TDBMDaoGenerator::toCamelCase($fk->getLocalTableName()).'By';
297
298
                    $camelizedColumns = array_map(['Mouf\\Database\\TDBM\\Utils\\TDBMDaoGenerator', 'toCamelCase'], $fk->getLocalColumns());
299
300
                    $methodName .= implode('And', $camelizedColumns);
301
302
                    $fksByMethodName[$methodName] = $fk;
303
                }
304
            } else {
305
                $methodName = 'get'.TDBMDaoGenerator::toCamelCase($fksForTable[0]->getLocalTableName());
306
                $fksByMethodName[$methodName] = $fksForTable[0];
307
            }
308
        }
309
310
        $code = '';
311
312
        foreach ($fksByMethodName as $methodName => $fk) {
313
            $getterCode = '    /**
314
     * Returns the list of %s pointing to this bean via the %s column.
315
     *
316
     * @return %s[]|ResultIterator
317
     */
318
    public function %s()
319
    {
320
        return $this->tdbmService->findObjects(%s, %s, %s);
321
    }
322
323
';
324
325
            list($sql, $parametersCode) = $this->getFilters($fk);
326
327
            $beanClass = TDBMDaoGenerator::getBeanNameFromTableName($fk->getLocalTableName());
328
            $code .= sprintf($getterCode,
329
                $beanClass,
330
                implode(', ', $fk->getColumns()),
331
                $beanClass,
332
                $methodName,
333
                var_export($fk->getLocalTableName(), true),
334
                $sql,
335
                $parametersCode
336
            );
337
        }
338
339
        return $code;
340
    }
341
342
    private function getFilters(ForeignKeyConstraint $fk)
343
    {
344
        $sqlParts = [];
345
        $counter = 0;
346
        $parameters = [];
347
348
        $pkColumns = $this->table->getPrimaryKeyColumns();
349
350
        foreach ($fk->getLocalColumns() as $columnName) {
351
            $paramName = 'tdbmparam'.$counter;
352
            $sqlParts[] = $fk->getLocalTableName().'.'.$columnName.' = :'.$paramName;
353
354
            $pkColumn = $pkColumns[$counter];
355
            $parameters[] = sprintf('%s => $this->get(%s, %s)', var_export($paramName, true), var_export($pkColumn, true), var_export($this->table->getName(), true));
356
            ++$counter;
357
        }
358
        $sql = "'".implode(' AND ', $sqlParts)."'";
359
        $parametersCode = '[ '.implode(', ', $parameters).' ]';
360
361
        return [$sql, $parametersCode];
362
    }
363
364
    /**
365
     * Generate code section about pivot tables.
366
     *
367
     * @return string
368
     */
369
    public function generatePivotTableCode()
370
    {
371
        $finalDescs = $this->getPivotTableDescriptors();
372
373
        $code = '';
374
375
        foreach ($finalDescs as $desc) {
376
            $code .= $this->getPivotTableCode($desc['name'], $desc['table'], $desc['localFK'], $desc['remoteFK']);
377
        }
378
379
        return $code;
380
    }
381
382
    private function getPivotTableDescriptors()
383
    {
384
        $descs = [];
385
        foreach ($this->schemaAnalyzer->detectJunctionTables() as $table) {
386
            // There are exactly 2 FKs since this is a pivot table.
387
            $fks = array_values($table->getForeignKeys());
388
389
            if ($fks[0]->getForeignTableName() === $this->table->getName()) {
390
                $localFK = $fks[0];
391
                $remoteFK = $fks[1];
392
            } elseif ($fks[1]->getForeignTableName() === $this->table->getName()) {
393
                $localFK = $fks[1];
394
                $remoteFK = $fks[0];
395
            } else {
396
                continue;
397
            }
398
399
            $descs[$remoteFK->getForeignTableName()][] = [
400
                'table' => $table,
401
                'localFK' => $localFK,
402
                'remoteFK' => $remoteFK,
403
            ];
404
        }
405
406
        $finalDescs = [];
407
        foreach ($descs as $descArray) {
408
            if (count($descArray) > 1) {
409
                foreach ($descArray as $desc) {
410
                    $desc['name'] = TDBMDaoGenerator::toCamelCase($desc['remoteFK']->getForeignTableName()).'By'.TDBMDaoGenerator::toCamelCase($desc['table']->getName());
411
                    $finalDescs[] = $desc;
412
                }
413
            } else {
414
                $desc = $descArray[0];
415
                $desc['name'] = TDBMDaoGenerator::toCamelCase($desc['remoteFK']->getForeignTableName());
416
                $finalDescs[] = $desc;
417
            }
418
        }
419
420
        return $finalDescs;
421
    }
422
423
    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...
424
    {
425
        $singularName = TDBMDaoGenerator::toSingular($name);
426
        $remoteBeanName = TDBMDaoGenerator::getBeanNameFromTableName($remoteFK->getForeignTableName());
427
        $variableName = '$'.TDBMDaoGenerator::toVariableName($remoteBeanName);
428
429
        $str = '    /**
430
     * Returns the list of %s associated to this bean via the %s pivot table.
431
     *
432
     * @return %s[]
433
     */
434
    public function get%s() {
435
        return $this->_getRelationships(%s);
436
    }
437
';
438
439
        $getterCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $name, var_export($remoteFK->getLocalTableName(), true));
440
441
        $str = '    /**
442
     * Adds a relationship with %s associated to this bean via the %s pivot table.
443
     *
444
     * @param %s %s
445
     */
446
    public function add%s(%s %s) {
447
        return $this->addRelationship(%s, %s);
448
    }
449
';
450
451
        $adderCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
452
453
        $str = '    /**
454
     * Deletes the relationship with %s associated to this bean via the %s pivot table.
455
     *
456
     * @param %s %s
457
     */
458
    public function remove%s(%s %s) {
459
        return $this->_removeRelationship(%s, %s);
460
    }
461
';
462
463
        $removerCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
464
465
        $str = '    /**
466
     * Returns whether this bean is associated with %s via the %s pivot table.
467
     *
468
     * @param %s %s
469
     * @return bool
470
     */
471
    public function has%s(%s %s) {
472
        return $this->hasRelationship(%s, %s);
473
    }
474
';
475
476
        $hasCode = sprintf($str, $remoteBeanName, $table->getName(), $remoteBeanName, $variableName, $singularName, $remoteBeanName, $variableName, var_export($remoteFK->getLocalTableName(), true), $variableName);
477
478
        $code = $getterCode.$adderCode.$removerCode.$hasCode;
479
480
        return $code;
481
    }
482
483
    public function generateJsonSerialize()
484
    {
485
        $tableName = $this->table->getName();
486
        $parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);
487
        if ($parentFk !== null) {
488
            $initializer = '$array = parent::jsonSerialize();';
489
        } else {
490
            $initializer = '$array = [];';
491
        }
492
493
        $str = '
494
    /**
495
     * Serializes the object for JSON encoding
496
     *
497
     * @param bool $stopRecursion Parameter used internally by TDBM to stop embedded objects from embedding other objects.
498
     * @return array
499
     */
500
    public function jsonSerialize($stopRecursion = false)
501
    {
502
        %s
503
%s
504
%s
505
        return $array;
506
    }
507
';
508
509
        $propertiesCode = '';
510
        foreach ($this->beanPropertyDescriptors as $beanPropertyDescriptor) {
511
            $propertiesCode .= $beanPropertyDescriptor->getJsonSerializeCode();
512
        }
513
514
        // Many to many relationships:
515
516
        $descs = $this->getPivotTableDescriptors();
517
518
        $many2manyCode = '';
519
520
        foreach ($descs as $desc) {
521
            $remoteFK = $desc['remoteFK'];
522
            $remoteBeanName = TDBMDaoGenerator::getBeanNameFromTableName($remoteFK->getForeignTableName());
523
            $variableName = '$'.TDBMDaoGenerator::toVariableName($remoteBeanName);
524
525
            $many2manyCode .= '        if (!$stopRecursion) {
526
            $array[\''.lcfirst($desc['name']).'\'] = array_map(function('.$remoteBeanName.' '.$variableName.') {
527
                return '.$variableName.'->jsonSerialize(true);
528
            }, $this->get'.$desc['name'].'());
529
        }
530
        ';
531
        }
532
533
        return sprintf($str, $initializer, $propertiesCode, $many2manyCode);
534
    }
535
536
    /**
537
     * Returns as an array the class we need to extend from and the list of use statements.
538
     *
539
     * @return array
540
     */
541
    private function generateExtendsAndUseStatements(ForeignKeyConstraint $parentFk = null)
542
    {
543
        $classes = [];
544
        if ($parentFk !== null) {
545
            $extends = TDBMDaoGenerator::getBeanNameFromTableName($parentFk->getForeignTableName());
546
            $classes[] = $extends;
547
        }
548
549
        foreach ($this->getBeanPropertyDescriptors() as $beanPropertyDescriptor) {
550
            $className = $beanPropertyDescriptor->getClassName();
551
            if (null !== $className) {
552
                $classes[] = $beanPropertyDescriptor->getClassName();
553
            }
554
        }
555
556
        foreach ($this->getPivotTableDescriptors() as $descriptor) {
557
            /* @var $fk ForeignKeyConstraint */
558
            $fk = $descriptor['remoteFK'];
559
            $classes[] = TDBMDaoGenerator::getBeanNameFromTableName($fk->getForeignTableName());
560
        }
561
562
        $classes = array_flip(array_flip($classes));
563
564
        return $classes;
565
    }
566
567
    /**
568
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
569
     *
570
     * @param string $beannamespace The namespace of the bean
571
     */
572
    public function generatePhpCode($beannamespace)
573
    {
574
        $tableName = $this->table->getName();
575
        $baseClassName = TDBMDaoGenerator::getBaseBeanNameFromTableName($tableName);
576
        $className = TDBMDaoGenerator::getBeanNameFromTableName($tableName);
577
        $parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);
578
579
        $classes = $this->generateExtendsAndUseStatements($parentFk);
580
581
        $uses = array_map(function ($className) use ($beannamespace) { return 'use '.$beannamespace.'\\'.$className.";\n"; }, $classes);
582
        $use = implode('', $uses);
583
584
        if ($parentFk !== null) {
585
            $extends = TDBMDaoGenerator::getBeanNameFromTableName($parentFk->getForeignTableName());
586
        } else {
587
            $extends = 'AbstractTDBMObject';
588
            $use .= "use Mouf\\Database\\TDBM\\AbstractTDBMObject;\n";
589
        }
590
591
        $str = "<?php
592
namespace {$beannamespace}\\Generated;
593
594
use Mouf\\Database\\TDBM\\ResultIterator;
595
$use
596
/*
597
 * This file has been automatically generated by TDBM.
598
 * DO NOT edit this file, as it might be overwritten.
599
 * If you need to perform changes, edit the $className class instead!
600
 */
601
602
/**
603
 * The $baseClassName class maps the '$tableName' table in database.
604
 */
605
class $baseClassName extends $extends implements \\JsonSerializable
606
{
607
";
608
609
        $str .= $this->generateBeanConstructor();
610
611
        foreach ($this->getExposedProperties() as $property) {
612
            $str .= $property->getGetterSetterCode();
613
        }
614
615
        $str .= $this->generateDirectForeignKeysCode();
616
        $str .= $this->generatePivotTableCode();
617
        $str .= $this->generateJsonSerialize();
618
619
        $str .= '}
620
';
621
622
        return $str;
623
    }
624
625
    /**
626
     * @param string $beanNamespace
627
     * @param string $beanClassName
628
     *
629
     * @return array first element: list of used beans, second item: PHP code as a string
630
     */
631
    public function generateFindByDaoCode($beanNamespace, $beanClassName)
632
    {
633
        $code = '';
634
        $usedBeans = [];
635
        foreach ($this->table->getIndexes() as $index) {
636
            if (!$index->isPrimary()) {
637
                list($usedBeansForIndex, $codeForIndex) = $this->generateFindByDaoCodeForIndex($index, $beanNamespace, $beanClassName);
638
                $code .= $codeForIndex;
639
                $usedBeans = array_merge($usedBeans, $usedBeansForIndex);
640
            }
641
        }
642
643
        return [$usedBeans, $code];
644
    }
645
646
    /**
647
     * @param Index  $index
648
     * @param string $beanNamespace
649
     * @param string $beanClassName
650
     *
651
     * @return array first element: list of used beans, second item: PHP code as a string
652
     */
653
    private function generateFindByDaoCodeForIndex(Index $index, $beanNamespace, $beanClassName)
654
    {
655
        $columns = $index->getColumns();
656
        $usedBeans = [];
657
658
        /*
659
         * The list of elements building this index (expressed as columns or foreign keys)
660
         * @var AbstractBeanPropertyDescriptor[]
661
         */
662
        $elements = [];
663
664
        foreach ($columns as $column) {
665
            $fk = $this->isPartOfForeignKey($this->table, $this->table->getColumn($column));
666
            if ($fk !== null) {
667
                if (!in_array($fk, $elements)) {
668
                    $elements[] = new ObjectBeanPropertyDescriptor($this->table, $fk, $this->schemaAnalyzer);
669
                }
670
            } else {
671
                $elements[] = new ScalarBeanPropertyDescriptor($this->table, $this->table->getColumn($column));
672
            }
673
        }
674
675
        // If the index is actually only a foreign key, let's bypass it entirely.
676
        if (count($elements) === 1 && $elements[0] instanceof ObjectBeanPropertyDescriptor) {
677
            return [[], ''];
678
        }
679
680
        $methodNameComponent = [];
681
        $functionParameters = [];
682
        $first = true;
683
        foreach ($elements as $element) {
684
            $methodNameComponent[] = $element->getUpperCamelCaseName();
685
            $functionParameter = $element->getClassName();
686
            if ($functionParameter) {
687
                $usedBeans[] = $beanNamespace.'\\'.$functionParameter;
688
                $functionParameter .= ' ';
689
            }
690
            $functionParameter .= $element->getVariableName();
691
            if ($first) {
692
                $first = false;
693
            } else {
694
                $functionParameter .= ' = null';
695
            }
696
            $functionParameters[] = $functionParameter;
697
        }
698
        if ($index->isUnique()) {
699
            $methodName = 'findOneBy'.implode('And', $methodNameComponent);
700
            $calledMethod = 'findOne';
701
        } else {
702
            $methodName = 'findBy'.implode('And', $methodNameComponent);
703
            $calledMethod = 'find';
704
        }
705
        $functionParametersString = implode(', ', $functionParameters);
706
707
        $count = 0;
708
709
        $params = [];
710
        $filterArrayCode = '';
711
        $commentArguments = [];
712
        foreach ($elements as $element) {
713
            $params[] = $element->getParamAnnotation();
714
            if ($element instanceof ScalarBeanPropertyDescriptor) {
715
                $filterArrayCode .= '            '.var_export($element->getColumnName(), true).' => '.$element->getVariableName().",\n";
716
            } else {
717
                ++$count;
718
                $filterArrayCode .= '            '.$count.' => '.$element->getVariableName().",\n";
719
            }
720
            $commentArguments[] = substr($element->getVariableName(), 1);
721
        }
722
        $paramsString = implode("\n", $params);
723
724
        $code = "
725
    /**
726
     * Get a list of $beanClassName filtered by ".implode(', ', $commentArguments).".
727
     *
728
$paramsString
729
     * @param mixed \$orderBy The order string
730
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
731
     * @param string \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
732
     * @return {$beanClassName}[]|ResultIterator|ResultArray
733
     */
734
    public function $methodName($functionParametersString, \$orderBy = null, array \$additionalTablesFetch = array(), \$mode = null)
735
    {
736
        \$filter = [
737
".$filterArrayCode."        ];
738
        return \$this->$calledMethod(\$filter, [], \$orderBy, \$additionalTablesFetch, \$mode);
739
    }
740
";
741
742
        return [$usedBeans, $code];
743
    }
744
}
745