Completed
Pull Request — 4.0 (#55)
by Huberty
05:04
created

BeanDescriptor::getConstructorProperties()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 4
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 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
     * Returns as an array the class we need to extend from and the list of use statements.
518
     *
519
     * @return array
520
     */
521
    private function generateExtendsAndUseStatements(ForeignKeyConstraint $parentFk = null)
522
    {
523
        $classes = [];
524
        if ($parentFk !== null) {
525
            $extends = TDBMDaoGenerator::getBeanNameFromTableName($parentFk->getForeignTableName());
526
            $classes[] = $extends;
527
        }
528
529
        foreach ($this->getBeanPropertyDescriptors() as $beanPropertyDescriptor) {
530
            $className = $beanPropertyDescriptor->getClassName();
531
            if (null !== $className) {
532
                $classes[] = $beanPropertyDescriptor->getClassName();
533
            }
534
        }
535
536
        foreach ($this->getPivotTableDescriptors() as $descriptor) {
537
            /* @var $fk ForeignKeyConstraint */
538
            $fk = $descriptor['remoteFK'];
539
            $classes[] = TDBMDaoGenerator::getBeanNameFromTableName($fk->getForeignTableName());
540
        }
541
542
        $classes = array_flip(array_flip($classes));
543
544
        return $classes;
545
    }
546
547
    /**
548
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
549
     *
550
     * @param string $beannamespace The namespace of the bean
551
     */
552
    public function generatePhpCode($beannamespace)
553
    {
554
        $tableName = $this->table->getName();
555
        $baseClassName = TDBMDaoGenerator::getBaseBeanNameFromTableName($tableName);
556
        $className = TDBMDaoGenerator::getBeanNameFromTableName($tableName);
557
        $parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);
558
559
        $classes = $this->generateExtendsAndUseStatements($parentFk);
560
561
        $uses = array_map(function ($className) use ($beannamespace) { return 'use '.$beannamespace.'\\'.$className.";\n"; }, $classes);
562
        $use = implode('', $uses);
563
564
        if ($parentFk !== null) {
565
            $extends = TDBMDaoGenerator::getBeanNameFromTableName($parentFk->getForeignTableName());
566
        } else {
567
            $extends = 'AbstractTDBMObject';
568
            $use .= "use Mouf\\Database\\TDBM\\AbstractTDBMObject;\n";
569
        }
570
571
        $str = "<?php
572
namespace {$beannamespace}\\Generated;
573
574
use Mouf\\Database\\TDBM\\ResultIterator;
575
$use
576
/*
577
 * This file has been automatically generated by TDBM.
578
 * DO NOT edit this file, as it might be overwritten.
579
 * If you need to perform changes, edit the $className class instead!
580
 */
581
582
/**
583
 * The $baseClassName class maps the '$tableName' table in database.
584
 */
585
class $baseClassName extends $extends implements \\JsonSerializable
586
{
587
";
588
589
        $str .= $this->generateBeanConstructor();
590
591
        foreach ($this->getExposedProperties() as $property) {
592
            $str .= $property->getGetterSetterCode();
593
        }
594
595
        $str .= $this->generateDirectForeignKeysCode();
596
        $str .= $this->generatePivotTableCode();
597
        $str .= $this->generateJsonSerialize();
598
599
        $str .= '}
600
';
601
602
        return $str;
603
    }
604
605
    /**
606
     * @param string $beanNamespace
607
     * @param string $beanClassName
608
     *
609
     * @return array first element: list of used beans, second item: PHP code as a string
610
     */
611
    public function generateFindByDaoCode($beanNamespace, $beanClassName)
612
    {
613
        $code = '';
614
        $usedBeans = [];
615
        foreach ($this->table->getIndexes() as $index) {
616
            if (!$index->isPrimary()) {
617
                list($usedBeansForIndex, $codeForIndex) = $this->generateFindByDaoCodeForIndex($index, $beanNamespace, $beanClassName);
618
                $code .= $codeForIndex;
619
                $usedBeans = array_merge($usedBeans, $usedBeansForIndex);
620
            }
621
        }
622
623
        return [$usedBeans, $code];
624
    }
625
626
    /**
627
     * @param Index  $index
628
     * @param string $beanNamespace
629
     * @param string $beanClassName
630
     *
631
     * @return array first element: list of used beans, second item: PHP code as a string
632
     */
633
    private function generateFindByDaoCodeForIndex(Index $index, $beanNamespace, $beanClassName)
634
    {
635
        $columns = $index->getColumns();
636
        $usedBeans = [];
637
638
        /*
639
         * The list of elements building this index (expressed as columns or foreign keys)
640
         * @var AbstractBeanPropertyDescriptor[]
641
         */
642
        $elements = [];
643
644
        foreach ($columns as $column) {
645
            $fk = $this->isPartOfForeignKey($this->table, $this->table->getColumn($column));
646
            if ($fk !== null) {
647
                if (!in_array($fk, $elements)) {
648
                    $elements[] = new ObjectBeanPropertyDescriptor($this->table, $fk, $this->schemaAnalyzer);
649
                }
650
            } else {
651
                $elements[] = new ScalarBeanPropertyDescriptor($this->table, $this->table->getColumn($column));
652
            }
653
        }
654
655
        // If the index is actually only a foreign key, let's bypass it entirely.
656
        if (count($elements) === 1 && $elements[0] instanceof ObjectBeanPropertyDescriptor) {
657
            return [[], ''];
658
        }
659
660
        $methodNameComponent = [];
661
        $functionParameters = [];
662
        $first = true;
663
        foreach ($elements as $element) {
664
            $methodNameComponent[] = $element->getUpperCamelCaseName();
665
            $functionParameter = $element->getClassName();
666
            if ($functionParameter) {
667
                $usedBeans[] = $beanNamespace.'\\'.$functionParameter;
668
                $functionParameter .= ' ';
669
            }
670
            $functionParameter .= $element->getVariableName();
671
            if ($first) {
672
                $first = false;
673
            } else {
674
                $functionParameter .= ' = null';
675
            }
676
            $functionParameters[] = $functionParameter;
677
        }
678
        if ($index->isUnique()) {
679
            $methodName = 'findOneBy'.implode('And', $methodNameComponent);
680
            $calledMethod = 'findOne';
681
        } else {
682
            $methodName = 'findBy'.implode('And', $methodNameComponent);
683
            $calledMethod = 'find';
684
        }
685
        $functionParametersString = implode(', ', $functionParameters);
686
687
        $count = 0;
688
689
        $params = [];
690
        $filterArrayCode = '';
691
        $commentArguments = [];
692
        foreach ($elements as $element) {
693
            $params[] = $element->getParamAnnotation();
694
            if ($element instanceof ScalarBeanPropertyDescriptor) {
695
                $filterArrayCode .= '            '.var_export($element->getColumnName(), true).' => '.$element->getVariableName().",\n";
696
            } else {
697
                ++$count;
698
                $filterArrayCode .= '            '.$count.' => '.$element->getVariableName().",\n";
699
            }
700
            $commentArguments[] = substr($element->getVariableName(), 1);
701
        }
702
        $paramsString = implode("\n", $params);
703
704
        $code = "
705
    /**
706
     * Get a list of $beanClassName filtered by ".implode(', ', $commentArguments).".
707
     *
708
$paramsString
709
     * @param mixed \$orderBy The order string
710
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
711
     * @param string \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
712
     * @return {$beanClassName}[]|ResultIterator|ResultArray
713
     */
714
    public function $methodName($functionParametersString, \$orderBy = null, array \$additionalTablesFetch = array(), \$mode = null)
715
    {
716
        \$filter = [
717
".$filterArrayCode."        ];
718
        return \$this->$calledMethod(\$filter, [], \$orderBy, \$additionalTablesFetch, \$mode);
719
    }
720
";
721
722
        return [$usedBeans, $code];
723
    }
724
}
725