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:
1 | <?php |
||
28 | class IndexPage extends Widget |
||
29 | { |
||
30 | |||
31 | /** |
||
32 | * @var string |
||
33 | */ |
||
34 | protected $_layout; |
||
35 | |||
36 | /** |
||
37 | * @var Model the search model |
||
38 | */ |
||
39 | public $model; |
||
40 | |||
41 | /** |
||
42 | * @var object original view context. |
||
43 | * It is used to render sub-views with the same context, as IndexPage |
||
44 | */ |
||
45 | public $originalContext; |
||
46 | |||
47 | /** |
||
48 | * @var DataProviderInterface |
||
49 | */ |
||
50 | public $dataProvider; |
||
51 | |||
52 | /** |
||
53 | * @var array Hash of document blocks, that can be rendered later in the widget's views |
||
54 | * Blocks can be set explicitly on widget initialisation, or by calling [[beginContent]] and |
||
55 | * [[endContent]] |
||
56 | * |
||
57 | * @see beginContent |
||
58 | * @see endContent |
||
59 | */ |
||
60 | public $contents = []; |
||
61 | |||
62 | /** |
||
63 | * @var string the name of current content block, that is under the render |
||
64 | * @see beginContent |
||
65 | * @see endContent |
||
66 | */ |
||
67 | protected $_current = null; |
||
68 | |||
69 | /** |
||
70 | * @var array |
||
71 | */ |
||
72 | public $searchFormData = []; |
||
73 | |||
74 | /** |
||
75 | * @var array |
||
76 | */ |
||
77 | public $searchFormOptions = []; |
||
78 | |||
79 | /** |
||
80 | * @var string the name of view file that contains search fields for the index page. Defaults to `_search` |
||
81 | * @see renderSearchForm() |
||
82 | */ |
||
83 | public $searchView = '_search'; |
||
84 | |||
85 | /** {@inheritdoc} */ |
||
86 | public function init() |
||
87 | { |
||
88 | parent::init(); |
||
89 | $searchFormId = Json::htmlEncode("#{$this->getBulkFormId()}"); |
||
90 | $this->originalContext = Yii::$app->view->context; |
||
91 | $view = $this->getView(); |
||
92 | $view->registerJs(<<<JS |
||
93 | // Checkbox |
||
94 | var checkboxes = $('table input[type="checkbox"]'); |
||
95 | var bulkcontainer = $('.box-bulk-actions fieldset'); |
||
96 | checkboxes.on('ifChecked ifUnchecked', function(event) { |
||
97 | if (event.type == 'ifChecked' && $('input.icheck').filter(':checked').length > 0) { |
||
98 | bulkcontainer.prop('disabled', false); |
||
99 | } else if ($('input.icheck').filter(':checked').length == 0) { |
||
100 | bulkcontainer.prop('disabled', true); |
||
101 | } |
||
102 | }); |
||
103 | // On/Off Actions TODO: reduce scope |
||
104 | $(document).on('click', '.box-bulk-actions a', function (event) { |
||
105 | var link = $(this); |
||
106 | var action = link.data('action'); |
||
107 | var form = $($searchFormId); |
||
108 | if (action) { |
||
109 | form.attr({'action': action, method: 'POST'}).submit(); |
||
110 | } |
||
111 | }); |
||
112 | JS |
||
113 | ); |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * Begins output buffer capture to save data in [[contents]] with the $name key. |
||
118 | * Must not be called nested. See [[endContent]] for capture terminating. |
||
119 | * @param string $name |
||
120 | */ |
||
121 | public function beginContent($name) |
||
122 | { |
||
123 | if ($this->_current) { |
||
124 | throw new InvalidParamException('Output buffer capture is already running for ' . $this->_current); |
||
125 | } |
||
126 | $this->_current = $name; |
||
127 | ob_start(); |
||
128 | ob_implicit_flush(false); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Terminates output buffer capture started by [[beginContent()]]. |
||
133 | * @see beginContent |
||
134 | */ |
||
135 | public function endContent() |
||
136 | { |
||
137 | if (!$this->_current) { |
||
138 | throw new InvalidParamException('Outout buffer capture is not running. Call beginContent() first'); |
||
139 | } |
||
140 | $this->contents[$this->_current] = ob_get_contents(); |
||
141 | ob_end_clean(); |
||
142 | $this->_current = null; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Returns content saved in [[content]] by $name. |
||
147 | * @param string $name |
||
148 | * @return string |
||
149 | */ |
||
150 | public function renderContent($name) |
||
151 | { |
||
152 | return $this->contents[$name]; |
||
153 | } |
||
154 | |||
155 | public function run() |
||
156 | { |
||
157 | return $this->render($this->getLayout()); |
||
158 | } |
||
159 | |||
160 | public function detectLayout() |
||
161 | { |
||
162 | $os = Yii::$app->get('orientationStorage'); |
||
163 | $n = $os->get(Yii::$app->controller->getRoute()); |
||
164 | return $n; |
||
165 | } |
||
166 | |||
167 | public function setSearchFormData($data = []) |
||
168 | { |
||
169 | $this->searchFormData = $data; |
||
170 | } |
||
171 | |||
172 | public function setSearchFormOptions($options = []) |
||
173 | { |
||
174 | $this->searchFormOptions = $options; |
||
175 | } |
||
176 | |||
177 | public function renderSearchForm($advancedSearchOptions = []) |
||
178 | { |
||
179 | $advancedSearchOptions = array_merge($advancedSearchOptions, $this->searchFormOptions); |
||
180 | ob_start(); |
||
181 | ob_implicit_flush(false); |
||
182 | try { |
||
183 | $search = $this->beginSearchForm($advancedSearchOptions); |
||
184 | foreach (['per_page', 'representation'] as $key) { |
||
185 | echo Html::hiddenInput($key, Yii::$app->request->get($key)); |
||
186 | } |
||
187 | echo Yii::$app->view->render($this->searchView, array_merge(compact('search'), $this->searchFormData), $this->originalContext); |
||
188 | $search->end(); |
||
189 | } catch (\Exception $e) { |
||
190 | ob_end_clean(); |
||
191 | throw $e; |
||
192 | } |
||
193 | |||
194 | return ob_get_clean(); |
||
195 | } |
||
196 | |||
197 | public function beginSearchForm($options = []) |
||
198 | { |
||
199 | return AdvancedSearch::begin(array_merge(['model' => $this->model], $options)); |
||
200 | } |
||
201 | |||
202 | public function renderSearchButton() |
||
203 | { |
||
204 | return AdvancedSearch::renderButton() . "\n"; |
||
205 | } |
||
206 | |||
207 | View Code Duplication | public function renderPerPage() |
|
|
|||
208 | { |
||
209 | return ButtonDropdown::widget([ |
||
210 | 'label' => Yii::t('app', 'Per page') . ': ' . (Yii::$app->request->get('per_page') ?: 25), |
||
211 | 'options' => ['class' => 'btn-default btn-sm'], |
||
212 | 'dropdown' => [ |
||
213 | 'items' => [ |
||
214 | ['label' => '25', 'url' => Url::current(['per_page' => null])], |
||
215 | ['label' => '50', 'url' => Url::current(['per_page' => 50])], |
||
216 | ['label' => '100', 'url' => Url::current(['per_page' => 100])], |
||
217 | ['label' => '200', 'url' => Url::current(['per_page' => 200])], |
||
218 | ['label' => '500', 'url' => Url::current(['per_page' => 500])], |
||
219 | ], |
||
220 | ], |
||
221 | ]); |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Renders button to choose representation. |
||
226 | * Returns empty string when nothing to choose (less then 2 representations available). |
||
227 | * @param array $grid class |
||
228 | * @param mixed $current selected representation |
||
229 | * @return string rendered HTML |
||
230 | */ |
||
231 | public static function renderRepresentations($grid, $current) |
||
232 | { |
||
233 | $representations = $grid::getRepresentations(); |
||
234 | if (count($representations)<2) { |
||
235 | return ''; |
||
236 | } |
||
237 | if (!isset($representations[$current])) { |
||
238 | $current = key($representations); |
||
239 | } |
||
240 | $items = []; |
||
241 | foreach ($representations as $key => $data) { |
||
242 | $items[] = [ |
||
243 | 'label' => $data['label'], |
||
244 | 'url' => Url::current(['representation' => $key]), |
||
245 | ]; |
||
246 | } |
||
247 | |||
248 | return ButtonDropdown::widget([ |
||
249 | 'label' => Yii::t('synt', 'View') . ': ' . $representations[$current]['label'], |
||
250 | 'options' => ['class' => 'btn-default btn-sm'], |
||
251 | 'dropdown' => [ |
||
252 | 'items' => $items, |
||
253 | ], |
||
254 | ]); |
||
255 | } |
||
256 | |||
257 | View Code Duplication | public function renderSorter(array $options) |
|
258 | { |
||
259 | return LinkSorter::widget(array_merge([ |
||
260 | 'show' => true, |
||
261 | 'sort' => $this->dataProvider->getSort(), |
||
262 | 'buttonClass' => 'btn btn-default dropdown-toggle btn-sm', |
||
263 | ], $options)); |
||
264 | } |
||
265 | |||
266 | public function getViewPath() |
||
267 | { |
||
268 | return parent::getViewPath() . DIRECTORY_SEPARATOR . (new \ReflectionClass($this))->getShortName(); |
||
269 | } |
||
270 | |||
271 | public function getBulkFormId() |
||
272 | { |
||
273 | return 'bulk-' . Inflector::camel2id($this->model->formName()); |
||
274 | } |
||
275 | |||
276 | public function beginBulkForm($action = '') |
||
277 | { |
||
278 | echo Html::beginForm($action, 'POST', ['id' => $this->getBulkFormId()]); |
||
279 | } |
||
280 | |||
281 | public function endBulkForm() |
||
282 | { |
||
283 | echo Html::endForm(); |
||
284 | } |
||
285 | |||
286 | View Code Duplication | public function renderBulkButton($text, $action, $color = 'default') |
|
287 | { |
||
288 | return Html::submitButton($text, [ |
||
289 | 'class' => "btn btn-$color btn-sm", |
||
290 | 'form' => $this->getBulkFormId(), |
||
291 | 'formmethod' => 'POST', |
||
292 | 'formaction' => $action, |
||
293 | ]); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @return string |
||
298 | */ |
||
299 | public function getLayout() |
||
300 | { |
||
301 | if ($this->_layout === null) { |
||
302 | $this->_layout = $this->detectLayout(); |
||
303 | } |
||
304 | return $this->_layout; |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * @param string $layout |
||
309 | */ |
||
310 | public function setLayout($layout) |
||
314 | } |
||
315 |
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.