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 PluginController 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 PluginController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
43 | class PluginController extends AbstractController |
||
44 | { |
||
45 | /** |
||
46 | * @var PluginService |
||
47 | */ |
||
48 | protected $pluginService; |
||
49 | |||
50 | /** |
||
51 | * @var BaseInfo |
||
52 | */ |
||
53 | protected $BaseInfo; |
||
54 | |||
55 | /** |
||
56 | * @var PluginRepository |
||
57 | */ |
||
58 | protected $pluginRepository; |
||
59 | |||
60 | /** |
||
61 | * @var PluginApiService |
||
62 | */ |
||
63 | protected $pluginApiService; |
||
64 | |||
65 | /** |
||
66 | * PluginController constructor. |
||
67 | * |
||
68 | * @param PluginRepository $pluginRepository |
||
69 | * @param PluginService $pluginService |
||
70 | * @param BaseInfoRepository $baseInfoRepository |
||
71 | * @param PluginApiService $pluginApiService |
||
72 | * |
||
73 | * @throws \Doctrine\ORM\NoResultException |
||
74 | * @throws \Doctrine\ORM\NonUniqueResultException |
||
75 | */ |
||
76 | View Code Duplication | public function __construct(PluginRepository $pluginRepository, PluginService $pluginService, BaseInfoRepository $baseInfoRepository, PluginApiService $pluginApiService) |
|
|
|||
77 | { |
||
78 | $this->pluginRepository = $pluginRepository; |
||
79 | $this->pluginService = $pluginService; |
||
80 | $this->BaseInfo = $baseInfoRepository->get(); |
||
81 | $this->pluginApiService = $pluginApiService; |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * インストール済プラグイン画面 |
||
86 | * |
||
87 | * @Route("/%eccube_admin_route%/store/plugin", name="admin_store_plugin") |
||
88 | * @Template("@admin/Store/plugin.twig") |
||
89 | * |
||
90 | * @return array |
||
91 | * @throws PluginException |
||
92 | */ |
||
93 | public function index() |
||
94 | { |
||
95 | $pluginForms = []; |
||
96 | $configPages = []; |
||
97 | $Plugins = $this->pluginRepository->findBy([], ['code' => 'ASC']); |
||
98 | |||
99 | // ファイル設置プラグインの取得. |
||
100 | $unregisteredPlugins = $this->getUnregisteredPlugins($Plugins); |
||
101 | $unregisteredPluginsConfigPages = []; |
||
102 | foreach ($unregisteredPlugins as $unregisteredPlugin) { |
||
103 | try { |
||
104 | $code = $unregisteredPlugin['code']; |
||
105 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) |
||
106 | $unregisteredPluginsConfigPages[$code] = $this->generateUrl('plugin_'.$code.'_config'); |
||
107 | } catch (RouteNotFoundException $e) { |
||
108 | // プラグインで設定画面のルートが定義されていない場合は無視 |
||
109 | } |
||
110 | } |
||
111 | |||
112 | $officialPlugins = []; |
||
113 | $unofficialPlugins = []; |
||
114 | |||
115 | foreach ($Plugins as $Plugin) { |
||
116 | $form = $this->formFactory |
||
117 | ->createNamedBuilder( |
||
118 | 'form'.$Plugin->getId(), |
||
119 | PluginManagementType::class, |
||
120 | null, |
||
121 | [ |
||
122 | 'plugin_id' => $Plugin->getId(), |
||
123 | ] |
||
124 | ) |
||
125 | ->getForm(); |
||
126 | $pluginForms[$Plugin->getId()] = $form->createView(); |
||
127 | |||
128 | try { |
||
129 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) |
||
130 | $configPages[$Plugin->getCode()] = $this->generateUrl(Container::underscore($Plugin->getCode()).'_admin_config'); |
||
131 | } catch (\Exception $e) { |
||
132 | // プラグインで設定画面のルートが定義されていない場合は無視 |
||
133 | } |
||
134 | if ($Plugin->getSource() == 0) { |
||
135 | // 商品IDが設定されていない場合、非公式プラグイン |
||
136 | $unofficialPlugins[] = $Plugin; |
||
137 | } else { |
||
138 | $officialPlugins[$Plugin->getSource()] = $Plugin; |
||
139 | } |
||
140 | } |
||
141 | |||
142 | // オーナーズストア通信 |
||
143 | $officialPluginsDetail = []; |
||
144 | try { |
||
145 | $data = $this->pluginApiService->getPurchased(); |
||
146 | foreach ($data as $item) { |
||
147 | if (isset($officialPlugins[$item['id']])) { |
||
148 | $Plugin = $officialPlugins[$item['id']]; |
||
149 | $officialPluginsDetail[$item['id']] = $item; |
||
150 | $officialPluginsDetail[$item['id']]['update_status'] = 0; |
||
151 | View Code Duplication | if ($this->pluginService->isUpdate($Plugin->getVersion(), $item['version'])) { |
|
152 | $officialPluginsDetail[$item['id']]['update_status'] = 1; |
||
153 | } |
||
154 | } else { |
||
155 | $Plugin = new Plugin(); |
||
156 | $Plugin->setName($item['name']); |
||
157 | $Plugin->setCode($item['code']); |
||
158 | $Plugin->setVersion($item['version']); |
||
159 | $Plugin->setSource($item['id']); |
||
160 | $Plugin->setEnabled(false); |
||
161 | $officialPlugins[$item['id']] = $Plugin; |
||
162 | $officialPluginsDetail[$item['id']] = $item; |
||
163 | $officialPluginsDetail[$item['id']]['update_status'] = 0; |
||
164 | View Code Duplication | if ($this->pluginService->isUpdate($Plugin->getVersion(), $item['version'])) { |
|
165 | $officialPluginsDetail[$item['id']]['update_status'] = 1; |
||
166 | } |
||
167 | } |
||
168 | } |
||
169 | } catch (PluginApiException $e) { |
||
170 | $this->addWarning($e->getMessage(), 'admin'); |
||
171 | } |
||
172 | |||
173 | return [ |
||
174 | 'plugin_forms' => $pluginForms, |
||
175 | 'officialPlugins' => $officialPlugins, |
||
176 | 'unofficialPlugins' => $unofficialPlugins, |
||
177 | 'configPages' => $configPages, |
||
178 | 'unregisteredPlugins' => $unregisteredPlugins, |
||
179 | 'unregisteredPluginsConfigPages' => $unregisteredPluginsConfigPages, |
||
180 | 'officialPluginsDetail' => $officialPluginsDetail, |
||
181 | ]; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * インストール済プラグインからのアップデート |
||
186 | * |
||
187 | * @Route("/%eccube_admin_route%/store/plugin/{id}/update", requirements={"id" = "\d+"}, name="admin_store_plugin_update", methods={"POST"}) |
||
188 | * |
||
189 | * @param Request $request |
||
190 | * @param Plugin $Plugin |
||
191 | * |
||
192 | * @return RedirectResponse |
||
193 | */ |
||
194 | public function update(Request $request, Plugin $Plugin) |
||
248 | |||
249 | /** |
||
250 | * 対象のプラグインを有効にします。 |
||
251 | * |
||
252 | * @Route("/%eccube_admin_route%/store/plugin/{id}/enable", requirements={"id" = "\d+"}, name="admin_store_plugin_enable", methods={"POST"}) |
||
253 | * |
||
254 | * @param Plugin $Plugin |
||
255 | * |
||
256 | * @return RedirectResponse|JsonResponse |
||
257 | * |
||
258 | * @throws PluginException |
||
259 | */ |
||
260 | public function enable(Plugin $Plugin, CacheUtil $cacheUtil, Request $request) |
||
261 | { |
||
262 | $this->isTokenValid(); |
||
263 | |||
264 | $log = null; |
||
265 | |||
266 | if ($Plugin->isEnabled()) { |
||
267 | if ($request->isXmlHttpRequest()) { |
||
268 | return $this->json(['success' => true]); |
||
269 | } else { |
||
270 | $this->addError('admin.plugin.already.enable', 'admin'); |
||
271 | } |
||
272 | } else { |
||
273 | // ストアからインストールしたプラグインは依存プラグインが有効化されているかを確認 |
||
274 | if ($Plugin->getSource()) { |
||
275 | $requires = $this->pluginService->getPluginRequired($Plugin); |
||
276 | View Code Duplication | $requires = array_filter($requires, function ($req) { |
|
277 | $code = preg_replace('/^ec-cube\//', '', $req['name']); |
||
278 | /** @var Plugin $DependPlugin */ |
||
279 | $DependPlugin = $this->pluginRepository->findOneBy(['code' => $code]); |
||
280 | |||
281 | return $DependPlugin->isEnabled() == false; |
||
282 | }); |
||
283 | if (!empty($requires)) { |
||
284 | $names = array_map(function ($req) { |
||
285 | return "「${req['description']}」"; |
||
286 | }, $requires); |
||
287 | $message = trans('%depend_name%を先に有効化してください。', ['%name%' => $Plugin->getName(), '%depend_name%' => implode(', ', $names)]); |
||
288 | |||
289 | if ($request->isXmlHttpRequest()) { |
||
290 | return $this->json(['success' => false, 'message' => $message], 400); |
||
291 | } else { |
||
292 | $this->addError($message, 'admin'); |
||
293 | |||
294 | return $this->redirectToRoute('admin_store_plugin'); |
||
295 | } |
||
296 | } |
||
297 | } |
||
298 | |||
299 | |||
300 | ob_start(); |
||
301 | |||
302 | if (!$Plugin->isInitialized()) { |
||
303 | $this->pluginService->installWithCode($Plugin->getCode()); |
||
304 | } |
||
305 | |||
306 | $this->pluginService->enable($Plugin); |
||
307 | $log = ob_get_clean(); |
||
308 | ob_end_flush(); |
||
309 | } |
||
310 | |||
311 | $cacheUtil->clearCache(); |
||
312 | |||
313 | View Code Duplication | if ($request->isXmlHttpRequest()) { |
|
314 | return $this->json(['success' => true, 'log' => $log]); |
||
315 | } else { |
||
316 | $this->addSuccess(trans('「%plugin_name%」を有効にしました。', ['%plugin_name%' => $Plugin->getName()]), 'admin'); |
||
317 | |||
318 | return $this->redirectToRoute('admin_store_plugin'); |
||
319 | } |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * 対象のプラグインを無効にします。 |
||
324 | * |
||
325 | * @Route("/%eccube_admin_route%/store/plugin/{id}/disable", requirements={"id" = "\d+"}, name="admin_store_plugin_disable", methods={"POST"}) |
||
326 | * |
||
327 | * @param Request $request |
||
328 | * @param Plugin $Plugin |
||
329 | * @param CacheUtil $cacheUtil |
||
330 | * |
||
331 | * @return \Symfony\Component\HttpFoundation\JsonResponse|RedirectResponse |
||
332 | */ |
||
333 | public function disable(Request $request, Plugin $Plugin, CacheUtil $cacheUtil) |
||
334 | { |
||
335 | $this->isTokenValid(); |
||
336 | |||
337 | $log = null; |
||
338 | if ($Plugin->isEnabled()) { |
||
339 | $dependents = $this->pluginService->findDependentPluginNeedDisable($Plugin->getCode()); |
||
340 | if (!empty($dependents)) { |
||
341 | $dependName = $dependents[0]; |
||
342 | $DependPlugin = $this->pluginRepository->findOneBy(['code' => $dependents[0]]); |
||
343 | if ($DependPlugin) { |
||
344 | $dependName = $DependPlugin->getName(); |
||
345 | } |
||
346 | $message = trans('admin.plugin.disable.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]); |
||
347 | |||
348 | if ($request->isXmlHttpRequest()) { |
||
349 | return $this->json(['message' => $message], 400); |
||
350 | } else { |
||
351 | $this->addError($message, 'admin'); |
||
352 | |||
353 | return $this->redirectToRoute('admin_store_plugin'); |
||
354 | } |
||
355 | } |
||
356 | |||
357 | ob_start(); |
||
358 | $this->pluginService->disable($Plugin); |
||
359 | $log = ob_get_clean(); |
||
360 | ob_end_flush(); |
||
361 | View Code Duplication | } else { |
|
362 | if ($request->isXmlHttpRequest()) { |
||
363 | return $this->json(['success' => true, 'log' => $log]); |
||
364 | } else { |
||
365 | $this->addError('admin.plugin.already.disable', 'admin'); |
||
366 | |||
367 | return $this->redirectToRoute('admin_store_plugin'); |
||
368 | } |
||
369 | } |
||
370 | |||
371 | $cacheUtil->clearCache(); |
||
372 | |||
373 | View Code Duplication | if ($request->isXmlHttpRequest()) { |
|
374 | return $this->json(['success' => true, 'log' => $log]); |
||
375 | } else { |
||
376 | $this->addSuccess('admin.plugin.disable.complete', 'admin'); |
||
377 | |||
378 | return $this->redirectToRoute('admin_store_plugin'); |
||
379 | } |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * 対象のプラグインを削除します。 |
||
384 | * |
||
385 | * @Route("/%eccube_admin_route%/store/plugin/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_uninstall", methods={"DELETE"}) |
||
386 | * |
||
387 | * @param Plugin $Plugin |
||
388 | * |
||
389 | * @return RedirectResponse |
||
390 | * |
||
391 | * @throws \Exception |
||
392 | */ |
||
393 | public function uninstall(Plugin $Plugin) |
||
423 | |||
424 | /** |
||
425 | * プラグインファイルアップロード画面 |
||
426 | * |
||
427 | * @Route("/%eccube_admin_route%/store/plugin/install", name="admin_store_plugin_install") |
||
428 | * @Template("@admin/Store/plugin_install.twig") |
||
429 | * |
||
430 | * @param Request $request |
||
431 | * |
||
432 | * @return array|RedirectResponse |
||
433 | */ |
||
434 | public function install(Request $request) |
||
486 | |||
487 | /** |
||
488 | * 認証キー設定画面 |
||
489 | * |
||
490 | * @Route("/%eccube_admin_route%/store/plugin/authentication_setting", name="admin_store_authentication_setting") |
||
491 | * @Template("@admin/Store/authentication_setting.twig") |
||
492 | */ |
||
493 | public function authenticationSetting(Request $request) |
||
516 | |||
517 | /** |
||
518 | * フォルダ設置のみのプラグインを取得する. |
||
519 | * |
||
520 | * @param array $plugins |
||
521 | * |
||
522 | * @return array |
||
523 | * @throws PluginException |
||
524 | */ |
||
525 | protected function getUnregisteredPlugins(array $plugins) |
||
561 | } |
||
562 |
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.