Total Complexity | 78 |
Total Lines | 775 |
Duplicated Lines | 1.68 % |
Changes | 0 |
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 CollectionLoader 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.
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 CollectionLoader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class CollectionLoader implements LoggerAwareInterface |
||
31 | { |
||
32 | use LoggerAwareTrait; |
||
33 | |||
34 | /** |
||
35 | * The source to load objects from. |
||
36 | * |
||
37 | * @var SourceInterface |
||
38 | */ |
||
39 | private $source; |
||
40 | |||
41 | /** |
||
42 | * The model to load the collection from. |
||
43 | * |
||
44 | * @var ModelInterface |
||
45 | */ |
||
46 | private $model; |
||
47 | |||
48 | /** |
||
49 | * Store the factory instance for the current class. |
||
50 | * |
||
51 | * @var FactoryInterface |
||
52 | */ |
||
53 | private $factory; |
||
54 | |||
55 | /** |
||
56 | * The callback routine applied to every object added to the collection. |
||
57 | * |
||
58 | * @var callable|null |
||
59 | */ |
||
60 | private $callback; |
||
61 | |||
62 | /** |
||
63 | * The field which defines the data's model. |
||
64 | * |
||
65 | * @var string|null |
||
66 | */ |
||
67 | private $dynamicTypeField; |
||
68 | |||
69 | /** |
||
70 | * The class name of the collection to use. |
||
71 | * |
||
72 | * Must be a fully-qualified PHP namespace and an implementation of {@see ArrayAccess}. |
||
73 | * |
||
74 | * @var string |
||
75 | */ |
||
76 | private $collectionClass = Collection::class; |
||
77 | |||
78 | /** |
||
79 | * Return a new CollectionLoader object. |
||
80 | * |
||
81 | * @param array $data The loader's dependencies. |
||
82 | */ |
||
83 | public function __construct(array $data) |
||
84 | { |
||
85 | if (!isset($data['logger'])) { |
||
86 | $data['logger'] = new NullLogger(); |
||
87 | } |
||
88 | |||
89 | $this->setLogger($data['logger']); |
||
90 | |||
91 | if (isset($data['collection'])) { |
||
92 | $this->setCollectionClass($data['collection']); |
||
93 | } |
||
94 | |||
95 | if (isset($data['factory'])) { |
||
96 | $this->setFactory($data['factory']); |
||
97 | } |
||
98 | |||
99 | if (isset($data['model'])) { |
||
100 | $this->setModel($data['model']); |
||
101 | } |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Set an object model factory. |
||
106 | * |
||
107 | * @param FactoryInterface $factory The model factory, to create objects. |
||
108 | * @return self |
||
109 | */ |
||
110 | public function setFactory(FactoryInterface $factory) |
||
111 | { |
||
112 | $this->factory = $factory; |
||
113 | |||
114 | return $this; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Retrieve the object model factory. |
||
119 | * |
||
120 | * @throws RuntimeException If the model factory was not previously set. |
||
121 | * @return FactoryInterface |
||
122 | */ |
||
123 | protected function factory() |
||
124 | { |
||
125 | if ($this->factory === null) { |
||
126 | throw new RuntimeException( |
||
127 | sprintf('Model Factory is not defined for "%s"', get_class($this)) |
||
128 | ); |
||
129 | } |
||
130 | |||
131 | return $this->factory; |
||
132 | } |
||
133 | |||
134 | /** |
||
135 | * Set the loader settings. |
||
136 | * |
||
137 | * @param array $data Data to assign to the loader. |
||
138 | * @return self |
||
139 | */ |
||
140 | View Code Duplication | public function setData(array $data) |
|
|
|||
141 | { |
||
142 | foreach ($data as $key => $val) { |
||
143 | $setter = $this->setter($key); |
||
144 | if (is_callable([ $this, $setter ])) { |
||
145 | $this->{$setter}($val); |
||
146 | } else { |
||
147 | $this->{$key} = $val; |
||
148 | } |
||
149 | } |
||
150 | |||
151 | return $this; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Retrieve the source to load objects from. |
||
156 | * |
||
157 | * @throws RuntimeException If no source has been defined. |
||
158 | * @return SourceInterface |
||
159 | */ |
||
160 | public function source() |
||
161 | { |
||
162 | if ($this->source === null) { |
||
163 | throw new RuntimeException('No source set.'); |
||
164 | } |
||
165 | |||
166 | return $this->source; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Set the source to load objects from. |
||
171 | * |
||
172 | * @param SourceInterface $source A data source. |
||
173 | * @return self |
||
174 | */ |
||
175 | public function setSource(SourceInterface $source) |
||
176 | { |
||
177 | $source->reset(); |
||
178 | |||
179 | $this->source = $source; |
||
180 | |||
181 | return $this; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Reset everything but the model. |
||
186 | * |
||
187 | * @return self |
||
188 | */ |
||
189 | public function reset() |
||
190 | { |
||
191 | if ($this->source) { |
||
192 | $this->source()->reset(); |
||
193 | } |
||
194 | |||
195 | $this->callback = null; |
||
196 | $this->dynamicTypeField = null; |
||
197 | |||
198 | return $this; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Retrieve the object model. |
||
203 | * |
||
204 | * @throws RuntimeException If no model has been defined. |
||
205 | * @return ModelInterface |
||
206 | */ |
||
207 | public function model() |
||
208 | { |
||
209 | if ($this->model === null) { |
||
210 | throw new RuntimeException('The collection loader must have a model.'); |
||
211 | } |
||
212 | |||
213 | return $this->model; |
||
214 | } |
||
215 | |||
216 | /** |
||
217 | * Determine if the loader has an object model. |
||
218 | * |
||
219 | * @return boolean |
||
220 | */ |
||
221 | public function hasModel() |
||
222 | { |
||
223 | return !!$this->model; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Set the model to use for the loaded objects. |
||
228 | * |
||
229 | * @param string|ModelInterface $model An object model. |
||
230 | * @throws InvalidArgumentException If the given argument is not a model. |
||
231 | * @return self |
||
232 | */ |
||
233 | public function setModel($model) |
||
234 | { |
||
235 | if (is_string($model)) { |
||
236 | $model = $this->factory()->get($model); |
||
237 | } |
||
238 | |||
239 | if (!$model instanceof ModelInterface) { |
||
240 | throw new InvalidArgumentException( |
||
241 | sprintf( |
||
242 | 'The model must be an instance of "%s"', |
||
243 | ModelInterface::class |
||
244 | ) |
||
245 | ); |
||
246 | } |
||
247 | |||
248 | $this->model = $model; |
||
249 | |||
250 | $this->setSource($model->source()); |
||
251 | |||
252 | return $this; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * @param string $field The field to use for dynamic object type. |
||
257 | * @throws InvalidArgumentException If the field is not a string. |
||
258 | * @return self |
||
259 | */ |
||
260 | public function setDynamicTypeField($field) |
||
261 | { |
||
262 | if (!is_string($field)) { |
||
263 | throw new InvalidArgumentException( |
||
264 | 'Dynamic type field must be a string' |
||
265 | ); |
||
266 | } |
||
267 | |||
268 | $this->dynamicTypeField = $field; |
||
269 | |||
270 | return $this; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Alias of {@see SourceInterface::properties()} |
||
275 | * |
||
276 | * @return array |
||
277 | */ |
||
278 | public function properties() |
||
279 | { |
||
280 | return $this->source()->properties(); |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Alias of {@see SourceInterface::setProperties()} |
||
285 | * |
||
286 | * @param array $properties An array of property identifiers. |
||
287 | * @return self |
||
288 | */ |
||
289 | public function setProperties(array $properties) |
||
290 | { |
||
291 | $this->source()->setProperties($properties); |
||
292 | |||
293 | return $this; |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Alias of {@see SourceInterface::addProperty()} |
||
298 | * |
||
299 | * @param string $property A property identifier. |
||
300 | * @return self |
||
301 | */ |
||
302 | public function addProperty($property) |
||
303 | { |
||
304 | $this->source()->addProperty($property); |
||
305 | |||
306 | return $this; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Set "search" keywords to filter multiple properties. |
||
311 | * |
||
312 | * @param array $keywords An array of keywords and properties. |
||
313 | * Expected format: `[ "search query", [ "field names…" ] ]`. |
||
314 | * @return self |
||
315 | */ |
||
316 | public function setKeywords(array $keywords) |
||
317 | { |
||
318 | foreach ($keywords as $query) { |
||
319 | $keyword = $query[0]; |
||
320 | $properties = (isset($query[1]) ? (array)$query[1] : null); |
||
321 | $this->addKeyword($keyword, $properties); |
||
322 | } |
||
323 | |||
324 | return $this; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Add a "search" keyword filter to multiple properties. |
||
329 | * |
||
330 | * @param string $keyword A value to match among $properties. |
||
331 | * @param array $properties One or more of properties to search amongst. |
||
332 | * @return self |
||
333 | */ |
||
334 | public function addKeyword($keyword, array $properties = null) |
||
335 | { |
||
336 | if ($properties === null) { |
||
337 | $properties = []; |
||
338 | } |
||
339 | |||
340 | foreach ($properties as $propertyIdent) { |
||
341 | $val = ('%'.$keyword.'%'); |
||
342 | $this->addFilter([ |
||
343 | 'property' => $propertyIdent, |
||
344 | 'operator' => 'LIKE', |
||
345 | 'value' => $val, |
||
346 | 'operand' => 'OR' |
||
347 | ]); |
||
348 | } |
||
349 | |||
350 | return $this; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Alias of {@see SourceInterface::filters()} |
||
355 | * |
||
356 | * @return FilterInterface[] |
||
357 | */ |
||
358 | public function filters() |
||
359 | { |
||
360 | return $this->source()->filters(); |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Alias of {@see SourceInterface::setFilters()} |
||
365 | * |
||
366 | * @param array $filters An array of filters. |
||
367 | * @return self |
||
368 | */ |
||
369 | public function setFilters(array $filters) |
||
370 | { |
||
371 | $this->source()->setFilters($filters); |
||
372 | return $this; |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * Alias of {@see SourceInterface::addFilters()} |
||
377 | * |
||
378 | * @param array $filters An array of filters. |
||
379 | * @return self |
||
380 | */ |
||
381 | public function addFilters(array $filters) |
||
382 | { |
||
383 | foreach ($filters as $f) { |
||
384 | $this->addFilter($f); |
||
385 | } |
||
386 | return $this; |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Alias of {@see SourceInterface::addFilter()} |
||
391 | * |
||
392 | * @param mixed $param The property to filter by, |
||
393 | * a {@see FilterInterface} object, |
||
394 | * or a filter array structure. |
||
395 | * @param mixed $value Optional value for the property to compare against. |
||
396 | * Only used if the first argument is a string. |
||
397 | * @param array $options Optional extra settings to apply on the filter. |
||
398 | * @return self |
||
399 | */ |
||
400 | public function addFilter($param, $value = null, array $options = null) |
||
401 | { |
||
402 | $this->source()->addFilter($param, $value, $options); |
||
403 | return $this; |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Alias of {@see SourceInterface::orders()} |
||
408 | * |
||
409 | * @return OrderInterface[] |
||
410 | */ |
||
411 | public function orders() |
||
412 | { |
||
413 | return $this->source()->orders(); |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Alias of {@see SourceInterface::setOrders()} |
||
418 | * |
||
419 | * @param array $orders An array of orders. |
||
420 | * @return self |
||
421 | */ |
||
422 | public function setOrders(array $orders) |
||
423 | { |
||
424 | $this->source()->setOrders($orders); |
||
425 | return $this; |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Alias of {@see SourceInterface::addOrders()} |
||
430 | * |
||
431 | * @param array $orders An array of orders. |
||
432 | * @return self |
||
433 | */ |
||
434 | public function addOrders(array $orders) |
||
435 | { |
||
436 | foreach ($orders as $o) { |
||
437 | $this->addOrder($o); |
||
438 | } |
||
439 | return $this; |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Alias of {@see SourceInterface::addOrder()} |
||
444 | * |
||
445 | * @param mixed $param The property to sort by, |
||
446 | * a {@see OrderInterface} object, |
||
447 | * or a order array structure. |
||
448 | * @param string $mode Optional sorting mode. |
||
449 | * Defaults to ascending if a property is provided. |
||
450 | * @param array $options Optional extra settings to apply on the order. |
||
451 | * @return self |
||
452 | */ |
||
453 | public function addOrder($param, $mode = 'asc', array $options = null) |
||
454 | { |
||
455 | $this->source()->addOrder($param, $mode, $options); |
||
456 | return $this; |
||
457 | } |
||
458 | |||
459 | /** |
||
460 | * Alias of {@see SourceInterface::pagination()} |
||
461 | * |
||
462 | * @return PaginationInterface |
||
463 | */ |
||
464 | public function pagination() |
||
467 | } |
||
468 | |||
469 | /** |
||
470 | * Alias of {@see SourceInterface::setPagination()} |
||
471 | * |
||
472 | * @param mixed $param An associative array of pagination settings. |
||
473 | * @return self |
||
474 | */ |
||
475 | public function setPagination($param) |
||
476 | { |
||
477 | $this->source()->setPagination($param); |
||
478 | |||
479 | return $this; |
||
480 | } |
||
481 | |||
482 | /** |
||
483 | * Alias of {@see PaginationInterface::page()} |
||
484 | * |
||
485 | * @return integer |
||
486 | */ |
||
487 | public function page() |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Alias of {@see PaginationInterface::pagination()} |
||
494 | * |
||
495 | * @param integer $page A page number. |
||
496 | * @return self |
||
497 | */ |
||
498 | public function setPage($page) |
||
499 | { |
||
500 | $this->pagination()->setPage($page); |
||
501 | |||
502 | return $this; |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * Alias of {@see PaginationInterface::numPerPage()} |
||
507 | * |
||
508 | * @return integer |
||
509 | */ |
||
510 | public function numPerPage() |
||
511 | { |
||
512 | return $this->pagination()->numPerPage(); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * Alias of {@see PaginationInterface::setNumPerPage()} |
||
517 | * |
||
518 | * @param integer $num The number of items to display per page. |
||
519 | * @return self |
||
520 | */ |
||
521 | public function setNumPerPage($num) |
||
522 | { |
||
523 | $this->pagination()->setNumPerPage($num); |
||
524 | |||
525 | return $this; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Set the callback routine applied to every object added to the collection. |
||
530 | * |
||
531 | * @param callable $callback The callback routine. |
||
532 | * @return self |
||
533 | */ |
||
534 | public function setCallback(callable $callback) |
||
535 | { |
||
536 | $this->callback = $callback; |
||
537 | |||
538 | return $this; |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Retrieve the callback routine applied to every object added to the collection. |
||
543 | * |
||
544 | * @return callable|null |
||
545 | */ |
||
546 | public function callback() |
||
547 | { |
||
548 | return $this->callback; |
||
549 | } |
||
550 | |||
551 | /** |
||
552 | * Load a collection from source. |
||
553 | * |
||
554 | * @param string|null $ident Optional. A pre-defined list to use from the model. |
||
555 | * @param callable|null $callback Process each entity after applying raw data. |
||
556 | * Leave blank to use {@see CollectionLoader::callback()}. |
||
557 | * @param callable|null $before Process each entity before applying raw data. |
||
558 | * @throws Exception If the database connection fails. |
||
559 | * @return ModelInterface[]|ArrayAccess |
||
560 | */ |
||
561 | public function load($ident = null, callable $callback = null, callable $before = null) |
||
562 | { |
||
563 | // Unused. |
||
564 | unset($ident); |
||
565 | |||
566 | $query = $this->source()->sqlLoad(); |
||
567 | |||
568 | return $this->loadFromQuery($query, $callback, $before); |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Get the total number of items for this collection query. |
||
573 | * |
||
574 | * @throws RuntimeException If the database connection fails. |
||
575 | * @return integer |
||
576 | */ |
||
577 | public function loadCount() |
||
578 | { |
||
579 | $query = $this->source()->sqlLoadCount(); |
||
580 | |||
581 | $db = $this->source()->db(); |
||
582 | if (!$db) { |
||
583 | throw new RuntimeException( |
||
584 | 'Could not instanciate a database connection.' |
||
585 | ); |
||
586 | } |
||
587 | $this->logger->debug($query); |
||
588 | |||
589 | $sth = $db->prepare($query); |
||
590 | $sth->execute(); |
||
591 | $res = $sth->fetchColumn(0); |
||
592 | |||
593 | return (int)$res; |
||
594 | } |
||
595 | |||
596 | /** |
||
597 | * Load list from query. |
||
598 | * |
||
599 | * **Example — Binding values to $query** |
||
600 | * |
||
601 | * ```php |
||
602 | * $this->loadFromQuery([ |
||
603 | * 'SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour', |
||
604 | * [ |
||
605 | * 'calories' => 150, |
||
606 | * 'colour' => 'red' |
||
607 | * ], |
||
608 | * [ 'calories' => PDO::PARAM_INT ] |
||
609 | * ]); |
||
610 | * ``` |
||
611 | * |
||
612 | * @param string|array $query The SQL query as a string or an array composed of the query, |
||
613 | * parameter binds, and types of parameter bindings. |
||
614 | * @param callable|null $callback Process each entity after applying raw data. |
||
615 | * Leave blank to use {@see CollectionLoader::callback()}. |
||
616 | * @param callable|null $before Process each entity before applying raw data. |
||
617 | * @throws RuntimeException If the database connection fails. |
||
618 | * @throws InvalidArgumentException If the SQL string/set is invalid. |
||
619 | * @return ModelInterface[]|ArrayAccess |
||
620 | */ |
||
621 | public function loadFromQuery($query, callable $callback = null, callable $before = null) |
||
622 | { |
||
623 | $db = $this->source()->db(); |
||
624 | |||
625 | if (!$db) { |
||
626 | throw new RuntimeException( |
||
627 | 'Could not instanciate a database connection.' |
||
628 | ); |
||
629 | } |
||
630 | |||
631 | /** @todo Filter binds */ |
||
632 | if (is_string($query)) { |
||
633 | $this->logger->debug($query); |
||
634 | $sth = $db->prepare($query); |
||
635 | $sth->execute(); |
||
636 | } elseif (is_array($query)) { |
||
637 | list($query, $binds, $types) = array_pad($query, 3, []); |
||
638 | $sth = $this->source()->dbQuery($query, $binds, $types); |
||
639 | } else { |
||
640 | throw new InvalidArgumentException(sprintf( |
||
641 | 'The SQL query must be a string or an array: '. |
||
642 | '[ string $query, array $binds, array $dataTypes ]; '. |
||
643 | 'received %s', |
||
644 | is_object($query) ? get_class($query) : $query |
||
645 | )); |
||
646 | } |
||
647 | |||
648 | $sth->setFetchMode(PDO::FETCH_ASSOC); |
||
649 | |||
650 | if ($callback === null) { |
||
651 | $callback = $this->callback(); |
||
652 | } |
||
653 | |||
654 | return $this->processCollection($sth, $before, $callback); |
||
655 | } |
||
656 | |||
657 | /** |
||
658 | * Process the collection of raw data. |
||
659 | * |
||
660 | * @param mixed[]|Traversable $results The raw result set. |
||
661 | * @param callable|null $before Process each entity before applying raw data. |
||
662 | * @param callable|null $after Process each entity after applying raw data. |
||
663 | * @return ModelInterface[]|ArrayAccess |
||
664 | */ |
||
665 | protected function processCollection($results, callable $before = null, callable $after = null) |
||
666 | { |
||
667 | $collection = $this->createCollection(); |
||
668 | foreach ($results as $objData) { |
||
669 | $obj = $this->processModel($objData, $before, $after); |
||
670 | |||
671 | if ($obj instanceof ModelInterface) { |
||
672 | $collection[] = $obj; |
||
673 | } |
||
674 | } |
||
675 | |||
676 | return $collection; |
||
677 | } |
||
678 | |||
679 | /** |
||
680 | * Process the raw data for one model. |
||
681 | * |
||
682 | * @param mixed $objData The raw dataset. |
||
683 | * @param callable|null $before Process each entity before applying raw data. |
||
684 | * @param callable|null $after Process each entity after applying raw data. |
||
685 | * @return ModelInterface|ArrayAccess|null |
||
686 | */ |
||
687 | protected function processModel($objData, callable $before = null, callable $after = null) |
||
708 | } |
||
709 | |||
710 | /** |
||
711 | * Create a collection class or array. |
||
712 | * |
||
713 | * @throws RuntimeException If the collection class is invalid. |
||
714 | * @return array|ArrayAccess |
||
715 | */ |
||
716 | public function createCollection() |
||
717 | { |
||
718 | $collectClass = $this->collectionClass(); |
||
719 | if ($collectClass === 'array') { |
||
720 | return []; |
||
721 | } |
||
722 | |||
723 | if (!class_exists($collectClass)) { |
||
724 | throw new RuntimeException(sprintf( |
||
725 | 'Collection class [%s] does not exist.', |
||
726 | $collectClass |
||
727 | )); |
||
728 | } |
||
729 | |||
730 | if (!is_subclass_of($collectClass, ArrayAccess::class)) { |
||
731 | throw new RuntimeException(sprintf( |
||
732 | 'Collection class [%s] must implement ArrayAccess.', |
||
733 | $collectClass |
||
734 | )); |
||
735 | } |
||
736 | |||
737 | $collection = new $collectClass; |
||
738 | |||
739 | return $collection; |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * Set the class name of the collection. |
||
744 | * |
||
745 | * @param string $className The class name of the collection. |
||
746 | * @throws InvalidArgumentException If the class name is not a string. |
||
747 | * @return self |
||
748 | */ |
||
749 | public function setCollectionClass($className) |
||
750 | { |
||
751 | if (!is_string($className)) { |
||
752 | throw new InvalidArgumentException( |
||
753 | 'Collection class name must be a string.' |
||
754 | ); |
||
755 | } |
||
756 | |||
757 | $this->collectionClass = $className; |
||
758 | |||
759 | return $this; |
||
760 | } |
||
761 | |||
762 | /** |
||
763 | * Retrieve the class name of the collection. |
||
764 | * |
||
765 | * @return string |
||
766 | */ |
||
767 | public function collectionClass() |
||
768 | { |
||
769 | return $this->collectionClass; |
||
770 | } |
||
771 | |||
772 | /** |
||
773 | * Allow an object to define how the key getter are called. |
||
774 | * |
||
775 | * @param string $key The key to get the getter from. |
||
776 | * @return string The getter method name, for a given key. |
||
777 | */ |
||
778 | protected function getter($key) |
||
779 | { |
||
780 | $getter = $key; |
||
781 | return $this->camelize($getter); |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * Allow an object to define how the key setter are called. |
||
786 | * |
||
787 | * @param string $key The key to get the setter from. |
||
788 | * @return string The setter method name, for a given key. |
||
789 | */ |
||
790 | protected function setter($key) |
||
791 | { |
||
792 | $setter = 'set_'.$key; |
||
793 | return $this->camelize($setter); |
||
794 | } |
||
795 | |||
796 | /** |
||
797 | * Transform a snake_case string to camelCase. |
||
798 | * |
||
799 | * @param string $str The snake_case string to camelize. |
||
800 | * @return string The camelcase'd string. |
||
801 | */ |
||
802 | protected function camelize($str) |
||
805 | } |
||
806 | } |
||
807 |
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.