Complex classes like PostgreSqlSchemaManager 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 PostgreSqlSchemaManager, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class PostgreSqlSchemaManager extends AbstractSchemaManager |
||
34 | { |
||
35 | /** |
||
36 | * @var array |
||
37 | */ |
||
38 | private $existingSchemaPaths; |
||
39 | |||
40 | /** |
||
41 | * Gets all the existing schema names. |
||
42 | * |
||
43 | * @return array |
||
44 | */ |
||
45 | public function getSchemaNames() |
||
51 | |||
52 | /** |
||
53 | * Returns an array of schema search paths. |
||
54 | * |
||
55 | * This is a PostgreSQL only function. |
||
56 | * |
||
57 | * @return array |
||
58 | */ |
||
59 | public function getSchemaSearchPaths() |
||
70 | |||
71 | /** |
||
72 | * Gets names of all existing schemas in the current users search path. |
||
73 | * |
||
74 | * This is a PostgreSQL only function. |
||
75 | * |
||
76 | * @return array |
||
77 | */ |
||
78 | public function getExistingSchemaSearchPaths() |
||
86 | |||
87 | /** |
||
88 | * Sets or resets the order of the existing schemas in the current search path of the user. |
||
89 | * |
||
90 | * This is a PostgreSQL only function. |
||
91 | * |
||
92 | * @return void |
||
93 | */ |
||
94 | public function determineExistingSchemaSearchPaths() |
||
103 | |||
104 | /** |
||
105 | * {@inheritdoc} |
||
106 | */ |
||
107 | public function dropDatabase($database) |
||
108 | { |
||
109 | try { |
||
110 | parent::dropDatabase($database); |
||
111 | } catch (DriverException $exception) { |
||
112 | // If we have a SQLSTATE 55006, the drop database operation failed |
||
113 | // because of active connections on the database. |
||
114 | // To force dropping the database, we first have to close all active connections |
||
115 | // on that database and issue the drop database operation again. |
||
116 | if ($exception->getSQLState() !== '55006') { |
||
117 | throw $exception; |
||
118 | } |
||
119 | |||
120 | $this->_execSql( |
||
121 | [ |
||
122 | $this->_platform->getDisallowDatabaseConnectionsSQL($database), |
||
|
|||
123 | $this->_platform->getCloseActiveDatabaseConnectionsSQL($database), |
||
124 | ] |
||
125 | ); |
||
126 | |||
127 | parent::dropDatabase($database); |
||
128 | } |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * {@inheritdoc} |
||
133 | */ |
||
134 | protected function _getPortableTableForeignKeyDefinition($tableForeignKey) |
||
159 | |||
160 | /** |
||
161 | * {@inheritdoc} |
||
162 | */ |
||
163 | protected function _getPortableTriggerDefinition($trigger) |
||
167 | |||
168 | /** |
||
169 | * {@inheritdoc} |
||
170 | */ |
||
171 | protected function _getPortableViewDefinition($view) |
||
175 | |||
176 | /** |
||
177 | * {@inheritdoc} |
||
178 | */ |
||
179 | protected function _getPortableUserDefinition($user) |
||
186 | |||
187 | /** |
||
188 | * {@inheritdoc} |
||
189 | */ |
||
190 | protected function _getPortableTableDefinition($table) |
||
201 | |||
202 | /** |
||
203 | * {@inheritdoc} |
||
204 | * |
||
205 | * @license New BSD License |
||
206 | * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html |
||
207 | */ |
||
208 | protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) |
||
238 | |||
239 | /** |
||
240 | * {@inheritdoc} |
||
241 | */ |
||
242 | protected function _getPortableDatabaseDefinition($database) |
||
246 | |||
247 | /** |
||
248 | * {@inheritdoc} |
||
249 | */ |
||
250 | 1 | protected function _getPortableSequencesList($sequences) |
|
251 | { |
||
252 | 1 | $sequenceDefinitions = []; |
|
253 | |||
254 | 1 | foreach ($sequences as $sequence) { |
|
255 | 1 | if ($sequence['schemaname'] != 'public') { |
|
256 | 1 | $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; |
|
257 | } else { |
||
258 | $sequenceName = $sequence['relname']; |
||
259 | } |
||
260 | |||
261 | 1 | $sequenceDefinitions[$sequenceName] = $sequence; |
|
262 | } |
||
263 | |||
264 | 1 | $list = []; |
|
265 | |||
266 | 1 | foreach ($this->filterAssetNames(array_keys($sequenceDefinitions)) as $sequenceName) { |
|
267 | 1 | $list[] = $this->_getPortableSequenceDefinition($sequenceDefinitions[$sequenceName]); |
|
268 | } |
||
269 | |||
270 | 1 | return $list; |
|
271 | } |
||
272 | |||
273 | /** |
||
274 | * {@inheritdoc} |
||
275 | */ |
||
276 | protected function getPortableNamespaceDefinition(array $namespace) |
||
280 | |||
281 | /** |
||
282 | * {@inheritdoc} |
||
283 | */ |
||
284 | 1 | protected function _getPortableSequenceDefinition($sequence) |
|
296 | |||
297 | /** |
||
298 | * {@inheritdoc} |
||
299 | */ |
||
300 | protected function _getPortableTableColumnDefinition($tableColumn) |
||
454 | |||
455 | /** |
||
456 | * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. |
||
457 | * |
||
458 | * @param mixed $defaultValue |
||
459 | * |
||
460 | * @return mixed |
||
461 | */ |
||
462 | private function fixVersion94NegativeNumericDefaultValue($defaultValue) |
||
470 | |||
471 | /** |
||
472 | * Un-escape a default value as given by PostgreSQL |
||
473 | * |
||
474 | * @param null|string $default |
||
475 | * |
||
476 | * @return null|string |
||
477 | */ |
||
478 | private function fixDefaultValueQuotes(?string $default): ?string |
||
489 | } |
||
490 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: