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 GenericEngine 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 GenericEngine, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
149 | class GenericEngine extends BaseEngine |
||
150 | { |
||
151 | |||
152 | /** |
||
153 | * {@inheritDoc} |
||
154 | * |
||
155 | * - operators: A list of registered operators methods as `name` => |
||
156 | * `methodName`. |
||
157 | * |
||
158 | * - strict: Used to filter any invalid word. Set to a string representing a |
||
159 | * regular expression describing which charaters should be removed. Or set |
||
160 | * to TRUE to used default discard criteria: only letters, digits and few |
||
161 | * basic symbols (".", ",", "/", etc). Defaults to TRUE (custom filter |
||
162 | * regex). |
||
163 | * |
||
164 | * - bannedWords: Array list of banned words, or a callable that should decide |
||
165 | * if the given word is banned or not. Defaults to empty array (allow |
||
166 | * everything). |
||
167 | * |
||
168 | * - fulltext: Whether to use FULLTEXT search whenever it is possible. Defaults to |
||
169 | * TRUE. This feature is only supported for MySQL InnoDB database engines. |
||
170 | * |
||
171 | * - datasetTable: Name of the MySQL table where words dataset should be stored and |
||
172 | * read from. This allows you to split large sets into different tables. |
||
173 | */ |
||
174 | protected $_defaultConfig = [ |
||
175 | 'operators' => [], |
||
176 | 'strict' => true, |
||
177 | 'bannedWords' => [], |
||
178 | 'fulltext' => true, |
||
179 | 'datasetTable' => 'search_datasets', |
||
180 | ]; |
||
181 | |||
182 | /** |
||
183 | * {@inheritDoc} |
||
184 | * |
||
185 | * @throws \Search\Engine\Generic\Exception\CompoundPrimaryKeyException When using |
||
186 | * compound primary keys |
||
187 | */ |
||
188 | public function __construct(Table $table, array $config = []) |
||
208 | |||
209 | /** |
||
210 | * {@inheritDoc} |
||
211 | */ |
||
212 | public function index(EntityInterface $entity) |
||
237 | |||
238 | /** |
||
239 | * {@inheritDoc} |
||
240 | */ |
||
241 | public function delete(EntityInterface $entity) |
||
250 | |||
251 | /** |
||
252 | * {@inheritDoc} |
||
253 | */ |
||
254 | public function get(EntityInterface $entity) |
||
264 | |||
265 | /** |
||
266 | * {@inheritDoc} |
||
267 | * |
||
268 | * It looks for search-criteria and applies them over the query object. For |
||
269 | * example, given the criteria below: |
||
270 | * |
||
271 | * "this phrase" -"and not this one" |
||
272 | * |
||
273 | * Alters the query object as follow: |
||
274 | * |
||
275 | * ```php |
||
276 | * $query->where([ |
||
277 | * 'indexed_words LIKE' => '%this phrase%', |
||
278 | * 'indexed_words NOT LIKE' => '%and not this one%' |
||
279 | * ]); |
||
280 | * ``` |
||
281 | * |
||
282 | * The `AND` & `OR` keywords are allowed to create complex conditions. For |
||
283 | * example: |
||
284 | * |
||
285 | * "this phrase" OR -"and not this one" AND "this" |
||
286 | * |
||
287 | * Will produce something like: |
||
288 | * |
||
289 | * ```php |
||
290 | * $query->where(['indexed_words LIKE' => '%this phrase%']) |
||
291 | * ->orWhere(['indexed_words NOT LIKE' => '%and not this one%']); |
||
292 | * ->andWhere(['indexed_words LIKE' => '%this%']); |
||
293 | * ``` |
||
294 | */ |
||
295 | public function search($criteria, Query $query) |
||
312 | |||
313 | /** |
||
314 | * Scopes the given query using the given operator token. |
||
315 | * |
||
316 | * @param \Cake\ORM\Query $query The query to scope |
||
317 | * @param \Search\Token $token Token describing an operator. e.g `-op_name:op_value` |
||
318 | * @return \Cake\ORM\Query Scoped query |
||
319 | */ |
||
320 | protected function _scopeOperator(Query $query, TokenInterface $token) |
||
324 | |||
325 | /** |
||
326 | * Scopes the given query using the given words token. |
||
327 | * |
||
328 | * @param \Cake\ORM\Query $query The query to scope |
||
329 | * @param \Search\TokenInterface $token Token describing a words sequence. e.g `this is a phrase` |
||
330 | * @return \Cake\ORM\Query Scoped query |
||
331 | */ |
||
332 | protected function _scopeWords(Query $query, TokenInterface $token) |
||
357 | |||
358 | /** |
||
359 | * Similar to "_scopeWords" but using MySQL's fulltext indexes. |
||
360 | * |
||
361 | * @param \Cake\ORM\Query $query The query to scope |
||
362 | * @param \Search\TokenInterface $token Token describing a words sequence. e.g `this is a phrase` |
||
363 | * @return \Cake\ORM\Query Scoped query |
||
364 | */ |
||
365 | protected function _scopeWordsInFulltext(Query $query, TokenInterface $token) |
||
388 | |||
389 | /** |
||
390 | * Whether FullText index is available or not and should be used. |
||
391 | * |
||
392 | * @return bool True if enabled and should be used, false otherwise |
||
393 | */ |
||
394 | protected function _isFullTextEnabled() |
||
428 | |||
429 | /** |
||
430 | * Gets a list of storage engine's stopwords. That is words that is considered |
||
431 | * common or Trivial enough that it is omitted from the search index and ignored |
||
432 | * in search queries |
||
433 | * |
||
434 | * @return array List of words |
||
435 | */ |
||
436 | protected function _stopWords() |
||
459 | |||
460 | /** |
||
461 | * Calculates entity's primary key. |
||
462 | * |
||
463 | * @param \Cake\Datasource\EntityInterface $entity The entity |
||
464 | * @return string |
||
465 | * @deprecated Use direct access as `$entity->get($this->config('pk'))` |
||
466 | */ |
||
467 | protected function _entityId(EntityInterface $entity) |
||
471 | |||
472 | /** |
||
473 | * Extracts a list of words to by indexed for given entity. |
||
474 | * |
||
475 | * NOTE: Words can be repeated, this allows to search phrases. |
||
476 | * |
||
477 | * @param \Cake\Datasource\EntityInterface $entity The entity for which generate |
||
478 | * the list of words |
||
479 | * @return string Space-separated list of words. e.g. `cat dog this that` |
||
480 | */ |
||
481 | protected function _extractEntityWords(EntityInterface $entity) |
||
509 | |||
510 | /** |
||
511 | * Removes any invalid word from the given text. |
||
512 | * |
||
513 | * @param string $text The text to filter |
||
514 | * @return string Filtered text |
||
515 | */ |
||
516 | protected function _filterText($text) |
||
540 | } |
||
541 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.