Completed
Push — 4.0 ( 243a2b...1abf1e )
by David
50:20 queued 27:28
created

TDBMDaoGenerator   C

Complexity

Total Complexity 41

Size/Duplication

Total Lines 656
Duplicated Lines 7.62 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 32
Bugs 5 Features 4
Metric Value
wmc 41
c 32
b 5
f 4
lcom 1
cbo 9
dl 50
loc 656
rs 6.3522

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 6 7 1
B generateAllDaosAndBeans() 0 30 2
A generateDaoAndBean() 0 11 1
A getBeanNameFromTableName() 0 4 1
A getDaoNameFromTableName() 0 4 1
A getBaseBeanNameFromTableName() 0 4 1
A getBaseDaoNameFromTableName() 0 4 1
B generateBean() 22 47 4
F generateDao() 22 236 11
A generateFactory() 0 67 3
B toCamelCase() 0 19 5
A toSingular() 0 4 1
A toVariableName() 0 4 1
A ensureDirectoryExist() 0 12 3
A setRootPath() 0 4 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\Driver\Connection;
7
use Doctrine\DBAL\Schema\Column;
8
use Doctrine\DBAL\Schema\Schema;
9
use Doctrine\DBAL\Schema\Table;
10
use Doctrine\DBAL\Types\Type;
11
use Mouf\Composer\ClassNameMapper;
12
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
13
use Mouf\Database\TDBM\TDBMException;
14
use Mouf\Database\TDBM\TDBMSchemaAnalyzer;
15
16
/**
17
 * This class generates automatically DAOs and Beans for TDBM.
18
 */
19
class TDBMDaoGenerator
20
{
21
    /**
22
     * @var SchemaAnalyzer
23
     */
24
    private $schemaAnalyzer;
25
26
    /**
27
     * @var Schema
28
     */
29
    private $schema;
30
31
    /**
32
     * The root directory of the project.
33
     *
34
     * @var string
35
     */
36
    private $rootPath;
37
38
    /**
39
     * @var TDBMSchemaAnalyzer
40
     */
41
    private $tdbmSchemaAnalyzer;
42
43
    /**
44
     * Constructor.
45
     *
46
     * @param Connection $dbConnection The connection to the database.
0 ignored issues
show
Bug introduced by
There is no parameter named $dbConnection. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
47
     */
48 View Code Duplication
    public function __construct(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...
49
    {
50
        $this->schemaAnalyzer = $schemaAnalyzer;
51
        $this->schema = $schema;
52
        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
53
        $this->rootPath = __DIR__.'/../../../../../../../../';
54
    }
55
56
    /**
57
     * Generates all the daos and beans.
58
     *
59
     * @param string $daoFactoryClassName The classe name of the DAO factory
60
     * @param string $daonamespace        The namespace for the DAOs, without trailing \
61
     * @param string $beannamespace       The Namespace for the beans, without trailing \
62
     * @param bool   $storeInUtc          If the generated daos should store the date in UTC timezone instead of user's timezone.
63
     *
64
     * @return \string[] the list of tables
65
     *
66
     * @throws TDBMException
67
     */
68
    public function generateAllDaosAndBeans($daoFactoryClassName, $daonamespace, $beannamespace, $storeInUtc)
69
    {
70
        // TODO: extract ClassNameMapper in its own package!
71
        $classNameMapper = ClassNameMapper::createFromComposerFile($this->rootPath.'composer.json');
72
73
        // TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
74
75
        $tableList = $this->schema->getTables();
76
77
        // Remove all beans and daos from junction tables
78
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables();
79
        $junctionTableNames = array_map(function (Table $table) {
80
            return $table->getName();
81
        }, $junctionTables);
82
83
        $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
84
            return !in_array($table->getName(), $junctionTableNames);
85
        });
86
87
        foreach ($tableList as $table) {
88
            $this->generateDaoAndBean($table, $daonamespace, $beannamespace, $classNameMapper, $storeInUtc);
89
        }
90
91
        $this->generateFactory($tableList, $daoFactoryClassName, $daonamespace, $classNameMapper);
92
93
        // Ok, let's return the list of all tables.
94
        // These will be used by the calling script to create Mouf instances.
95
96
        return array_map(function (Table $table) { return $table->getName(); }, $tableList);
97
    }
98
99
    /**
100
     * Generates in one method call the daos and the beans for one table.
101
     *
102
     * @param $tableName
103
     */
104
    public function generateDaoAndBean(Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc)
105
    {
106
        $tableName = $table->getName();
107
        $daoName = $this->getDaoNameFromTableName($tableName);
108
        $beanName = $this->getBeanNameFromTableName($tableName);
109
        $baseBeanName = $this->getBaseBeanNameFromTableName($tableName);
110
        $baseDaoName = $this->getBaseDaoNameFromTableName($tableName);
111
112
        $this->generateBean($beanName, $baseBeanName, $table, $beannamespace, $classNameMapper, $storeInUtc);
113
        $this->generateDao($daoName, $baseDaoName, $beanName, $table, $daonamespace, $beannamespace, $classNameMapper);
114
    }
115
116
    /**
117
     * Returns the name of the bean class from the table name.
118
     *
119
     * @param $tableName
120
     *
121
     * @return string
122
     */
123
    public static function getBeanNameFromTableName($tableName)
124
    {
125
        return self::toSingular(self::toCamelCase($tableName)).'Bean';
126
    }
127
128
    /**
129
     * Returns the name of the DAO class from the table name.
130
     *
131
     * @param $tableName
132
     *
133
     * @return string
134
     */
135
    public static function getDaoNameFromTableName($tableName)
136
    {
137
        return self::toSingular(self::toCamelCase($tableName)).'Dao';
138
    }
139
140
    /**
141
     * Returns the name of the base bean class from the table name.
142
     *
143
     * @param $tableName
144
     *
145
     * @return string
146
     */
147
    public static function getBaseBeanNameFromTableName($tableName)
148
    {
149
        return self::toSingular(self::toCamelCase($tableName)).'BaseBean';
150
    }
151
152
    /**
153
     * Returns the name of the base DAO class from the table name.
154
     *
155
     * @param $tableName
156
     *
157
     * @return string
158
     */
159
    public static function getBaseDaoNameFromTableName($tableName)
160
    {
161
        return self::toSingular(self::toCamelCase($tableName)).'BaseDao';
162
    }
163
164
    /**
165
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
166
     *
167
     * @param string          $className       The name of the class
168
     * @param string          $baseClassName   The name of the base class which will be extended (name only, no directory)
169
     * @param Table           $table           The table
170
     * @param string          $beannamespace   The namespace of the bean
171
     * @param ClassNameMapper $classNameMapper
172
     *
173
     * @throws TDBMException
174
     */
175
    public function generateBean($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...
176
    {
177
        $beanDescriptor = new BeanDescriptor($table, $this->schemaAnalyzer, $this->schema, $this->tdbmSchemaAnalyzer);
178
179
        $str = $beanDescriptor->generatePhpCode($beannamespace);
180
181
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$baseClassName);
182
        if (!$possibleBaseFileNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $possibleBaseFileNames of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
183
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$baseClassName.'" is not autoloadable.');
184
        }
185
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
186
187
        $this->ensureDirectoryExist($possibleBaseFileName);
188
        file_put_contents($possibleBaseFileName, $str);
189
        @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...
190
191
        $possibleFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$className);
192
        if (!$possibleFileNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $possibleFileNames of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
193
            // @codeCoverageIgnoreStart
194
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$className.'" is not autoloadable.');
195
            // @codeCoverageIgnoreEnd
196
        }
197
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
198
199 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...
200
            $tableName = $table->getName();
201
202
            $str = "<?php
203
/*
204
 * This file has been automatically generated by TDBM.
205
 * You can edit this file as it will not be overwritten.
206
 */
207
208
namespace {$beannamespace};
209
210
/**
211
 * The $className class maps the '$tableName' table in database.
212
 */
213
class $className extends $baseClassName
214
{
215
216
}";
217
            $this->ensureDirectoryExist($possibleFileName);
218
            file_put_contents($possibleFileName, $str);
219
            @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...
220
        }
221
    }
222
223
    /**
224
     * Writes the PHP bean DAO with simple functions to create/get/save objects.
225
     *
226
     * @param string $fileName  The file that will be written (without the directory)
0 ignored issues
show
Bug introduced by
There is no parameter named $fileName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
227
     * @param string $className The name of the class
228
     * @param string $tableName The name of the table
0 ignored issues
show
Bug introduced by
There is no parameter named $tableName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
229
     */
230
    public function generateDao($className, $baseClassName, $beanClassName, Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper)
231
    {
232
        $tableName = $table->getName();
233
        $primaryKeyColumns = $table->getPrimaryKeyColumns();
234
235
        $defaultSort = null;
236
        foreach ($table->getColumns() as $column) {
237
            $comments = $column->getComment();
238
            $matches = array();
239
            if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
240
                $defaultSort = $data['column_name'];
0 ignored issues
show
Bug introduced by
The variable $data does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
241
                if (count($matches == 3)) {
242
                    $defaultSortDirection = $matches[2];
243
                } else {
244
                    $defaultSortDirection = 'ASC';
245
                }
246
            }
247
        }
248
249
        // FIXME: lowercase tables with _ in the name should work!
250
        $tableCamel = self::toSingular(self::toCamelCase($tableName));
251
252
        $beanClassWithoutNameSpace = $beanClassName;
253
        $beanClassName = $beannamespace.'\\'.$beanClassName;
254
255
        $str = "<?php
256
257
/*
258
 * This file has been automatically generated by TDBM.
259
 * DO NOT edit this file, as it might be overwritten.
260
 * If you need to perform changes, edit the $className class instead!
261
 */
262
263
namespace {$daonamespace};
264
265
use Mouf\\Database\\TDBM\\TDBMService;
266
use Mouf\\Database\\TDBM\\ResultIterator;
267
use Mouf\\Database\\TDBM\\ArrayIterator;
268
use $beanClassName;
269
270
/**
271
 * The $baseClassName class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
272
 *
273
 */
274
class $baseClassName
275
{
276
277
    /**
278
     * @var TDBMService
279
     */
280
    protected \$tdbmService;
281
282
    /**
283
     * The default sort column.
284
     *
285
     * @var string
286
     */
287
    private \$defaultSort = ".($defaultSort ? "'$defaultSort'" : 'null').";
288
289
    /**
290
     * The default sort direction.
291
     *
292
     * @var string
293
     */
294
    private \$defaultDirection = ".($defaultSort && $defaultSortDirection ? "'$defaultSortDirection'" : "'asc'").";
0 ignored issues
show
Bug introduced by
The variable $defaultSortDirection does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
295
296
    /**
297
     * Sets the TDBM service used by this DAO.
298
     *
299
     * @param TDBMService \$tdbmService
300
     */
301
    public function __construct(TDBMService \$tdbmService)
302
    {
303
        \$this->tdbmService = \$tdbmService;
304
    }
305
306
    /**
307
     * Return a new instance of $beanClassWithoutNameSpace object, that will be persisted in database.
308
     *
309
     * @return $beanClassWithoutNameSpace
310
     */// TODO!
311
    /*public function create()
312
    {
313
        return \$this->tdbmService->getNewObject('$tableName', true);
314
    }*/
315
316
    /**
317
     * Persist the $beanClassWithoutNameSpace instance.
318
     *
319
     * @param $beanClassWithoutNameSpace \$obj The bean to save.
320
     */
321
    public function save($beanClassWithoutNameSpace \$obj)
322
    {
323
        \$this->tdbmService->save(\$obj);
324
    }
325
326
    /**
327
     * Get all $tableCamel records.
328
     *
329
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
330
     */
331
    public function findAll()
332
    {
333
        if (\$this->defaultSort) {
334
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
335
        } else {
336
            \$orderBy = null;
337
        }
338
        return \$this->tdbmService->findObjects('$tableName',  null, [], \$orderBy);
339
    }
340
    ";
341
342
        if (count($primaryKeyColumns) === 1) {
343
            $primaryKeyColumn = $primaryKeyColumns[0];
344
            $str .= "
345
    /**
346
     * Get $beanClassWithoutNameSpace specified by its ID (its primary key)
347
     * If the primary key does not exist, an exception is thrown.
348
     *
349
     * @param string|int \$id
350
     * @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.
351
     * @return $beanClassWithoutNameSpace
352
     * @throws TDBMException
353
     */
354
    public function getById(\$id, \$lazyLoading = false)
355
    {
356
        return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);
357
    }
358
    ";
359
        }
360
        $str .= "
361
    /**
362
     * Deletes the $beanClassWithoutNameSpace passed in parameter.
363
     *
364
     * @param $beanClassWithoutNameSpace \$obj object to delete
365
     * @param bool \$cascade if true, it will delete all object linked to \$obj
366
     */
367
    public function delete($beanClassWithoutNameSpace \$obj, \$cascade = false)
368
    {
369
        if (\$cascade === true) {
370
            \$this->tdbmService->deleteCascade(\$obj);
371
        } else {
372
            \$this->tdbmService->delete(\$obj);
373
        }
374
    }
375
376
377
    /**
378
     * Get a list of $beanClassWithoutNameSpace specified by its filters.
379
     *
380
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
381
     * @param array \$parameters The parameters associated with the filter
382
     * @param mixed \$orderby The order string
383
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
384
     * @param string \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
385
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
386
     */
387
    protected function find(\$filter=null, array \$parameters = [], \$orderby=null, array \$additionalTablesFetch = array(), \$mode = null)
388
    {
389
        if (\$this->defaultSort && \$orderby == null) {
390
            \$orderby = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
391
        }
392
        return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderby, \$additionalTablesFetch, \$mode);
393
    }
394
395
    /**
396
     * Get a single $beanClassWithoutNameSpace specified by its filters.
397
     *
398
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
399
     * @param array \$parameters The parameters associated with the filter
400
     * @return $beanClassWithoutNameSpace
401
     */
402
    protected function findOne(\$filter=null, array \$parameters = [])
403
    {
404
        return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters);
405
    }
406
407
    /**
408
     * Sets the default column for default sorting.
409
     *
410
     * @param string \$defaultSort
411
     */
412
    public function setDefaultSort(\$defaultSort)
413
    {
414
        \$this->defaultSort = \$defaultSort;
415
    }
416
    ";
417
418
        $str .= '
419
}
420
';
421
422
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$baseClassName);
423
        if (!$possibleBaseFileNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $possibleBaseFileNames of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
424
            // @codeCoverageIgnoreStart
425
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$baseClassName.'" is not autoloadable.');
426
            // @codeCoverageIgnoreEnd
427
        }
428
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
429
430
        $this->ensureDirectoryExist($possibleBaseFileName);
431
        file_put_contents($possibleBaseFileName, $str);
432
        @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...
433
434
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$className);
435
        if (!$possibleFileNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $possibleFileNames of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
436
            // @codeCoverageIgnoreStart
437
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$className.'" is not autoloadable.');
438
            // @codeCoverageIgnoreEnd
439
        }
440
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
441
442
        // Now, let's generate the "editable" class
443 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...
444
            $str = "<?php
445
446
/*
447
 * This file has been automatically generated by TDBM.
448
 * You can edit this file as it will not be overwritten.
449
 */
450
451
namespace {$daonamespace};
452
453
/**
454
 * The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
455
 */
456
class $className extends $baseClassName
457
{
458
459
}
460
";
461
            $this->ensureDirectoryExist($possibleFileName);
462
            file_put_contents($possibleFileName, $str);
463
            @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...
464
        }
465
    }
466
467
    /**
468
     * Generates the factory bean.
469
     *
470
     * @param Table[] $tableList
471
     */
472
    private function generateFactory(array $tableList, $daoFactoryClassName, $daoNamespace, ClassNameMapper $classNameMapper)
473
    {
474
        // For each table, let's write a property.
475
476
        $str = "<?php
477
478
/*
479
 * This file has been automatically generated by TDBM.
480
 * DO NOT edit this file, as it might be overwritten.
481
 */
482
483
namespace {$daoNamespace};
484
485
/**
486
 * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.
487
 *
488
 */
489
class $daoFactoryClassName
490
{
491
";
492
493
        foreach ($tableList as $table) {
494
            $tableName = $table->getName();
495
            $daoClassName = $this->getDaoNameFromTableName($tableName);
496
            $daoInstanceName = self::toVariableName($daoClassName);
497
498
            $str .= '    /**
499
     * @var '.$daoClassName.'
500
     */
501
    private $'.$daoInstanceName.';
502
503
    /**
504
     * Returns an instance of the '.$daoClassName.' class.
505
     *
506
     * @return '.$daoClassName.'
507
     */
508
    public function get'.$daoClassName.'()
509
    {
510
        return $this->'.$daoInstanceName.';
511
    }
512
513
    /**
514
     * Sets the instance of the '.$daoClassName.' class that will be returned by the factory getter.
515
     *
516
     * @param '.$daoClassName.' $'.$daoInstanceName.'
517
     */
518
    public function set'.$daoClassName.'('.$daoClassName.' $'.$daoInstanceName.') {
519
        $this->'.$daoInstanceName.' = $'.$daoInstanceName.';
520
    }
521
522
';
523
        }
524
525
        $str .= '
526
}
527
';
528
529
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daoNamespace.'\\'.$daoFactoryClassName);
530
        if (!$possibleFileNames) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $possibleFileNames of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
531
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$daoNamespace.'\\'.$daoFactoryClassName.'" is not autoloadable.');
532
        }
533
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
534
535
        $this->ensureDirectoryExist($possibleFileName);
536
        file_put_contents($possibleFileName, $str);
537
        @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...
538
    }
539
540
    /**
541
     * Transforms a string to camelCase (except the first letter will be uppercase too).
542
     * Underscores and spaces are removed and the first letter after the underscore is uppercased.
543
     *
544
     * @param $str string
545
     *
546
     * @return string
547
     */
548
    public static function toCamelCase($str)
549
    {
550
        $str = strtoupper(substr($str, 0, 1)).substr($str, 1);
551
        while (true) {
552
            if (strpos($str, '_') === false && strpos($str, ' ') === false) {
553
                break;
554
            }
555
556
            $pos = strpos($str, '_');
557
            if ($pos === false) {
558
                $pos = strpos($str, ' ');
559
            }
560
            $before = substr($str, 0, $pos);
561
            $after = substr($str, $pos + 1);
562
            $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
563
        }
564
565
        return $str;
566
    }
567
568
    /**
569
     * Tries to put string to the singular form (if it is plural).
570
     * We assume the table names are in english.
571
     *
572
     * @param $str string
573
     *
574
     * @return string
575
     */
576
    public static function toSingular($str)
577
    {
578
        return Inflector::singularize($str);
579
    }
580
581
    /**
582
     * Put the first letter of the string in lower case.
583
     * Very useful to transform a class name into a variable name.
584
     *
585
     * @param $str string
586
     *
587
     * @return string
588
     */
589
    public static function toVariableName($str)
590
    {
591
        return strtolower(substr($str, 0, 1)).substr($str, 1);
592
    }
593
594
    /**
595
     * Ensures the file passed in parameter can be written in its directory.
596
     *
597
     * @param string $fileName
598
     *
599
     * @throws TDBMException
600
     */
601
    private function ensureDirectoryExist($fileName)
602
    {
603
        $dirName = dirname($fileName);
604
        if (!file_exists($dirName)) {
605
            $old = umask(0);
606
            $result = mkdir($dirName, 0775, true);
607
            umask($old);
608
            if ($result === false) {
609
                throw new TDBMException("Unable to create directory: '".$dirName."'.");
610
            }
611
        }
612
    }
613
614
    /**
615
     * @param string $rootPath
616
     */
617
    public function setRootPath($rootPath)
618
    {
619
        $this->rootPath = $rootPath;
620
    }
621
622
    /**
623
     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
624
     *
625
     * @param Type $type The DBAL type
626
     *
627
     * @return string The PHP type
628
     */
629
    public static function dbalTypeToPhpType(Type $type)
630
    {
631
        $map = [
632
            Type::TARRAY => 'array',
633
            Type::SIMPLE_ARRAY => 'array',
634
            Type::JSON_ARRAY => 'array',
635
            Type::BIGINT => 'string',
636
            Type::BOOLEAN => 'bool',
637
            Type::DATETIME => '\DateTimeInterface',
638
            Type::DATETIMETZ => '\DateTimeInterface',
639
            Type::DATE => '\DateTimeInterface',
640
            Type::TIME => '\DateTimeInterface',
641
            Type::DECIMAL => 'float',
642
            Type::INTEGER => 'int',
643
            Type::OBJECT => 'string',
644
            Type::SMALLINT => 'int',
645
            Type::STRING => 'string',
646
            Type::TEXT => 'string',
647
            Type::BINARY => 'string',
648
            Type::BLOB => 'string',
649
            Type::FLOAT => 'float',
650
            Type::GUID => 'string',
651
        ];
652
653
        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
654
    }
655
656
    /**
657
     * @param string $beanNamespace
658
     *
659
     * @return \string[] Returns a map mapping table name to beans name
660
     */
661
    public function buildTableToBeanMap($beanNamespace)
662
    {
663
        $tableToBeanMap = [];
664
665
        $tables = $this->schema->getTables();
666
667
        foreach ($tables as $table) {
668
            $tableName = $table->getName();
669
            $tableToBeanMap[$tableName] = $beanNamespace.'\\'.self::getBeanNameFromTableName($tableName);
670
        }
671
672
        return $tableToBeanMap;
673
    }
674
}
675