Complex classes like GenerateAdminCommand 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 GenerateAdminCommand, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
35 | class GenerateAdminCommand extends QuestionableCommand |
||
36 | { |
||
37 | /** |
||
38 | * @var string[] |
||
39 | */ |
||
40 | private $managerTypes; |
||
41 | |||
42 | public function configure() |
||
43 | { |
||
44 | $this |
||
45 | ->setName('sonata:admin:generate') |
||
46 | ->setDescription('Generates an admin class based on the given model class') |
||
47 | ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class') |
||
48 | ->addOption('bundle', 'b', InputOption::VALUE_OPTIONAL, 'The bundle name') |
||
49 | ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename') |
||
50 | ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename') |
||
51 | ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type') |
||
52 | ->addOption('services', 'y', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yml') |
||
53 | ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID') |
||
54 | ; |
||
55 | } |
||
56 | |||
57 | public function isEnabled() |
||
58 | { |
||
59 | return class_exists(SensioGeneratorBundle::class); |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * @param string $managerType |
||
64 | * |
||
65 | * @throws \InvalidArgumentException |
||
66 | * |
||
67 | * @return string |
||
68 | */ |
||
69 | public function validateManagerType($managerType) |
||
70 | { |
||
71 | $managerTypes = $this->getAvailableManagerTypes(); |
||
72 | |||
73 | if (!isset($managerTypes[$managerType])) { |
||
74 | throw new \InvalidArgumentException(sprintf( |
||
75 | 'Invalid manager type "%s". Available manager types are "%s".', |
||
76 | $managerType, |
||
77 | implode('", "', $managerTypes) |
||
78 | )); |
||
79 | } |
||
80 | |||
81 | return $managerType; |
||
82 | } |
||
83 | |||
84 | protected function execute(InputInterface $input, OutputInterface $output) |
||
85 | { |
||
86 | $modelClass = Validators::validateClass($input->getArgument('model')); |
||
|
|||
87 | $modelClassBasename = current(\array_slice(explode('\\', $modelClass), -1)); |
||
88 | $bundle = $this->getBundle($input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass)); |
||
89 | $adminClassBasename = $input->getOption('admin') ?: $modelClassBasename.'Admin'; |
||
90 | $adminClassBasename = Validators::validateAdminClassBasename($adminClassBasename); |
||
91 | $managerType = $input->getOption('manager') ?: $this->getDefaultManagerType(); |
||
92 | $modelManager = $this->getModelManager($managerType); |
||
93 | $skeletonDirectory = __DIR__.'/../Resources/skeleton'; |
||
94 | $adminGenerator = new AdminGenerator($modelManager, $skeletonDirectory); |
||
95 | |||
96 | try { |
||
97 | $adminGenerator->generate($bundle, $adminClassBasename, $modelClass); |
||
98 | $output->writeln(sprintf( |
||
99 | '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".', |
||
100 | PHP_EOL, |
||
101 | $adminGenerator->getClass(), |
||
102 | realpath($adminGenerator->getFile()) |
||
103 | )); |
||
104 | } catch (\Exception $e) { |
||
105 | $this->writeError($output, $e->getMessage()); |
||
106 | } |
||
107 | |||
108 | $controllerName = CRUDController::class; |
||
109 | |||
110 | if ($controllerClassBasename = $input->getOption('controller')) { |
||
111 | $controllerClassBasename = Validators::validateControllerClassBasename($controllerClassBasename); |
||
112 | $controllerGenerator = new ControllerGenerator($skeletonDirectory); |
||
113 | |||
114 | try { |
||
115 | $controllerGenerator->generate($bundle, $controllerClassBasename); |
||
116 | $controllerName = $controllerGenerator->getClass(); |
||
117 | $output->writeln(sprintf( |
||
118 | '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".', |
||
119 | PHP_EOL, |
||
120 | $controllerName, |
||
121 | realpath($controllerGenerator->getFile()) |
||
122 | )); |
||
123 | } catch (\Exception $e) { |
||
124 | $this->writeError($output, $e->getMessage()); |
||
125 | } |
||
126 | } |
||
127 | |||
128 | if ($servicesFile = $input->getOption('services')) { |
||
129 | $adminClass = $adminGenerator->getClass(); |
||
130 | $file = sprintf('%s/Resources/config/%s', $bundle->getPath(), $servicesFile); |
||
131 | $servicesManipulator = new ServicesManipulator($file); |
||
132 | |||
133 | try { |
||
134 | $id = $input->getOption('id') ?: $this->getAdminServiceId($bundle->getName(), $adminClassBasename); |
||
135 | $servicesManipulator->addResource($id, $modelClass, $adminClass, $controllerName, $managerType); |
||
136 | $output->writeln(sprintf( |
||
137 | '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".', |
||
138 | PHP_EOL, |
||
139 | $id, |
||
140 | realpath($file) |
||
141 | )); |
||
142 | } catch (\Exception $e) { |
||
143 | $this->writeError($output, $e->getMessage()); |
||
144 | } |
||
145 | } |
||
146 | |||
147 | return 0; |
||
148 | } |
||
149 | |||
150 | protected function interact(InputInterface $input, OutputInterface $output) |
||
225 | |||
226 | /** |
||
227 | * @throws \InvalidArgumentException |
||
228 | */ |
||
229 | private function getBundleNameFromClass(string $class): ?string |
||
242 | |||
243 | private function getBundle(string $name): BundleInterface |
||
247 | |||
248 | private function writeError(OutputInterface $output, string $message): void |
||
252 | |||
253 | /** |
||
254 | * @throws \RuntimeException |
||
255 | */ |
||
256 | private function getDefaultManagerType(): string |
||
264 | |||
265 | private function getModelManager(string $managerType): ModelManagerInterface |
||
272 | |||
273 | private function getAdminServiceId(string $bundleName, string $adminClassBasename): string |
||
285 | |||
286 | /** |
||
287 | * @return string[] |
||
288 | */ |
||
289 | private function getAvailableManagerTypes(): array |
||
310 | |||
311 | private function getKernel(): KernelInterface |
||
318 | } |
||
319 |
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.