Licitator 1.0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
10 KiB

5 years ago
  1. <?php
  2. declare(strict_types=1);
  3. namespace Prometeo\CommandsBundle\Commands;
  4. use Sonata\AdminBundle\Command\Validators;
  5. use Sonata\AdminBundle\Manipulator\ServicesManipulator;
  6. use Sonata\AdminBundle\Model\ModelManagerInterface;
  7. use Symfony\Bundle\MakerBundle\ConsoleStyle;
  8. use Symfony\Bundle\MakerBundle\DependencyBuilder;
  9. use Symfony\Bundle\MakerBundle\Generator;
  10. use Symfony\Bundle\MakerBundle\InputConfiguration;
  11. use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
  12. use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
  13. use Symfony\Component\Console\Command\Command;
  14. use Symfony\Component\Console\Input\InputArgument;
  15. use Symfony\Component\Console\Input\InputInterface;
  16. use Symfony\Component\Console\Input\InputOption;
  17. use Symfony\Component\DependencyInjection\Container;
  18. class MakeAdmin extends AbstractMaker
  19. {
  20. /**
  21. * @var string
  22. */
  23. private $projectDirectory;
  24. /**
  25. * @var string[]
  26. */
  27. private $availableModelManagers;
  28. /**
  29. * @var string
  30. */
  31. private $skeletonDirectory;
  32. /**
  33. * @var string
  34. */
  35. private $modelClass;
  36. /**
  37. * @var string
  38. */
  39. private $modelClassBasename;
  40. /**
  41. * @var string
  42. */
  43. private $adminClassBasename;
  44. /**
  45. * @var string
  46. */
  47. private $controllerClassBasename;
  48. /**
  49. * @var string
  50. */
  51. private $managerType;
  52. /**
  53. * @var ModelManagerInterface
  54. */
  55. private $modelManager;
  56. public function __construct($projectDirectory, array $modelManagers = [])
  57. {
  58. $this->projectDirectory = $projectDirectory;
  59. $this->availableModelManagers = $modelManagers;
  60. $this->skeletonDirectory = __DIR__ . '/../Resources/skeleton';
  61. }
  62. public static function getCommandName(): string
  63. {
  64. return 'prometeo:make:admin';
  65. }
  66. public function configureCommand(Command $command, InputConfiguration $inputConfig): void
  67. {
  68. $command
  69. ->setDescription('Generates an admin class based on the given model class')
  70. ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class')
  71. ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename')
  72. ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename')
  73. ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type')
  74. ->addOption('services', 's', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yaml')
  75. ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID');
  76. $inputConfig->setArgumentAsNonInteractive('model');
  77. }
  78. public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
  79. {
  80. if ($input->getArgument('model')) {
  81. return;
  82. }
  83. $io->section('Welcome to the Sonata Admin');
  84. $this->modelClass = $io->ask(
  85. 'The fully qualified model class',
  86. $input->getArgument('model'),
  87. [Validators::class, 'validateClass']
  88. );
  89. $this->modelClassBasename = current(\array_slice(explode('\\', $this->modelClass), -1));
  90. $this->adminClassBasename = $io->ask(
  91. 'The admin class basename',
  92. $input->getOption('admin') ?: $this->modelClassBasename . 'Admin',
  93. [Validators::class, 'validateAdminClassBasename']
  94. );
  95. if (\count($this->availableModelManagers) > 1) {
  96. $managerTypes = array_keys($this->availableModelManagers);
  97. $this->managerType = $io->choice('The manager type', $managerTypes, $managerTypes[0]);
  98. $input->setOption('manager', $this->managerType);
  99. }
  100. if ($io->confirm('Do you want to generate a controller?', false)) {
  101. $this->controllerClassBasename = $io->ask(
  102. 'The controller class basename',
  103. $input->getOption('controller') ?: $this->modelClassBasename . 'AdminController',
  104. [Validators::class, 'validateControllerClassBasename']
  105. );
  106. $input->setOption('controller', $this->controllerClassBasename);
  107. }
  108. $input->setOption('services', false);
  109. if ($io->confirm('Do you want to update the services YAML configuration file?', true)) {
  110. $path = $this->projectDirectory . '/config/';
  111. $servicesFile = $io->ask(
  112. 'The services YAML configuration file',
  113. is_file($path . 'admin.yaml') ? 'admin.yaml' : 'services.yaml',
  114. [Validators::class, 'validateServicesFile']
  115. );
  116. $id = $io->ask(
  117. 'The admin service ID',
  118. $this->getAdminServiceId($this->adminClassBasename),
  119. [Validators::class, 'validateServiceId']
  120. );
  121. $input->setOption('services', $servicesFile);
  122. $input->setOption('id', $id);
  123. }
  124. $input->setArgument('model', $this->modelClass);
  125. $input->setOption('admin', $this->adminClassBasename);
  126. }
  127. /**
  128. * Configure any library dependencies that your maker requires.
  129. */
  130. public function configureDependencies(DependencyBuilder $dependencies): void
  131. {
  132. }
  133. /**
  134. * Called after normal code generation: allows you to do anything.
  135. */
  136. public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
  137. {
  138. $this->configure($input);
  139. $adminClassNameDetails = $generator->createClassNameDetails(
  140. $this->adminClassBasename,
  141. 'Admin\\',
  142. 'Admin'
  143. );
  144. $adminClassFullName = $adminClassNameDetails->getFullName();
  145. $this->generateAdmin($io, $generator, $adminClassNameDetails);
  146. $controllerClassFullName = '';
  147. if ($this->controllerClassBasename) {
  148. $controllerClassNameDetails = $generator->createClassNameDetails(
  149. $this->controllerClassBasename,
  150. 'Controller\\',
  151. 'Controller'
  152. );
  153. $this->generateController($input, $io, $generator, $controllerClassNameDetails);
  154. $controllerClassFullName = $controllerClassNameDetails->getFullName();
  155. }
  156. $this->generateService($input, $io, $adminClassFullName, $controllerClassFullName);
  157. }
  158. private function getAdminServiceId(string $adminClassBasename): string
  159. {
  160. return Container::underscore(sprintf(
  161. 'admin.%s',
  162. str_replace('\\', '.', 'Admin' === substr($adminClassBasename, -5) ?
  163. substr($adminClassBasename, 0, -5) : $adminClassBasename)
  164. ));
  165. }
  166. private function generateService(
  167. InputInterface $input,
  168. ConsoleStyle $io,
  169. string $adminClassFullName,
  170. string $controllerClassFullName
  171. ): void
  172. {
  173. if ($servicesFile = $input->getOption('services')) {
  174. $file = sprintf('%s/config/%s', $this->projectDirectory, $servicesFile);
  175. $servicesManipulator = new ServicesManipulator($file);
  176. $controllerName = $this->controllerClassBasename ? $controllerClassFullName : '~';
  177. $id = $input->getOption('id') ?:
  178. $this->getAdminServiceId('App', $this->adminClassBasename);
  179. $servicesManipulator->addResource(
  180. $id,
  181. $this->modelClass,
  182. $adminClassFullName,
  183. $controllerName,
  184. substr($this->managerType, \strlen('sonata.admin.manager.'))
  185. );
  186. $io->writeln(sprintf(
  187. '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".',
  188. PHP_EOL,
  189. $id,
  190. realpath($file)
  191. ));
  192. }
  193. }
  194. private function generateController(
  195. InputInterface $input,
  196. ConsoleStyle $io,
  197. Generator $generator,
  198. ClassNameDetails $controllerClassNameDetails
  199. ): void
  200. {
  201. $controllerClassFullName = null;
  202. if ($controllerClassNameDetails) {
  203. $controllerClassFullName = $controllerClassNameDetails->getFullName();
  204. $generator->generateClass(
  205. $controllerClassFullName,
  206. __DIR__ . '/../Resources/skeleton/make/AdminController.tpl.php',
  207. []
  208. );
  209. $generator->writeChanges();
  210. $io->writeln(sprintf(
  211. '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
  212. PHP_EOL,
  213. $controllerClassNameDetails->getShortName(),
  214. $controllerClassFullName
  215. ));
  216. }
  217. }
  218. private function generateAdmin(
  219. ConsoleStyle $io,
  220. Generator $generator,
  221. ClassNameDetails $adminClassNameDetails
  222. ): void
  223. {
  224. $adminClassFullName = $adminClassNameDetails->getFullName();
  225. $fields = $this->modelManager->getExportFields($this->modelClass);
  226. $fieldString = '';
  227. foreach ($fields as $field) {
  228. $fieldString = $fieldString . sprintf('%12s', '') . "->add('" . $field . "')" . PHP_EOL;
  229. }
  230. $fieldString .= sprintf('%12s', '');
  231. $generator->generateClass(
  232. $adminClassFullName,
  233. __DIR__ . '/../Resources/skeleton/make/Admin.tpl.php',
  234. ['fields' => $fieldString]
  235. );
  236. $generator->writeChanges();
  237. $io->writeln(sprintf(
  238. '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
  239. PHP_EOL,
  240. $adminClassNameDetails->getShortName(),
  241. $adminClassFullName
  242. ));
  243. }
  244. private function configure(InputInterface $input): void
  245. {
  246. $this->modelClass = Validators::validateClass($input->getArgument('model'));
  247. $this->modelClassBasename = (new \ReflectionClass($this->modelClass))->getShortName();
  248. $this->adminClassBasename = Validators::validateAdminClassBasename(
  249. $input->getOption('admin') ?: $this->modelClassBasename . 'Admin'
  250. );
  251. if ($this->controllerClassBasename = $input->getOption('controller')) {
  252. $this->controllerClassBasename = Validators::validateControllerClassBasename($this->controllerClassBasename);
  253. }
  254. if (0 === \count($this->availableModelManagers)) {
  255. throw new \InvalidArgumentException('There are no model managers registered.');
  256. }
  257. $this->managerType = $input->getOption('manager') ?: array_keys($this->availableModelManagers)[0];
  258. $this->modelManager = $this->availableModelManagers[$this->managerType] ?: current($this->availableModelManagers);
  259. }
  260. }