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
|
|
|
* @var TDBMSchemaAnalyzer |
39
|
|
|
*/ |
40
|
|
|
private $tdbmSchemaAnalyzer; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Constructor. |
44
|
|
|
* |
45
|
|
|
* @param SchemaAnalyzer $schemaAnalyzer |
46
|
|
|
* @param Schema $schema |
47
|
|
|
* @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer |
48
|
|
|
*/ |
49
|
|
View Code Duplication |
public function __construct(SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer) |
|
|
|
|
50
|
|
|
{ |
51
|
|
|
$this->schemaAnalyzer = $schemaAnalyzer; |
52
|
|
|
$this->schema = $schema; |
53
|
|
|
$this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer; |
54
|
|
|
$this->rootPath = __DIR__.'/../../../../../../../../'; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Generates all the daos and beans. |
59
|
|
|
* |
60
|
|
|
* @param string $daoFactoryClassName The classe name of the DAO factory |
61
|
|
|
* @param string $daonamespace The namespace for the DAOs, without trailing \ |
62
|
|
|
* @param string $beannamespace The Namespace for the beans, without trailing \ |
63
|
|
|
* @param bool $storeInUtc If the generated daos should store the date in UTC timezone instead of user's timezone. |
64
|
|
|
* |
65
|
|
|
* @return \string[] the list of tables |
66
|
|
|
* |
67
|
|
|
* @throws TDBMException |
68
|
|
|
*/ |
69
|
|
|
public function generateAllDaosAndBeans($daoFactoryClassName, $daonamespace, $beannamespace, $storeInUtc) |
70
|
|
|
{ |
71
|
|
|
// TODO: extract ClassNameMapper in its own package! |
72
|
|
|
$classNameMapper = ClassNameMapper::createFromComposerFile($this->rootPath.'composer.json'); |
73
|
|
|
|
74
|
|
|
// TODO: check that no class name ends with "Base". Otherwise, there will be name clash. |
75
|
|
|
|
76
|
|
|
$tableList = $this->schema->getTables(); |
77
|
|
|
|
78
|
|
|
// Remove all beans and daos from junction tables |
79
|
|
|
$junctionTables = $this->schemaAnalyzer->detectJunctionTables(); |
80
|
|
|
$junctionTableNames = array_map(function (Table $table) { |
81
|
|
|
return $table->getName(); |
82
|
|
|
}, $junctionTables); |
83
|
|
|
|
84
|
|
|
$tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) { |
85
|
|
|
return !in_array($table->getName(), $junctionTableNames); |
86
|
|
|
}); |
87
|
|
|
|
88
|
|
|
foreach ($tableList as $table) { |
89
|
|
|
$this->generateDaoAndBean($table, $daonamespace, $beannamespace, $classNameMapper, $storeInUtc); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
$this->generateFactory($tableList, $daoFactoryClassName, $daonamespace, $classNameMapper); |
93
|
|
|
|
94
|
|
|
// Ok, let's return the list of all tables. |
95
|
|
|
// These will be used by the calling script to create Mouf instances. |
96
|
|
|
|
97
|
|
|
return array_map(function (Table $table) { return $table->getName(); }, $tableList); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Generates in one method call the daos and the beans for one table. |
102
|
|
|
* |
103
|
|
|
* @param Table $table |
104
|
|
|
* @param string $daonamespace |
105
|
|
|
* @param string $beannamespace |
106
|
|
|
* @param ClassNameMapper $classNameMapper |
107
|
|
|
* @param bool $storeInUtc |
108
|
|
|
* |
109
|
|
|
* @throws TDBMException |
110
|
|
|
*/ |
111
|
|
|
public function generateDaoAndBean(Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc) |
112
|
|
|
{ |
113
|
|
|
$tableName = $table->getName(); |
114
|
|
|
$daoName = $this->getDaoNameFromTableName($tableName); |
115
|
|
|
$beanName = $this->getBeanNameFromTableName($tableName); |
116
|
|
|
$baseBeanName = $this->getBaseBeanNameFromTableName($tableName); |
117
|
|
|
$baseDaoName = $this->getBaseDaoNameFromTableName($tableName); |
118
|
|
|
|
119
|
|
|
$beanDescriptor = new BeanDescriptor($table, $this->schemaAnalyzer, $this->schema, $this->tdbmSchemaAnalyzer); |
120
|
|
|
|
121
|
|
|
$this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table, $beannamespace, $classNameMapper, $storeInUtc); |
122
|
|
|
$this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table, $daonamespace, $beannamespace, $classNameMapper); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Returns the name of the bean class from the table name. |
127
|
|
|
* |
128
|
|
|
* @param $tableName |
129
|
|
|
* |
130
|
|
|
* @return string |
131
|
|
|
*/ |
132
|
|
|
public static function getBeanNameFromTableName($tableName) |
133
|
|
|
{ |
134
|
|
|
return self::toSingular(self::toCamelCase($tableName)).'Bean'; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Returns the name of the DAO class from the table name. |
139
|
|
|
* |
140
|
|
|
* @param $tableName |
141
|
|
|
* |
142
|
|
|
* @return string |
143
|
|
|
*/ |
144
|
|
|
public static function getDaoNameFromTableName($tableName) |
145
|
|
|
{ |
146
|
|
|
return self::toSingular(self::toCamelCase($tableName)).'Dao'; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Returns the name of the base bean class from the table name. |
151
|
|
|
* |
152
|
|
|
* @param $tableName |
153
|
|
|
* |
154
|
|
|
* @return string |
155
|
|
|
*/ |
156
|
|
|
public static function getBaseBeanNameFromTableName($tableName) |
157
|
|
|
{ |
158
|
|
|
return self::toSingular(self::toCamelCase($tableName)).'BaseBean'; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Returns the name of the base DAO class from the table name. |
163
|
|
|
* |
164
|
|
|
* @param $tableName |
165
|
|
|
* |
166
|
|
|
* @return string |
167
|
|
|
*/ |
168
|
|
|
public static function getBaseDaoNameFromTableName($tableName) |
169
|
|
|
{ |
170
|
|
|
return self::toSingular(self::toCamelCase($tableName)).'BaseDao'; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Writes the PHP bean file with all getters and setters from the table passed in parameter. |
175
|
|
|
* |
176
|
|
|
* @param BeanDescriptor $beanDescriptor |
177
|
|
|
* @param string $className The name of the class |
178
|
|
|
* @param string $baseClassName The name of the base class which will be extended (name only, no directory) |
179
|
|
|
* @param Table $table The table |
180
|
|
|
* @param string $beannamespace The namespace of the bean |
181
|
|
|
* @param ClassNameMapper $classNameMapper |
182
|
|
|
* |
183
|
|
|
* @throws TDBMException |
184
|
|
|
*/ |
185
|
|
|
public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc) |
|
|
|
|
186
|
|
|
{ |
187
|
|
|
$str = $beanDescriptor->generatePhpCode($beannamespace); |
188
|
|
|
|
189
|
|
|
$possibleBaseFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$baseClassName); |
190
|
|
|
if (empty($possibleBaseFileNames)) { |
191
|
|
|
throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$baseClassName.'" is not autoloadable.'); |
192
|
|
|
} |
193
|
|
|
$possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0]; |
194
|
|
|
|
195
|
|
|
$this->ensureDirectoryExist($possibleBaseFileName); |
196
|
|
|
file_put_contents($possibleBaseFileName, $str); |
197
|
|
|
@chmod($possibleBaseFileName, 0664); |
|
|
|
|
198
|
|
|
|
199
|
|
|
$possibleFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$className); |
200
|
|
|
if (empty($possibleFileNames)) { |
201
|
|
|
// @codeCoverageIgnoreStart |
202
|
|
|
throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$className.'" is not autoloadable.'); |
203
|
|
|
// @codeCoverageIgnoreEnd |
204
|
|
|
} |
205
|
|
|
$possibleFileName = $this->rootPath.$possibleFileNames[0]; |
206
|
|
|
|
207
|
|
View Code Duplication |
if (!file_exists($possibleFileName)) { |
|
|
|
|
208
|
|
|
$tableName = $table->getName(); |
209
|
|
|
|
210
|
|
|
$str = "<?php |
211
|
|
|
/* |
212
|
|
|
* This file has been automatically generated by TDBM. |
213
|
|
|
* You can edit this file as it will not be overwritten. |
214
|
|
|
*/ |
215
|
|
|
|
216
|
|
|
namespace {$beannamespace}; |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* The $className class maps the '$tableName' table in database. |
220
|
|
|
*/ |
221
|
|
|
class $className extends $baseClassName |
222
|
|
|
{ |
223
|
|
|
|
224
|
|
|
}"; |
225
|
|
|
$this->ensureDirectoryExist($possibleFileName); |
226
|
|
|
file_put_contents($possibleFileName, $str); |
227
|
|
|
@chmod($possibleFileName, 0664); |
|
|
|
|
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Tries to find a @defaultSort annotation in one of the columns. |
233
|
|
|
* |
234
|
|
|
* @param Table $table |
235
|
|
|
* |
236
|
|
|
* @return array First item: column name, Second item: column order (asc/desc) |
237
|
|
|
*/ |
238
|
|
|
private function getDefaultSortColumnFromAnnotation(Table $table) |
239
|
|
|
{ |
240
|
|
|
$defaultSort = null; |
241
|
|
|
$defaultSortDirection = null; |
242
|
|
|
foreach ($table->getColumns() as $column) { |
243
|
|
|
$comments = $column->getComment(); |
244
|
|
|
$matches = []; |
245
|
|
|
if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) { |
246
|
|
|
$defaultSort = $column->getName(); |
247
|
|
|
if (count($matches) === 3) { |
248
|
|
|
$defaultSortDirection = $matches[2]; |
249
|
|
|
} else { |
250
|
|
|
$defaultSortDirection = 'ASC'; |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
return [$defaultSort, $defaultSortDirection]; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Writes the PHP bean DAO with simple functions to create/get/save objects. |
260
|
|
|
* |
261
|
|
|
* @param BeanDescriptor $beanDescriptor |
262
|
|
|
* @param string $className The name of the class |
263
|
|
|
* @param string $baseClassName |
264
|
|
|
* @param string $beanClassName |
265
|
|
|
* @param Table $table |
266
|
|
|
* @param string $daonamespace |
267
|
|
|
* @param string $beannamespace |
268
|
|
|
* @param ClassNameMapper $classNameMapper |
269
|
|
|
* |
270
|
|
|
* @throws TDBMException |
271
|
|
|
*/ |
272
|
|
|
public function generateDao(BeanDescriptor $beanDescriptor, $className, $baseClassName, $beanClassName, Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper) |
273
|
|
|
{ |
274
|
|
|
$tableName = $table->getName(); |
275
|
|
|
$primaryKeyColumns = $table->getPrimaryKeyColumns(); |
276
|
|
|
|
277
|
|
|
list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table); |
278
|
|
|
|
279
|
|
|
// FIXME: lowercase tables with _ in the name should work! |
280
|
|
|
$tableCamel = self::toSingular(self::toCamelCase($tableName)); |
281
|
|
|
|
282
|
|
|
$beanClassWithoutNameSpace = $beanClassName; |
283
|
|
|
$beanClassName = $beannamespace.'\\'.$beanClassName; |
284
|
|
|
|
285
|
|
|
list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace); |
286
|
|
|
|
287
|
|
|
$usedBeans[] = $beanClassName; |
288
|
|
|
// Let's suppress duplicates in used beans (if any) |
289
|
|
|
$usedBeans = array_flip(array_flip($usedBeans)); |
290
|
|
|
$useStatements = array_map(function ($usedBean) { |
291
|
|
|
return "use $usedBean;\n"; |
292
|
|
|
}, $usedBeans); |
293
|
|
|
|
294
|
|
|
$str = "<?php |
295
|
|
|
|
296
|
|
|
/* |
297
|
|
|
* This file has been automatically generated by TDBM. |
298
|
|
|
* DO NOT edit this file, as it might be overwritten. |
299
|
|
|
* If you need to perform changes, edit the $className class instead! |
300
|
|
|
*/ |
301
|
|
|
|
302
|
|
|
namespace {$daonamespace}; |
303
|
|
|
|
304
|
|
|
use Mouf\\Database\\TDBM\\TDBMService; |
305
|
|
|
use Mouf\\Database\\TDBM\\ResultIterator; |
306
|
|
|
use Mouf\\Database\\TDBM\\ArrayIterator; |
307
|
|
|
".implode('', $useStatements)." |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* The $baseClassName class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table. |
311
|
|
|
* |
312
|
|
|
*/ |
313
|
|
|
class $baseClassName |
314
|
|
|
{ |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @var TDBMService |
318
|
|
|
*/ |
319
|
|
|
protected \$tdbmService; |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* The default sort column. |
323
|
|
|
* |
324
|
|
|
* @var string |
325
|
|
|
*/ |
326
|
|
|
private \$defaultSort = ".($defaultSort ? "'$defaultSort'" : 'null')."; |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* The default sort direction. |
330
|
|
|
* |
331
|
|
|
* @var string |
332
|
|
|
*/ |
333
|
|
|
private \$defaultDirection = ".($defaultSort && $defaultSortDirection ? "'$defaultSortDirection'" : "'asc'")."; |
|
|
|
|
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Sets the TDBM service used by this DAO. |
337
|
|
|
* |
338
|
|
|
* @param TDBMService \$tdbmService |
339
|
|
|
*/ |
340
|
|
|
public function __construct(TDBMService \$tdbmService) |
341
|
|
|
{ |
342
|
|
|
\$this->tdbmService = \$tdbmService; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Persist the $beanClassWithoutNameSpace instance. |
347
|
|
|
* |
348
|
|
|
* @param $beanClassWithoutNameSpace \$obj The bean to save. |
349
|
|
|
*/ |
350
|
|
|
public function save($beanClassWithoutNameSpace \$obj) |
351
|
|
|
{ |
352
|
|
|
\$this->tdbmService->save(\$obj); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* Get all $tableCamel records. |
357
|
|
|
* |
358
|
|
|
* @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray |
359
|
|
|
*/ |
360
|
|
|
public function findAll() |
361
|
|
|
{ |
362
|
|
|
if (\$this->defaultSort) { |
363
|
|
|
\$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection; |
364
|
|
|
} else { |
365
|
|
|
\$orderBy = null; |
366
|
|
|
} |
367
|
|
|
return \$this->tdbmService->findObjects('$tableName', null, [], \$orderBy); |
368
|
|
|
} |
369
|
|
|
"; |
370
|
|
|
|
371
|
|
|
if (count($primaryKeyColumns) === 1) { |
372
|
|
|
$primaryKeyColumn = $primaryKeyColumns[0]; |
373
|
|
|
$str .= " |
374
|
|
|
/** |
375
|
|
|
* Get $beanClassWithoutNameSpace specified by its ID (its primary key) |
376
|
|
|
* If the primary key does not exist, an exception is thrown. |
377
|
|
|
* |
378
|
|
|
* @param string|int \$id |
379
|
|
|
* @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. |
380
|
|
|
* @return $beanClassWithoutNameSpace |
381
|
|
|
* @throws TDBMException |
382
|
|
|
*/ |
383
|
|
|
public function getById(\$id, \$lazyLoading = false) |
384
|
|
|
{ |
385
|
|
|
return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading); |
386
|
|
|
} |
387
|
|
|
"; |
388
|
|
|
} |
389
|
|
|
$str .= " |
390
|
|
|
/** |
391
|
|
|
* Deletes the $beanClassWithoutNameSpace passed in parameter. |
392
|
|
|
* |
393
|
|
|
* @param $beanClassWithoutNameSpace \$obj object to delete |
394
|
|
|
* @param bool \$cascade if true, it will delete all object linked to \$obj |
395
|
|
|
*/ |
396
|
|
|
public function delete($beanClassWithoutNameSpace \$obj, \$cascade = false) |
397
|
|
|
{ |
398
|
|
|
if (\$cascade === true) { |
399
|
|
|
\$this->tdbmService->deleteCascade(\$obj); |
400
|
|
|
} else { |
401
|
|
|
\$this->tdbmService->delete(\$obj); |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Get a list of $beanClassWithoutNameSpace specified by its filters. |
408
|
|
|
* |
409
|
|
|
* @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description) |
410
|
|
|
* @param array \$parameters The parameters associated with the filter |
411
|
|
|
* @param mixed \$orderBy The order string |
412
|
|
|
* @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement) |
413
|
|
|
* @param int \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY. |
414
|
|
|
* @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray |
415
|
|
|
*/ |
416
|
|
|
protected function find(\$filter = null, array \$parameters = [], \$orderBy=null, array \$additionalTablesFetch = [], \$mode = null) |
417
|
|
|
{ |
418
|
|
|
if (\$this->defaultSort && \$orderBy == null) { |
419
|
|
|
\$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection; |
420
|
|
|
} |
421
|
|
|
return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode); |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Get a single $beanClassWithoutNameSpace specified by its filters. |
426
|
|
|
* |
427
|
|
|
* @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description) |
428
|
|
|
* @param array \$parameters The parameters associated with the filter |
429
|
|
|
* @return $beanClassWithoutNameSpace |
430
|
|
|
*/ |
431
|
|
|
protected function findOne(\$filter=null, array \$parameters = []) |
432
|
|
|
{ |
433
|
|
|
return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* Sets the default column for default sorting. |
438
|
|
|
* |
439
|
|
|
* @param string \$defaultSort |
440
|
|
|
*/ |
441
|
|
|
public function setDefaultSort(\$defaultSort) |
442
|
|
|
{ |
443
|
|
|
\$this->defaultSort = \$defaultSort; |
444
|
|
|
} |
445
|
|
|
"; |
446
|
|
|
|
447
|
|
|
$str .= $findByDaoCode; |
448
|
|
|
$str .= '} |
449
|
|
|
'; |
450
|
|
|
|
451
|
|
|
$possibleBaseFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$baseClassName); |
452
|
|
|
if (empty($possibleBaseFileNames)) { |
453
|
|
|
// @codeCoverageIgnoreStart |
454
|
|
|
throw new TDBMException('Sorry, autoload namespace issue. The class "'.$baseClassName.'" is not autoloadable.'); |
455
|
|
|
// @codeCoverageIgnoreEnd |
456
|
|
|
} |
457
|
|
|
$possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0]; |
458
|
|
|
|
459
|
|
|
$this->ensureDirectoryExist($possibleBaseFileName); |
460
|
|
|
file_put_contents($possibleBaseFileName, $str); |
461
|
|
|
@chmod($possibleBaseFileName, 0664); |
|
|
|
|
462
|
|
|
|
463
|
|
|
$possibleFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$className); |
464
|
|
|
if (empty($possibleFileNames)) { |
465
|
|
|
// @codeCoverageIgnoreStart |
466
|
|
|
throw new TDBMException('Sorry, autoload namespace issue. The class "'.$className.'" is not autoloadable.'); |
467
|
|
|
// @codeCoverageIgnoreEnd |
468
|
|
|
} |
469
|
|
|
$possibleFileName = $this->rootPath.$possibleFileNames[0]; |
470
|
|
|
|
471
|
|
|
// Now, let's generate the "editable" class |
472
|
|
View Code Duplication |
if (!file_exists($possibleFileName)) { |
|
|
|
|
473
|
|
|
$str = "<?php |
474
|
|
|
|
475
|
|
|
/* |
476
|
|
|
* This file has been automatically generated by TDBM. |
477
|
|
|
* You can edit this file as it will not be overwritten. |
478
|
|
|
*/ |
479
|
|
|
|
480
|
|
|
namespace {$daonamespace}; |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table. |
484
|
|
|
*/ |
485
|
|
|
class $className extends $baseClassName |
486
|
|
|
{ |
487
|
|
|
|
488
|
|
|
} |
489
|
|
|
"; |
490
|
|
|
$this->ensureDirectoryExist($possibleFileName); |
491
|
|
|
file_put_contents($possibleFileName, $str); |
492
|
|
|
@chmod($possibleFileName, 0664); |
|
|
|
|
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* Generates the factory bean. |
498
|
|
|
* |
499
|
|
|
* @param Table[] $tableList |
500
|
|
|
*/ |
501
|
|
|
private function generateFactory(array $tableList, $daoFactoryClassName, $daoNamespace, ClassNameMapper $classNameMapper) |
502
|
|
|
{ |
503
|
|
|
// For each table, let's write a property. |
504
|
|
|
|
505
|
|
|
$str = "<?php |
506
|
|
|
|
507
|
|
|
/* |
508
|
|
|
* This file has been automatically generated by TDBM. |
509
|
|
|
* DO NOT edit this file, as it might be overwritten. |
510
|
|
|
*/ |
511
|
|
|
|
512
|
|
|
namespace {$daoNamespace}; |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM. |
516
|
|
|
* |
517
|
|
|
*/ |
518
|
|
|
class $daoFactoryClassName |
519
|
|
|
{ |
520
|
|
|
"; |
521
|
|
|
|
522
|
|
|
foreach ($tableList as $table) { |
523
|
|
|
$tableName = $table->getName(); |
524
|
|
|
$daoClassName = $this->getDaoNameFromTableName($tableName); |
525
|
|
|
$daoInstanceName = self::toVariableName($daoClassName); |
526
|
|
|
|
527
|
|
|
$str .= ' /** |
528
|
|
|
* @var '.$daoClassName.' |
529
|
|
|
*/ |
530
|
|
|
private $'.$daoInstanceName.'; |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* Returns an instance of the '.$daoClassName.' class. |
534
|
|
|
* |
535
|
|
|
* @return '.$daoClassName.' |
536
|
|
|
*/ |
537
|
|
|
public function get'.$daoClassName.'() |
538
|
|
|
{ |
539
|
|
|
return $this->'.$daoInstanceName.'; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
/** |
543
|
|
|
* Sets the instance of the '.$daoClassName.' class that will be returned by the factory getter. |
544
|
|
|
* |
545
|
|
|
* @param '.$daoClassName.' $'.$daoInstanceName.' |
546
|
|
|
*/ |
547
|
|
|
public function set'.$daoClassName.'('.$daoClassName.' $'.$daoInstanceName.') { |
548
|
|
|
$this->'.$daoInstanceName.' = $'.$daoInstanceName.'; |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
'; |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
$str .= ' |
555
|
|
|
} |
556
|
|
|
'; |
557
|
|
|
|
558
|
|
|
$possibleFileNames = $classNameMapper->getPossibleFileNames($daoNamespace.'\\'.$daoFactoryClassName); |
559
|
|
|
if (empty($possibleFileNames)) { |
560
|
|
|
throw new TDBMException('Sorry, autoload namespace issue. The class "'.$daoNamespace.'\\'.$daoFactoryClassName.'" is not autoloadable.'); |
561
|
|
|
} |
562
|
|
|
$possibleFileName = $this->rootPath.$possibleFileNames[0]; |
563
|
|
|
|
564
|
|
|
$this->ensureDirectoryExist($possibleFileName); |
565
|
|
|
file_put_contents($possibleFileName, $str); |
566
|
|
|
@chmod($possibleFileName, 0664); |
|
|
|
|
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* Transforms a string to camelCase (except the first letter will be uppercase too). |
571
|
|
|
* Underscores and spaces are removed and the first letter after the underscore is uppercased. |
572
|
|
|
* |
573
|
|
|
* @param $str string |
574
|
|
|
* |
575
|
|
|
* @return string |
576
|
|
|
*/ |
577
|
|
|
public static function toCamelCase($str) |
578
|
|
|
{ |
579
|
|
|
$str = strtoupper(substr($str, 0, 1)).substr($str, 1); |
580
|
|
|
while (true) { |
581
|
|
|
if (strpos($str, '_') === false && strpos($str, ' ') === false) { |
582
|
|
|
break; |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
$pos = strpos($str, '_'); |
586
|
|
|
if ($pos === false) { |
587
|
|
|
$pos = strpos($str, ' '); |
588
|
|
|
} |
589
|
|
|
$before = substr($str, 0, $pos); |
590
|
|
|
$after = substr($str, $pos + 1); |
591
|
|
|
$str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1); |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
return $str; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* Tries to put string to the singular form (if it is plural). |
599
|
|
|
* We assume the table names are in english. |
600
|
|
|
* |
601
|
|
|
* @param $str string |
602
|
|
|
* |
603
|
|
|
* @return string |
604
|
|
|
*/ |
605
|
|
|
public static function toSingular($str) |
606
|
|
|
{ |
607
|
|
|
return Inflector::singularize($str); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* Put the first letter of the string in lower case. |
612
|
|
|
* Very useful to transform a class name into a variable name. |
613
|
|
|
* |
614
|
|
|
* @param $str string |
615
|
|
|
* |
616
|
|
|
* @return string |
617
|
|
|
*/ |
618
|
|
|
public static function toVariableName($str) |
619
|
|
|
{ |
620
|
|
|
return strtolower(substr($str, 0, 1)).substr($str, 1); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
/** |
624
|
|
|
* Ensures the file passed in parameter can be written in its directory. |
625
|
|
|
* |
626
|
|
|
* @param string $fileName |
627
|
|
|
* |
628
|
|
|
* @throws TDBMException |
629
|
|
|
*/ |
630
|
|
|
private function ensureDirectoryExist($fileName) |
631
|
|
|
{ |
632
|
|
|
$dirName = dirname($fileName); |
633
|
|
|
if (!file_exists($dirName)) { |
634
|
|
|
$old = umask(0); |
635
|
|
|
$result = mkdir($dirName, 0775, true); |
636
|
|
|
umask($old); |
637
|
|
|
if ($result === false) { |
638
|
|
|
throw new TDBMException("Unable to create directory: '".$dirName."'."); |
639
|
|
|
} |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
/** |
644
|
|
|
* @param string $rootPath |
645
|
|
|
*/ |
646
|
|
|
public function setRootPath($rootPath) |
647
|
|
|
{ |
648
|
|
|
$this->rootPath = $rootPath; |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* @return string |
653
|
|
|
*/ |
654
|
|
|
public function getRootPath() |
655
|
|
|
{ |
656
|
|
|
return $this->rootPath; |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
/** |
660
|
|
|
* Transforms a DBAL type into a PHP type (for PHPDoc purpose). |
661
|
|
|
* |
662
|
|
|
* @param Type $type The DBAL type |
663
|
|
|
* |
664
|
|
|
* @return string The PHP type |
665
|
|
|
*/ |
666
|
|
|
public static function dbalTypeToPhpType(Type $type) |
667
|
|
|
{ |
668
|
|
|
$map = [ |
669
|
|
|
Type::TARRAY => 'array', |
670
|
|
|
Type::SIMPLE_ARRAY => 'array', |
671
|
|
|
Type::JSON_ARRAY => 'array', |
672
|
|
|
Type::BIGINT => 'string', |
673
|
|
|
Type::BOOLEAN => 'bool', |
674
|
|
|
Type::DATETIME => '\DateTimeInterface', |
675
|
|
|
Type::DATETIMETZ => '\DateTimeInterface', |
676
|
|
|
Type::DATE => '\DateTimeInterface', |
677
|
|
|
Type::TIME => '\DateTimeInterface', |
678
|
|
|
Type::DECIMAL => 'float', |
679
|
|
|
Type::INTEGER => 'int', |
680
|
|
|
Type::OBJECT => 'string', |
681
|
|
|
Type::SMALLINT => 'int', |
682
|
|
|
Type::STRING => 'string', |
683
|
|
|
Type::TEXT => 'string', |
684
|
|
|
Type::BINARY => 'string', |
685
|
|
|
Type::BLOB => 'string', |
686
|
|
|
Type::FLOAT => 'float', |
687
|
|
|
Type::GUID => 'string', |
688
|
|
|
]; |
689
|
|
|
|
690
|
|
|
return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName(); |
691
|
|
|
} |
692
|
|
|
|
693
|
|
|
/** |
694
|
|
|
* @param string $beanNamespace |
695
|
|
|
* |
696
|
|
|
* @return \string[] Returns a map mapping table name to beans name |
697
|
|
|
*/ |
698
|
|
|
public function buildTableToBeanMap($beanNamespace) |
699
|
|
|
{ |
700
|
|
|
$tableToBeanMap = []; |
701
|
|
|
|
702
|
|
|
$tables = $this->schema->getTables(); |
703
|
|
|
|
704
|
|
|
foreach ($tables as $table) { |
705
|
|
|
$tableName = $table->getName(); |
706
|
|
|
$tableToBeanMap[$tableName] = $beanNamespace.'\\'.self::getBeanNameFromTableName($tableName); |
707
|
|
|
} |
708
|
|
|
|
709
|
|
|
return $tableToBeanMap; |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|
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.