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 TrackerInline 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 TrackerInline, and based on these observations, apply Extract Interface, too.
1 | <?php defined('BASEPATH') or exit('No direct script access allowed'); |
||
3 | class TrackerInline extends Auth_Controller { |
||
4 | private $userID; |
||
5 | |||
6 | public function __construct() { |
||
7 | parent::__construct(FALSE); |
||
8 | |||
9 | $this->load->library('Limiter'); |
||
10 | $this->load->library('form_validation'); |
||
11 | |||
12 | //1000 requests per hour to either AJAX request. |
||
13 | if($this->limiter->limit('tracker_general', 1000)) { |
||
14 | $this->output->set_status_header('429', 'Rate limit reached'); //rate limited reached |
||
15 | |||
16 | exit_ci(); |
||
17 | } |
||
18 | |||
19 | $this->userID = (int) $this->User->id; |
||
20 | } |
||
21 | |||
22 | /** |
||
23 | * Used locally to update the users' latest read chapter of a series. |
||
24 | * |
||
25 | * REQ_PARAMS: id, chapter |
||
26 | * METHOD: POST |
||
27 | * URL: /ajax/update_inline |
||
28 | */ |
||
29 | View Code Duplication | public function update() : void { |
|
30 | $this->form_validation->set_rules('id', 'Chapter ID', 'required|ctype_digit'); |
||
31 | $this->form_validation->set_rules('chapter', 'Chapter', 'required'); |
||
32 | |||
33 | if($this->form_validation->run() === TRUE) { |
||
34 | $success = $this->Tracker->list->updateByID($this->userID, $this->input->post('id'), $this->input->post('chapter')); |
||
35 | if($success) { |
||
36 | $this->output->set_status_header('200'); //Success! |
||
37 | } else { |
||
38 | $this->output->set_status_header('400', 'Unable to update?'); |
||
39 | } |
||
40 | } else { |
||
41 | $this->output->set_status_header('400', 'Missing/invalid parameters.'); |
||
42 | } |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Used locally to remove (multiple) series from a users' list. |
||
47 | * |
||
48 | * REQ_PARAMS: id[] |
||
49 | * METHOD: POST |
||
50 | * URL: /ajax/delete_inline |
||
51 | */ |
||
52 | public function delete() : void { |
||
53 | $this->form_validation->set_rules('id[]', 'List of IDs', 'required|ctype_digit'); |
||
54 | |||
55 | if($this->form_validation->run() === TRUE) { |
||
56 | $status = $this->Tracker->list->deleteByIDList($this->input->post('id[]')); |
||
57 | switch($status['code']) { |
||
58 | case 0: |
||
59 | $this->output->set_status_header('200'); //Success! |
||
60 | break; |
||
61 | case 1: |
||
62 | $this->output->set_status_header('400', 'Request contains invalid IDs'); |
||
63 | break; |
||
64 | case 2: |
||
65 | $this->output->set_status_header('400', 'Request contains invalid elements.'); |
||
66 | break; |
||
67 | } |
||
68 | } else { |
||
69 | $this->output->set_status_header('400', 'Request contained invalid elements!'); |
||
70 | } |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Used to import a tracker exported list. |
||
75 | * |
||
76 | * REQ_PARAMS: json |
||
77 | * METHOD: POST |
||
78 | * URL: /import_list |
||
79 | */ |
||
80 | View Code Duplication | public function import() : void { |
|
81 | $this->form_validation->set_rules('json', 'JSON String', 'required|is_valid_json'); |
||
82 | |||
83 | if($this->form_validation->run() === TRUE) { |
||
84 | $status = $this->Tracker->portation->importFromJSON($this->input->post('json')); |
||
85 | switch($status['code']) { |
||
86 | case 0: |
||
87 | $this->output->set_status_header('200'); //Success |
||
88 | break; |
||
89 | case 1: |
||
90 | $this->output->set_status_header('400', 'JSON contains invalid keys'); |
||
91 | break; |
||
92 | case 2: |
||
93 | $this->output->set_status_header('400', 'Unable to add some rows from JSON'); |
||
94 | //$this->_render_json(json_encode($status['failed_rows'])); //TODO: We should list what rows these are. |
||
95 | break; |
||
96 | } |
||
97 | } else { |
||
98 | if(!$this->form_validation->isRuleValid('is_valid_json')) { |
||
99 | $this->output->set_status_header('400', 'File isn\'t valid JSON!'); |
||
100 | } else { |
||
101 | $this->output->set_status_header('400', 'No file sent'); |
||
102 | } |
||
103 | } |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Used to export a users' list. |
||
108 | * |
||
109 | * REQ_PARAMS: N/A |
||
110 | * METHOD: GET/POST |
||
111 | * URL: /export_list |
||
112 | */ |
||
113 | public function export() : void { |
||
117 | |||
118 | /** |
||
119 | * Used to set title tags |
||
120 | * |
||
121 | * REQ_PARAMS: id, tag_string |
||
122 | * METHOD: POST |
||
123 | * URL: /tag_update |
||
124 | */ |
||
125 | public function tag_update() : void { |
||
151 | |||
152 | /** |
||
153 | * Used to mass set title tags |
||
154 | * |
||
155 | * REQ_PARAMS: id[], tag_string |
||
156 | * METHOD: POST |
||
157 | * URL: /tag_update |
||
158 | */ |
||
159 | public function mass_tag_update() : void { |
||
192 | |||
193 | /** |
||
194 | * @param string $tag_string |
||
195 | * |
||
196 | * @return string |
||
197 | */ |
||
198 | private function _clean_tag_string(string $tag_string) : string { |
||
205 | |||
206 | /** |
||
207 | * Used to set chapter user category |
||
208 | * |
||
209 | * REQ_PARAMS: id[], category |
||
210 | * METHOD: POST |
||
211 | * URL: /set_category |
||
212 | */ |
||
213 | View Code Duplication | public function set_category() : void { |
|
234 | |||
235 | /** |
||
236 | * Used to permanently hide the current notice. |
||
237 | * |
||
238 | * REQ_PARAMS: [none] |
||
239 | * METHOD: POST |
||
240 | * URL: /ajax/hide_notice |
||
241 | */ |
||
242 | public function hide_notice() : void { |
||
249 | |||
250 | |||
251 | /** |
||
252 | * Used locally to ignore the latest chapter of a series so it doesn't clog up the current unread list. |
||
253 | * |
||
254 | * REQ_PARAMS: id, chapter |
||
255 | * METHOD: POST |
||
256 | * URL: /ajax/ignore_inline |
||
257 | */ |
||
258 | View Code Duplication | public function ignore() : void { |
|
273 | |||
274 | /** |
||
275 | * Used to set MAL ID |
||
276 | * |
||
277 | * REQ_PARAMS: id, mal_id |
||
278 | * METHOD: POST |
||
279 | * URL: /set_mal_id |
||
280 | */ |
||
281 | public function set_mal_id() : void { |
||
302 | } |
||
303 |