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 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 |
||
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) |
|
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) |
||
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) |
||
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) |
||
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) |
||
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) |
||
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) |
||
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) |
||
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) |
||
495 | |||
496 | /** |
||
497 | * Generates the factory bean. |
||
498 | * |
||
499 | * @param Table[] $tableList |
||
500 | */ |
||
501 | private function generateFactory(array $tableList, $daoFactoryClassName, $daoNamespace, ClassNameMapper $classNameMapper) |
||
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) |
||
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) |
||
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) |
||
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) |
||
642 | |||
643 | /** |
||
644 | * @param string $rootPath |
||
645 | */ |
||
646 | public function setRootPath($rootPath) |
||
650 | |||
651 | /** |
||
652 | * @return string |
||
653 | */ |
||
654 | public function getRootPath() |
||
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) |
||
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) |
||
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.