Completed
Push — 4.0 ( b0bc74...f44cfe )
by David
04:47
created

TDBMDaoGenerator   D

Complexity

Total Complexity 43

Size/Duplication

Total Lines 703
Duplicated Lines 6.69 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 40
Bugs 5 Features 6
Metric Value
wmc 43
c 40
b 5
f 6
lcom 1
cbo 9
dl 47
loc 703
rs 4.923

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B generateAllDaosAndBeans() 0 28 2
A generateDaoAndBean() 0 12 1
A getBeanNameFromTableName() 0 4 1
A getDaoNameFromTableName() 0 4 1
A getBaseBeanNameFromTableName() 0 4 1
A getBaseDaoNameFromTableName() 0 4 1
B generateBean() 23 45 4
A getDefaultSortColumnFromAnnotation() 0 19 4
C generateDao() 24 225 8
B generateFactory() 0 74 4
B toCamelCase() 0 19 5
A toSingular() 0 4 1
A toVariableName() 0 4 1
A ensureDirectoryExist() 0 12 3
A setComposerFile() 0 5 1
B dbalTypeToPhpType() 0 26 2
A buildTableToBeanMap() 0 13 2

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 TDBMDaoGenerator 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 TDBMDaoGenerator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Mouf\Database\TDBM\Utils;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\Schema;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\DBAL\Types\Type;
10
use Mouf\Composer\ClassNameMapper;
11
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
12
use Mouf\Database\TDBM\TDBMException;
13
use Mouf\Database\TDBM\TDBMSchemaAnalyzer;
14
15
/**
16
 * This class generates automatically DAOs and Beans for TDBM.
17
 */
18
class TDBMDaoGenerator
19
{
20
    /**
21
     * @var SchemaAnalyzer
22
     */
23
    private $schemaAnalyzer;
24
25
    /**
26
     * @var Schema
27
     */
28
    private $schema;
29
30
    /**
31
     * The root directory of the project.
32
     *
33
     * @var string
34
     */
35
    private $rootPath;
36
37
    /**
38
     * Name of composer file.
39
     *
40
     * @var string
41
     */
42
    private $composerFile;
43
44
    /**
45
     * @var TDBMSchemaAnalyzer
46
     */
47
    private $tdbmSchemaAnalyzer;
48
49
    /**
50
     * Constructor.
51
     *
52
     * @param SchemaAnalyzer     $schemaAnalyzer
53
     * @param Schema             $schema
54
     * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer
55
     */
56
    public function __construct(SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
57
    {
58
        $this->schemaAnalyzer = $schemaAnalyzer;
59
        $this->schema = $schema;
60
        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
61
        $this->rootPath = __DIR__.'/../../../../../../../../';
62
        $this->composerFile = 'composer.json';
63
    }
64
65
    /**
66
     * Generates all the daos and beans.
67
     *
68
     * @param string $daoFactoryClassName The classe name of the DAO factory
69
     * @param string $daonamespace        The namespace for the DAOs, without trailing \
70
     * @param string $beannamespace       The Namespace for the beans, without trailing \
71
     * @param bool   $storeInUtc          If the generated daos should store the date in UTC timezone instead of user's timezone.
72
     *
73
     * @return \string[] the list of tables
74
     *
75
     * @throws TDBMException
76
     */
77
    public function generateAllDaosAndBeans($daoFactoryClassName, $daonamespace, $beannamespace, $storeInUtc)
78
    {
79
        $classNameMapper = ClassNameMapper::createFromComposerFile($this->rootPath.$this->composerFile);
80
        // TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
81
82
        $tableList = $this->schema->getTables();
83
84
        // Remove all beans and daos from junction tables
85
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables();
86
        $junctionTableNames = array_map(function (Table $table) {
87
            return $table->getName();
88
        }, $junctionTables);
89
90
        $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
91
            return !in_array($table->getName(), $junctionTableNames);
92
        });
93
94
        foreach ($tableList as $table) {
95
            $this->generateDaoAndBean($table, $daonamespace, $beannamespace, $classNameMapper, $storeInUtc);
96
        }
97
98
        $this->generateFactory($tableList, $daoFactoryClassName, $daonamespace, $classNameMapper);
99
100
        // Ok, let's return the list of all tables.
101
        // These will be used by the calling script to create Mouf instances.
102
103
        return array_map(function (Table $table) { return $table->getName(); }, $tableList);
104
    }
105
106
    /**
107
     * Generates in one method call the daos and the beans for one table.
108
     *
109
     * @param Table           $table
110
     * @param string          $daonamespace
111
     * @param string          $beannamespace
112
     * @param ClassNameMapper $classNameMapper
113
     * @param bool            $storeInUtc
114
     *
115
     * @throws TDBMException
116
     */
117
    public function generateDaoAndBean(Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc)
118
    {
119
        $tableName = $table->getName();
120
        $daoName = $this->getDaoNameFromTableName($tableName);
121
        $beanName = $this->getBeanNameFromTableName($tableName);
122
        $baseBeanName = $this->getBaseBeanNameFromTableName($tableName);
123
        $baseDaoName = $this->getBaseDaoNameFromTableName($tableName);
124
125
        $beanDescriptor = new BeanDescriptor($table, $this->schemaAnalyzer, $this->schema, $this->tdbmSchemaAnalyzer);
126
        $this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table, $beannamespace, $classNameMapper, $storeInUtc);
127
        $this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table, $daonamespace, $beannamespace, $classNameMapper);
128
    }
129
130
    /**
131
     * Returns the name of the bean class from the table name.
132
     *
133
     * @param $tableName
134
     *
135
     * @return string
136
     */
137
    public static function getBeanNameFromTableName($tableName)
138
    {
139
        return self::toSingular(self::toCamelCase($tableName)).'Bean';
140
    }
141
142
    /**
143
     * Returns the name of the DAO class from the table name.
144
     *
145
     * @param $tableName
146
     *
147
     * @return string
148
     */
149
    public static function getDaoNameFromTableName($tableName)
150
    {
151
        return self::toSingular(self::toCamelCase($tableName)).'Dao';
152
    }
153
154
    /**
155
     * Returns the name of the base bean class from the table name.
156
     *
157
     * @param $tableName
158
     *
159
     * @return string
160
     */
161
    public static function getBaseBeanNameFromTableName($tableName)
162
    {
163
        return self::toSingular(self::toCamelCase($tableName)).'BaseBean';
164
    }
165
166
    /**
167
     * Returns the name of the base DAO class from the table name.
168
     *
169
     * @param $tableName
170
     *
171
     * @return string
172
     */
173
    public static function getBaseDaoNameFromTableName($tableName)
174
    {
175
        return self::toSingular(self::toCamelCase($tableName)).'BaseDao';
176
    }
177
178
    /**
179
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
180
     *
181
     * @param BeanDescriptor  $beanDescriptor
182
     * @param string          $className       The name of the class
183
     * @param string          $baseClassName   The name of the base class which will be extended (name only, no directory)
184
     * @param Table           $table           The table
185
     * @param string          $beannamespace   The namespace of the bean
186
     * @param ClassNameMapper $classNameMapper
187
     *
188
     * @throws TDBMException
189
     */
190
    public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc)
0 ignored issues
show
Unused Code introduced by
The parameter $storeInUtc 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...
191
    {
192
        $str = $beanDescriptor->generatePhpCode($beannamespace);
193
194
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\Generated\\'.$baseClassName);
195
        if (empty($possibleBaseFileNames)) {
196
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$baseClassName.'" is not autoloadable.');
197
        }
198
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
199
200
        $this->ensureDirectoryExist($possibleBaseFileName);
201
        file_put_contents($possibleBaseFileName, $str);
202
        @chmod($possibleBaseFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
203
204
        $possibleFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$className);
205
        if (empty($possibleFileNames)) {
206
            // @codeCoverageIgnoreStart
207
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$className.'" is not autoloadable.');
208
            // @codeCoverageIgnoreEnd
209
        }
210
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
211 View Code Duplication
        if (!file_exists($possibleFileName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
212
            $tableName = $table->getName();
213
            $str = "<?php
214
/*
215
 * This file has been automatically generated by TDBM.
216
 * You can edit this file as it will not be overwritten.
217
 */
218
219
namespace {$beannamespace};
220
221
use {$beannamespace}\\Generated\\{$baseClassName};
222
223
/**
224
 * The $className class maps the '$tableName' table in database.
225
 */
226
class $className extends $baseClassName
227
{
228
229
}";
230
            $this->ensureDirectoryExist($possibleFileName);
231
            file_put_contents($possibleFileName, $str);
232
            @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
233
        }
234
    }
235
236
    /**
237
     * Tries to find a @defaultSort annotation in one of the columns.
238
     *
239
     * @param Table $table
240
     *
241
     * @return array First item: column name, Second item: column order (asc/desc)
242
     */
243
    private function getDefaultSortColumnFromAnnotation(Table $table)
244
    {
245
        $defaultSort = null;
246
        $defaultSortDirection = null;
247
        foreach ($table->getColumns() as $column) {
248
            $comments = $column->getComment();
249
            $matches = [];
250
            if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
251
                $defaultSort = $column->getName();
252
                if (count($matches) === 3) {
253
                    $defaultSortDirection = $matches[2];
254
                } else {
255
                    $defaultSortDirection = 'ASC';
256
                }
257
            }
258
        }
259
260
        return [$defaultSort, $defaultSortDirection];
261
    }
262
263
    /**
264
     * Writes the PHP bean DAO with simple functions to create/get/save objects.
265
     *
266
     * @param BeanDescriptor  $beanDescriptor
267
     * @param string          $className       The name of the class
268
     * @param string          $baseClassName
269
     * @param string          $beanClassName
270
     * @param Table           $table
271
     * @param string          $daonamespace
272
     * @param string          $beannamespace
273
     * @param ClassNameMapper $classNameMapper
274
     *
275
     * @throws TDBMException
276
     */
277
    public function generateDao(BeanDescriptor $beanDescriptor, $className, $baseClassName, $beanClassName, Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper)
278
    {
279
        $tableName = $table->getName();
280
        $primaryKeyColumns = $table->getPrimaryKeyColumns();
281
282
        list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table);
283
284
        // FIXME: lowercase tables with _ in the name should work!
285
        $tableCamel = self::toSingular(self::toCamelCase($tableName));
286
287
        $beanClassWithoutNameSpace = $beanClassName;
288
        $beanClassName = $beannamespace.'\\'.$beanClassName;
289
290
        list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace);
291
292
        $usedBeans[] = $beanClassName;
293
        // Let's suppress duplicates in used beans (if any)
294
        $usedBeans = array_flip(array_flip($usedBeans));
295
        $useStatements = array_map(function ($usedBean) {
296
            return "use $usedBean;\n";
297
        }, $usedBeans);
298
299
        $str = "<?php
300
301
/*
302
 * This file has been automatically generated by TDBM.
303
 * DO NOT edit this file, as it might be overwritten.
304
 * If you need to perform changes, edit the $className class instead!
305
 */
306
307
namespace {$daonamespace}\\Generated;
308
309
use Mouf\\Database\\TDBM\\TDBMService;
310
use Mouf\\Database\\TDBM\\ResultIterator;
311
use Mouf\\Database\\TDBM\\ArrayIterator;
312
".implode('', $useStatements)."
313
314
/**
315
 * The $baseClassName class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
316
 *
317
 */
318
class $baseClassName
319
{
320
321
    /**
322
     * @var TDBMService
323
     */
324
    protected \$tdbmService;
325
326
    /**
327
     * The default sort column.
328
     *
329
     * @var string
330
     */
331
    private \$defaultSort = ".($defaultSort ? "'$defaultSort'" : 'null').';
332
333
    /**
334
     * The default sort direction.
335
     *
336
     * @var string
337
     */
338
    private $defaultDirection = '.($defaultSort && $defaultSortDirection ? "'$defaultSortDirection'" : "'asc'").";
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultSort 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...
Bug Best Practice introduced by
The expression $defaultSortDirection 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...
339
340
    /**
341
     * Sets the TDBM service used by this DAO.
342
     *
343
     * @param TDBMService \$tdbmService
344
     */
345
    public function __construct(TDBMService \$tdbmService)
346
    {
347
        \$this->tdbmService = \$tdbmService;
348
    }
349
350
    /**
351
     * Persist the $beanClassWithoutNameSpace instance.
352
     *
353
     * @param $beanClassWithoutNameSpace \$obj The bean to save.
354
     */
355
    public function save($beanClassWithoutNameSpace \$obj)
356
    {
357
        \$this->tdbmService->save(\$obj);
358
    }
359
360
    /**
361
     * Get all $tableCamel records.
362
     *
363
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
364
     */
365
    public function findAll()
366
    {
367
        if (\$this->defaultSort) {
368
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
369
        } else {
370
            \$orderBy = null;
371
        }
372
        return \$this->tdbmService->findObjects('$tableName',  null, [], \$orderBy);
373
    }
374
    ";
375
376
        if (count($primaryKeyColumns) === 1) {
377
            $primaryKeyColumn = $primaryKeyColumns[0];
378
            $str .= "
379
    /**
380
     * Get $beanClassWithoutNameSpace specified by its ID (its primary key)
381
     * If the primary key does not exist, an exception is thrown.
382
     *
383
     * @param string|int \$id
384
     * @param bool \$lazyLoading If set to true, the object will not be loaded right away. Instead, it will be loaded when you first try to access a method of the object.
385
     * @return $beanClassWithoutNameSpace
386
     * @throws TDBMException
387
     */
388
    public function getById(\$id, \$lazyLoading = false)
389
    {
390
        return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);
391
    }
392
    ";
393
        }
394
        $str .= "
395
    /**
396
     * Deletes the $beanClassWithoutNameSpace passed in parameter.
397
     *
398
     * @param $beanClassWithoutNameSpace \$obj object to delete
399
     * @param bool \$cascade if true, it will delete all object linked to \$obj
400
     */
401
    public function delete($beanClassWithoutNameSpace \$obj, \$cascade = false)
402
    {
403
        if (\$cascade === true) {
404
            \$this->tdbmService->deleteCascade(\$obj);
405
        } else {
406
            \$this->tdbmService->delete(\$obj);
407
        }
408
    }
409
410
411
    /**
412
     * Get a list of $beanClassWithoutNameSpace specified by its filters.
413
     *
414
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
415
     * @param array \$parameters The parameters associated with the filter
416
     * @param mixed \$orderBy The order string
417
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
418
     * @param int \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
419
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
420
     */
421
    protected function find(\$filter = null, array \$parameters = [], \$orderBy=null, array \$additionalTablesFetch = [], \$mode = null)
422
    {
423
        if (\$this->defaultSort && \$orderBy == null) {
424
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
425
        }
426
        return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode);
427
    }
428
429
    /**
430
     * Get a single $beanClassWithoutNameSpace specified by its filters.
431
     *
432
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
433
     * @param array \$parameters The parameters associated with the filter
434
     * @return $beanClassWithoutNameSpace
435
     */
436
    protected function findOne(\$filter=null, array \$parameters = [])
437
    {
438
        return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters);
439
    }
440
441
    /**
442
     * Sets the default column for default sorting.
443
     *
444
     * @param string \$defaultSort
445
     */
446
    public function setDefaultSort(\$defaultSort)
447
    {
448
        \$this->defaultSort = \$defaultSort;
449
    }
450
";
451
452
        $str .= $findByDaoCode;
453
        $str .= '}
454
';
455
456
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\Generated\\'.$baseClassName);
457
        if (empty($possibleBaseFileNames)) {
458
            // @codeCoverageIgnoreStart
459
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$baseClassName.'" is not autoloadable.');
460
            // @codeCoverageIgnoreEnd
461
        }
462
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
463
464
        $this->ensureDirectoryExist($possibleBaseFileName);
465
        file_put_contents($possibleBaseFileName, $str);
466
        @chmod($possibleBaseFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
467
468
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$className);
469
        if (empty($possibleFileNames)) {
470
            // @codeCoverageIgnoreStart
471
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$className.'" is not autoloadable.');
472
            // @codeCoverageIgnoreEnd
473
        }
474
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
475
476
        // Now, let's generate the "editable" class
477 View Code Duplication
        if (!file_exists($possibleFileName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
478
            $str = "<?php
479
480
/*
481
 * This file has been automatically generated by TDBM.
482
 * You can edit this file as it will not be overwritten.
483
 */
484
485
namespace {$daonamespace};
486
487
use {$daonamespace}\\Generated\\{$baseClassName};
488
489
/**
490
 * The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
491
 */
492
class $className extends $baseClassName
493
{
494
495
}
496
";
497
            $this->ensureDirectoryExist($possibleFileName);
498
            file_put_contents($possibleFileName, $str);
499
            @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
500
        }
501
    }
502
503
    /**
504
     * Generates the factory bean.
505
     *
506
     * @param Table[] $tableList
507
     */
508
    private function generateFactory(array $tableList, $daoFactoryClassName, $daoNamespace, ClassNameMapper $classNameMapper)
509
    {
510
        // For each table, let's write a property.
511
512
        $str = "<?php
513
514
/*
515
 * This file has been automatically generated by TDBM.
516
 * DO NOT edit this file, as it might be overwritten.
517
 */
518
519
namespace {$daoNamespace}\\Generated;
520
";
521
        foreach ($tableList as $table) {
522
            $tableName = $table->getName();
523
            $daoClassName = $this->getDaoNameFromTableName($tableName);
524
            $str .= "use {$daoNamespace}\\".$daoClassName.";\n";
525
        }
526
527
        $str .= "
528
/**
529
 * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.
530
 *
531
 */
532
class $daoFactoryClassName
533
{
534
";
535
536
        foreach ($tableList as $table) {
537
            $tableName = $table->getName();
538
            $daoClassName = $this->getDaoNameFromTableName($tableName);
539
            $daoInstanceName = self::toVariableName($daoClassName);
540
541
            $str .= '    /**
542
     * @var '.$daoClassName.'
543
     */
544
    private $'.$daoInstanceName.';
545
546
    /**
547
     * Returns an instance of the '.$daoClassName.' class.
548
     *
549
     * @return '.$daoClassName.'
550
     */
551
    public function get'.$daoClassName.'()
552
    {
553
        return $this->'.$daoInstanceName.';
554
    }
555
556
    /**
557
     * Sets the instance of the '.$daoClassName.' class that will be returned by the factory getter.
558
     *
559
     * @param '.$daoClassName.' $'.$daoInstanceName.'
560
     */
561
    public function set'.$daoClassName.'('.$daoClassName.' $'.$daoInstanceName.') {
562
        $this->'.$daoInstanceName.' = $'.$daoInstanceName.';
563
    }
564
565
';
566
        }
567
568
        $str .= '
569
}
570
';
571
572
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daoNamespace.'\\Generated\\'.$daoFactoryClassName);
573
        if (empty($possibleFileNames)) {
574
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$daoNamespace.'\\'.$daoFactoryClassName.'" is not autoloadable.');
575
        }
576
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
577
578
        $this->ensureDirectoryExist($possibleFileName);
579
        file_put_contents($possibleFileName, $str);
580
        @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
581
    }
582
583
    /**
584
     * Transforms a string to camelCase (except the first letter will be uppercase too).
585
     * Underscores and spaces are removed and the first letter after the underscore is uppercased.
586
     *
587
     * @param $str string
588
     *
589
     * @return string
590
     */
591
    public static function toCamelCase($str)
592
    {
593
        $str = strtoupper(substr($str, 0, 1)).substr($str, 1);
594
        while (true) {
595
            if (strpos($str, '_') === false && strpos($str, ' ') === false) {
596
                break;
597
            }
598
599
            $pos = strpos($str, '_');
600
            if ($pos === false) {
601
                $pos = strpos($str, ' ');
602
            }
603
            $before = substr($str, 0, $pos);
604
            $after = substr($str, $pos + 1);
605
            $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
606
        }
607
608
        return $str;
609
    }
610
611
    /**
612
     * Tries to put string to the singular form (if it is plural).
613
     * We assume the table names are in english.
614
     *
615
     * @param $str string
616
     *
617
     * @return string
618
     */
619
    public static function toSingular($str)
620
    {
621
        return Inflector::singularize($str);
622
    }
623
624
    /**
625
     * Put the first letter of the string in lower case.
626
     * Very useful to transform a class name into a variable name.
627
     *
628
     * @param $str string
629
     *
630
     * @return string
631
     */
632
    public static function toVariableName($str)
633
    {
634
        return strtolower(substr($str, 0, 1)).substr($str, 1);
635
    }
636
637
    /**
638
     * Ensures the file passed in parameter can be written in its directory.
639
     *
640
     * @param string $fileName
641
     *
642
     * @throws TDBMException
643
     */
644
    private function ensureDirectoryExist($fileName)
645
    {
646
        $dirName = dirname($fileName);
647
        if (!file_exists($dirName)) {
648
            $old = umask(0);
649
            $result = mkdir($dirName, 0775, true);
650
            umask($old);
651
            if ($result === false) {
652
                throw new TDBMException("Unable to create directory: '".$dirName."'.");
653
            }
654
        }
655
    }
656
657
    /**
658
     * Absolute path to composer json file.
659
     *
660
     * @param string $composerFile
661
     */
662
    public function setComposerFile($composerFile)
663
    {
664
        $this->rootPath = dirname($composerFile).'/';
665
        $this->composerFile = basename($composerFile);
666
    }
667
668
    /**
669
     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
670
     *
671
     * @param Type $type The DBAL type
672
     *
673
     * @return string The PHP type
674
     */
675
    public static function dbalTypeToPhpType(Type $type)
676
    {
677
        $map = [
678
            Type::TARRAY => 'array',
679
            Type::SIMPLE_ARRAY => 'array',
680
            Type::JSON_ARRAY => 'array',
681
            Type::BIGINT => 'string',
682
            Type::BOOLEAN => 'bool',
683
            Type::DATETIME => '\DateTimeInterface',
684
            Type::DATETIMETZ => '\DateTimeInterface',
685
            Type::DATE => '\DateTimeInterface',
686
            Type::TIME => '\DateTimeInterface',
687
            Type::DECIMAL => 'float',
688
            Type::INTEGER => 'int',
689
            Type::OBJECT => 'string',
690
            Type::SMALLINT => 'int',
691
            Type::STRING => 'string',
692
            Type::TEXT => 'string',
693
            Type::BINARY => 'string',
694
            Type::BLOB => 'string',
695
            Type::FLOAT => 'float',
696
            Type::GUID => 'string',
697
        ];
698
699
        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
700
    }
701
702
    /**
703
     * @param string $beanNamespace
704
     *
705
     * @return \string[] Returns a map mapping table name to beans name
706
     */
707
    public function buildTableToBeanMap($beanNamespace)
708
    {
709
        $tableToBeanMap = [];
710
711
        $tables = $this->schema->getTables();
712
713
        foreach ($tables as $table) {
714
            $tableName = $table->getName();
715
            $tableToBeanMap[$tableName] = $beanNamespace.'\\'.self::getBeanNameFromTableName($tableName);
716
        }
717
718
        return $tableToBeanMap;
719
    }
720
}
721