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 EcDataUtils 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 EcDataUtils, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | class EcDataUtils { |
||
16 | /** |
||
17 | * Given an ec entity, return its id. |
||
18 | * |
||
19 | * @param object $ec |
||
20 | * An early childhood taxonomy term. |
||
21 | * |
||
22 | * @return int|null entity id |
||
23 | * The taxonomy term's id. |
||
24 | */ |
||
25 | public static function getEcId($ec) { |
||
26 | if (isset($ec->field_esd_ec_id['und']) && isset($ec->field_esd_ec_id['und'][0]) && isset($ec->field_esd_ec_id['und'][0]['value'])) { |
||
27 | return $ec->field_esd_ec_id['und'][0]['value']; |
||
28 | } |
||
29 | elseif (gettype($ec->field_esd_ec_id) == 'string') { |
||
30 | return $ec->field_esd_ec_id; |
||
31 | } |
||
32 | else { |
||
33 | return NULL; |
||
34 | } |
||
35 | } |
||
36 | |||
37 | /** |
||
38 | * Returns data tables that JOIN by buildingcode. |
||
39 | * |
||
40 | * @return array |
||
41 | * Array of drupal data table objects. |
||
42 | */ |
||
43 | public static function getDataTablesWithBcodes() { |
||
44 | return array_filter(data_get_all_tables(), function($table) { |
||
45 | return (isset($table->meta['join']) && isset($table->meta['join']['field_data_field_bcode'])); |
||
46 | }); |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * Returns data tables that JOIN by ESD internal school ID. |
||
51 | * |
||
52 | * @return array |
||
53 | * Array of drupal data table objects. |
||
54 | */ |
||
55 | public static function getDataTablesWithEsdSchids() { |
||
56 | return array_filter(data_get_all_tables(), function($table) { |
||
57 | return (isset($table->meta['join']) && isset($table->meta['join']['field_data_field_esd_schid'])); |
||
58 | }); |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Returns data tables that JOIN by earlychild programid. |
||
63 | * |
||
64 | * @return array |
||
65 | * Array of drupal data table objects. |
||
66 | */ |
||
67 | public static function getDataTablesWithProgramIds() { |
||
68 | return array_filter(data_get_all_tables(), function($table) { |
||
69 | return (isset($table->meta['join']) && isset($table->meta['join']['earlychild'])); |
||
70 | }); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Extracts table names from array of drupal data table objects. |
||
75 | * |
||
76 | * @return array |
||
77 | * table names |
||
78 | */ |
||
79 | public static function extractDataTableNames($tables) { |
||
82 | |||
83 | /** |
||
84 | * As seen in commerce_services: flatten fields. |
||
85 | * |
||
86 | * For the ESD api, we: |
||
87 | * * flatten fields (remove i18n & change multiple fields to arrays) |
||
88 | * * output taxonomy term references as [{tid:'tid',name:'name'},{...}] |
||
89 | * or just {tid:'tid',name:'name'} |
||
90 | * |
||
91 | * The original docs: |
||
92 | * |
||
93 | * Flattens field value arrays on the given entity. |
||
94 | * |
||
95 | * Field flattening in Commerce Services involves reducing their value arrays |
||
96 | * to just the current language of the entity and reducing fields with single |
||
97 | * column schemas to simple scalar values or arrays of scalar values. |
||
98 | * |
||
99 | * Note that because this function irreparably alters an entity's structure, |
||
100 | * it should only be called using a clone of the entity whose field value |
||
101 | * arrays should be flattened. Otherwise the flattening will affect the entity |
||
102 | * as stored in the entity cache, causing potential errors should that entity |
||
103 | * be loaded and manipulated later in the same request. |
||
104 | * |
||
105 | * @param string $entity_type |
||
106 | * The machine-name entity type of the given entity. |
||
107 | * @param object $cloned_entity |
||
108 | * A clone of the entity whose field value arrays should be flattened. |
||
109 | */ |
||
110 | public static function flattenFields($entity_type, $cloned_entity) { |
||
111 | $bundle = field_extract_bundle($entity_type, $cloned_entity); |
||
112 | $clone_wrapper = entity_metadata_wrapper($entity_type, $cloned_entity); |
||
113 | |||
114 | // Loop over every field instance on the given entity. |
||
115 | foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) { |
||
116 | $field_info = field_info_field($field_name); |
||
117 | |||
118 | // Add file & image URLs. |
||
119 | if (in_array($field_info['type'], array('file', 'image'))) { |
||
120 | $url_field_name = $field_name . '_url'; |
||
121 | // Set the URL for a single value field. |
||
122 | if ($field_info['cardinality'] == 1) { |
||
123 | $field_value = $clone_wrapper->{$field_name}->raw(); |
||
124 | $cloned_entity->{$url_field_name} = ''; |
||
125 | $url = NULL; |
||
126 | // If the field value contains a URI... |
||
127 | View Code Duplication | if (!empty($field_value['uri'])) { |
|
128 | // And we can generate a URL to the file at that URI... |
||
129 | $url = file_create_url($field_value['uri']); |
||
130 | if (!empty($url)) { |
||
131 | // Add it to the entity using the URL field name. |
||
132 | $cloned_entity->{$url_field_name} = $url; |
||
133 | } |
||
134 | } |
||
135 | } |
||
136 | else { |
||
137 | // Otherwise loop over the field and generate each URL. |
||
138 | $cloned_entity->{$url_field_name} = array(); |
||
139 | foreach ($clone_wrapper->{$field_name}->getIterator() as $delta => $field_wrapper) { |
||
140 | $field_value = $field_wrapper->raw(); |
||
141 | $url = NULL; |
||
142 | // If the field value contains a URI... |
||
143 | View Code Duplication | if (!empty($field_value['uri'])) { |
|
144 | // And we can generate a URL to the file at that URI... |
||
145 | $url = file_create_url($field_value['uri']); |
||
146 | if (!empty($url)) { |
||
147 | // Add it to the entity using the URL field name. |
||
148 | $cloned_entity->{$url_field_name}[$delta] = $url; |
||
149 | } |
||
150 | } |
||
151 | // If the field value did not have a URI or the URL to the file could not |
||
152 | // be determined, add an empty URL string to the entity. |
||
153 | if (empty($url)) { |
||
154 | $cloned_entity->{$url_field_name}[$delta] = ''; |
||
155 | } |
||
156 | } |
||
157 | } |
||
158 | } |
||
159 | |||
160 | // Set the field property to the raw wrapper value, which applies the |
||
161 | // desired flattening of the value array. |
||
162 | // For taxonomy term refs, format nicely using loadtermnames module 'name' |
||
163 | if ($clone_wrapper->{$field_name}->type() == 'taxonomy_term') { |
||
164 | $term = $clone_wrapper->{$field_name}->value(); |
||
165 | // Explicitly set single-value taxonomy_term fields as null, rather than |
||
166 | // an empty array. |
||
167 | if (count($cloned_entity->{$field_name}) == 0) { |
||
168 | $cloned_entity->{$field_name} = NULL; |
||
169 | } |
||
170 | if ($cloned_entity->{$field_name}) { |
||
171 | $cloned_entity->{$field_name} = ['tid' => $term->tid, 'name' => $term->name]; |
||
172 | } |
||
173 | } |
||
174 | elseif ($clone_wrapper->{$field_name}->type() == 'list<taxonomy_term>') { |
||
175 | $new_val = []; |
||
176 | foreach ($clone_wrapper->{$field_name}->value() as $term) { |
||
177 | $new_val[] = ['tid' => $term->tid, 'name' => $term->name]; |
||
178 | } |
||
179 | $cloned_entity->{$field_name} = $new_val; |
||
180 | } |
||
181 | elseif ($clone_wrapper->{$field_name}->type() == 'list<text>') { |
||
182 | $opts = $clone_wrapper->{$field_name}->optionsList(); |
||
183 | $newval = []; |
||
184 | $iter = 0; |
||
185 | |||
186 | foreach ($clone_wrapper->{$field_name}->value() as $machine_val) { |
||
187 | $label = $opts[$machine_val]; |
||
188 | |||
189 | $newval[$iter]['machine_name'] = $machine_val; |
||
190 | // For fields with only labels, set both as the same thing. |
||
191 | $newval[$iter]['label'] = ($label) ? $label : $machine_val; |
||
192 | $iter++; |
||
193 | } |
||
194 | unset($iter); |
||
195 | |||
196 | $cloned_entity->{$field_name} = $newval; |
||
197 | unset($newval); |
||
198 | } |
||
199 | elseif ($clone_wrapper->{$field_name}->type() == 'text' && $clone_wrapper->{$field_name}->label()) { |
||
200 | $cloned_entity->{$field_name} = [ |
||
201 | 'machine_name' => $clone_wrapper->{$field_name}->value(), |
||
202 | 'label' => $clone_wrapper->{$field_name}->label(), |
||
203 | ]; |
||
204 | } |
||
205 | else { |
||
206 | $cloned_entity->{$field_name} = $clone_wrapper->{$field_name}->raw(); |
||
207 | } |
||
208 | } |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Converts underscore_name to camelName. |
||
213 | */ |
||
214 | public static function underscoreToCamel($underscore_name) { |
||
215 | $underscore_name[0] = strtoupper($underscore_name[0]); |
||
216 | |||
217 | return preg_replace_callback('/_([a-z0-9])/', function($c) { |
||
218 | return strtoupper($c[1]); |
||
219 | }, $underscore_name); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Converts camelName to underscore_name. |
||
224 | */ |
||
225 | public static function camelToUnderscore($camel_name) { |
||
228 | |||
229 | /** |
||
230 | * As seen in commerce_services... |
||
231 | * |
||
232 | * Returns a list of properties for the specified entity type. |
||
233 | * |
||
234 | * For the purpose of the Commerce Services module, the properties returned |
||
235 | * are those that correspond to a database column as determined by the Entity |
||
236 | * API. These may be used to filter and sort index queries. |
||
237 | * |
||
238 | * @param string $entity_type |
||
239 | * Machine-name of the entity type whose properties should be returned. |
||
240 | * |
||
241 | * @return array |
||
242 | * An associative array of properties for the specified entity type with the |
||
243 | * key being the property name and the value being the corresponding schema |
||
244 | * field on the entity type's base table. |
||
245 | */ |
||
246 | public static function entityTypeProperties($entity_type) { |
||
271 | |||
272 | /** |
||
273 | * As seen in commerce_services... |
||
274 | * |
||
275 | * Returns a list of fields for the specified entity type. |
||
276 | * |
||
277 | * @param string $entity_type |
||
278 | * Machine-name of the entity type whose properties should be returned. |
||
279 | * @param string $bundle |
||
280 | * Optional bundle name to limit the returned fields to. |
||
281 | * |
||
282 | * @return array |
||
283 | * An associative array of fields for the specified entity type with the key |
||
284 | * being the field name and the value being the Entity API property type. |
||
285 | */ |
||
286 | public static function entityTypeFields($entity_type, $bundle = NULL) { |
||
320 | |||
321 | /** |
||
322 | * As seen in commerce_services: filtering. |
||
323 | * |
||
324 | * Adds property and field conditions to an index EntityFieldQuery. |
||
325 | * |
||
326 | * @param \EntityFieldQuery $query |
||
327 | * The EntityFieldQuery object being built for the index query. |
||
328 | * @param string $entity_type |
||
329 | * Machine-name of the entity type of the index query. |
||
330 | * @param array $filter |
||
331 | * An associative array of property names, single column field names, or |
||
332 | * multi-column field column names with their values to use to filter the |
||
333 | * result set of the index request. |
||
334 | * @param array $filter_op |
||
335 | * An associative array of field and property names with the operators to |
||
336 | * use when applying their filter conditions to the index request query. |
||
337 | */ |
||
338 | public static function indexQueryFilter(\EntityFieldQuery $query, $entity_type, array $filter, array $filter_op) { |
||
404 | |||
405 | /** |
||
406 | * As seen in commerce_services: sorting. |
||
407 | * |
||
408 | * Adds property and field order by directions to an index EntityFieldQuery. |
||
409 | * |
||
410 | * @param \EntityFieldQuery $query |
||
411 | * The EntityFieldQuery object being built for the index query. |
||
412 | * @param string $entity_type |
||
413 | * Machine-name of the entity type of the index query. |
||
414 | * @param array $sort_by |
||
415 | * An array of database fields to sort the query by, with sort fields being |
||
416 | * valid properties, single column field names, or multi-column field column |
||
417 | * names for the matching entity type. |
||
418 | * @param array $sort_order |
||
419 | * The corresponding sort orders for the fields specified in the $sort_by |
||
420 | * array; one of either 'DESC' or 'ASC'. |
||
421 | */ |
||
422 | public static function indexQuerySort(\EntityFieldQuery $query, $entity_type, array $sort_by, array $sort_order) { |
||
473 | |||
474 | } |
||
475 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.