5 * Contains \Drupal\Console\Command\Generate\ModuleCommand.
8 namespace Drupal\Console\Command\Generate;
10 use Symfony\Component\Config\Definition\Exception\Exception;
11 use Symfony\Component\Console\Input\InputInterface;
12 use Symfony\Component\Console\Input\InputOption;
13 use Symfony\Component\Console\Output\OutputInterface;
14 use Drupal\Console\Generator\ModuleGenerator;
15 use Drupal\Console\Command\Shared\ConfirmationTrait;
16 use Symfony\Component\Console\Command\Command;
17 use Drupal\Console\Core\Style\DrupalStyle;
18 use Drupal\Console\Utils\Validator;
19 use Drupal\Console\Core\Command\Shared\CommandTrait;
20 use Drupal\Console\Core\Utils\StringConverter;
21 use Drupal\Console\Utils\DrupalApi;
22 use GuzzleHttp\Client;
23 use Drupal\Console\Utils\Site;
24 use GuzzleHttp\Exception\ClientException;
26 class ModuleCommand extends Command
28 use ConfirmationTrait;
32 * @var ModuleGenerator
47 * @var StringConverter
49 protected $stringConverter;
59 protected $httpClient;
69 protected $twigtemplate;
73 * ModuleCommand constructor.
75 * @param ModuleGenerator $generator
76 * @param Validator $validator
78 * @param StringConverter $stringConverter
79 * @param DrupalApi $drupalApi
80 * @param Client $httpClient
82 * @param $twigtemplate
84 public function __construct(
85 ModuleGenerator $generator,
88 StringConverter $stringConverter,
94 $this->generator = $generator;
95 $this->validator = $validator;
96 $this->appRoot = $appRoot;
97 $this->stringConverter = $stringConverter;
98 $this->drupalApi = $drupalApi;
99 $this->httpClient = $httpClient;
101 $this->twigtemplate = $twigtemplate;
102 parent::__construct();
108 protected function configure()
111 ->setName('generate:module')
112 ->setDescription($this->trans('commands.generate.module.description'))
113 ->setHelp($this->trans('commands.generate.module.help'))
117 InputOption::VALUE_REQUIRED,
118 $this->trans('commands.generate.module.options.module')
123 InputOption::VALUE_REQUIRED,
124 $this->trans('commands.generate.module.options.machine-name')
129 InputOption::VALUE_REQUIRED,
130 $this->trans('commands.generate.module.options.module-path')
135 InputOption::VALUE_OPTIONAL,
136 $this->trans('commands.generate.module.options.description')
141 InputOption::VALUE_OPTIONAL,
142 $this->trans('commands.generate.module.options.core')
147 InputOption::VALUE_OPTIONAL,
148 $this->trans('commands.generate.module.options.package')
153 InputOption::VALUE_NONE,
154 $this->trans('commands.generate.module.options.module-file')
159 InputOption::VALUE_REQUIRED,
160 $this->trans('commands.generate.module.options.features-bundle')
165 InputOption::VALUE_NONE,
166 $this->trans('commands.generate.module.options.composer')
171 InputOption::VALUE_OPTIONAL,
172 $this->trans('commands.generate.module.options.dependencies')
177 InputOption::VALUE_OPTIONAL,
178 $this->trans('commands.generate.module.options.test')
183 InputOption::VALUE_OPTIONAL,
184 $this->trans('commands.generate.module.options.twigtemplate')
191 protected function execute(InputInterface $input, OutputInterface $output)
193 $io = new DrupalStyle($input, $output);
194 $yes = $input->hasOption('yes')?$input->getOption('yes'):false;
196 // @see use Drupal\Console\Command\Shared\ConfirmationTrait::confirmGeneration
197 if (!$this->confirmGeneration($io, $yes)) {
201 $module = $this->validator->validateModuleName($input->getOption('module'));
203 $modulePath = $this->appRoot . $input->getOption('module-path');
204 $modulePath = $this->validator->validateModulePath($modulePath, true);
206 $machineName = $this->validator->validateMachineName($input->getOption('machine-name'));
207 $description = $input->getOption('description');
208 $core = $input->getOption('core');
209 $package = $input->getOption('package');
210 $moduleFile = $input->getOption('module-file');
211 $featuresBundle = $input->getOption('features-bundle');
212 $composer = $input->getOption('composer');
213 $test = $input->getOption('test');
214 $twigtemplate = $input->getOption('twigtemplate');
216 // Modules Dependencies, re-factor and share with other commands
217 $dependencies = $this->validator->validateModuleDependencies($input->getOption('dependencies'));
218 // Check if all module dependencies are available
220 $checked_dependencies = $this->checkDependencies($dependencies['success'], $io);
221 if (!empty($checked_dependencies['no_modules'])) {
224 $this->trans('commands.generate.module.warnings.module-unavailable'),
225 implode(', ', $checked_dependencies['no_modules'])
229 $dependencies = $dependencies['success'];
232 $this->generator->generate(
249 * @param array $dependencies
252 private function checkDependencies(array $dependencies, DrupalStyle $io)
254 $this->site->loadLegacyFile('/core/modules/system/system.module');
257 $modules = system_rebuild_module_data();
258 foreach ($modules as $module_id => $module) {
259 array_push($localModules, basename($module->subpath));
262 $checkDependencies = [
263 'local_modules' => [],
264 'drupal_modules' => [],
268 foreach ($dependencies as $module) {
269 if (in_array($module, $localModules)) {
270 $checkDependencies['local_modules'][] = $module;
273 $response = $this->httpClient->head('https://www.drupal.org/project/' . $module);
274 $header_link = explode(';', $response->getHeader('link'));
275 if (empty($header_link[0])) {
276 $checkDependencies['no_modules'][] = $module;
278 $checkDependencies['drupal_modules'][] = $module;
280 } catch (ClientException $e) {
281 $checkDependencies['no_modules'][] = $module;
286 return $checkDependencies;
292 protected function interact(InputInterface $input, OutputInterface $output)
294 $io = new DrupalStyle($input, $output);
296 $validator = $this->validator;
299 $module = $input->getOption('module') ?
300 $this->validator->validateModuleName(
301 $input->getOption('module')
303 } catch (\Exception $error) {
304 $io->error($error->getMessage());
311 $this->trans('commands.generate.module.questions.module'),
313 function ($module) use ($validator) {
314 return $validator->validateModuleName($module);
317 $input->setOption('module', $module);
321 $machineName = $input->getOption('machine-name') ?
322 $this->validator->validateModuleName(
323 $input->getOption('machine-name')
325 } catch (\Exception $error) {
326 $io->error($error->getMessage());
330 $machineName = $io->ask(
331 $this->trans('commands.generate.module.questions.machine-name'),
332 $this->stringConverter->createMachineName($module),
333 function ($machine_name) use ($validator) {
334 return $validator->validateMachineName($machine_name);
337 $input->setOption('machine-name', $machineName);
340 $modulePath = $input->getOption('module-path');
342 $drupalRoot = $this->appRoot;
343 $modulePath = $io->ask(
344 $this->trans('commands.generate.module.questions.module-path'),
346 function ($modulePath) use ($drupalRoot, $machineName) {
347 $modulePath = ($modulePath[0] != '/' ? '/' : '').$modulePath;
348 $fullPath = $drupalRoot.$modulePath.'/'.$machineName;
349 if (file_exists($fullPath)) {
350 throw new \InvalidArgumentException(
352 $this->trans('commands.generate.module.errors.directory-exists'),
362 $input->setOption('module-path', $modulePath);
364 $description = $input->getOption('description');
366 $description = $io->ask(
367 $this->trans('commands.generate.module.questions.description'),
371 $input->setOption('description', $description);
373 $package = $input->getOption('package');
376 $this->trans('commands.generate.module.questions.package'),
380 $input->setOption('package', $package);
382 $core = $input->getOption('core');
385 $this->trans('commands.generate.module.questions.core'), '8.x',
387 // Only allow 8.x and higher as core version.
388 if (!preg_match('/^([0-9]+)\.x$/', $core, $matches) || ($matches[1] < 8)) {
389 throw new \InvalidArgumentException(
391 $this->trans('commands.generate.module.errors.invalid-core'),
400 $input->setOption('core', $core);
403 $moduleFile = $input->getOption('module-file');
405 $moduleFile = $io->confirm(
406 $this->trans('commands.generate.module.questions.module-file'),
409 $input->setOption('module-file', $moduleFile);
412 $featuresBundle = $input->getOption('features-bundle');
413 if (!$featuresBundle) {
414 $featuresSupport = $io->confirm(
415 $this->trans('commands.generate.module.questions.features-support'),
418 if ($featuresSupport) {
419 $featuresBundle = $io->ask(
420 $this->trans('commands.generate.module.questions.features-bundle'),
424 $input->setOption('features-bundle', $featuresBundle);
427 $composer = $input->getOption('composer');
429 $composer = $io->confirm(
430 $this->trans('commands.generate.module.questions.composer'),
433 $input->setOption('composer', $composer);
436 $dependencies = $input->getOption('dependencies');
437 if (!$dependencies) {
438 $addDependencies = $io->confirm(
439 $this->trans('commands.generate.module.questions.dependencies'),
442 if ($addDependencies) {
443 $dependencies = $io->ask(
444 $this->trans('commands.generate.module.options.dependencies')
447 $input->setOption('dependencies', $dependencies);
450 $test = $input->getOption('test');
452 $test = $io->confirm(
453 $this->trans('commands.generate.module.questions.test'),
456 $input->setOption('test', $test);
459 $twigtemplate = $input->getOption('twigtemplate');
460 if (!$twigtemplate) {
461 $twigtemplate = $io->confirm(
462 $this->trans('commands.generate.module.questions.twigtemplate'),
465 $input->setOption('twigtemplate', $twigtemplate);
470 * @return ModuleGenerator
472 protected function createGenerator()
474 return new ModuleGenerator();