GenerateAdminCommand.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. /*
  3. * This file is part of the Sonata package.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sonata\AdminBundle\Command;
  11. use Doctrine\Common\Persistence\Mapping\ClassMetadata;
  12. use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
  13. use Sonata\AdminBundle\Generator\AdminGenerator;
  14. use Sonata\AdminBundle\Generator\ControllerGenerator;
  15. use Sonata\AdminBundle\Manipulator\ServicesManipulator;
  16. use Sonata\AdminBundle\Model\ModelManagerInterface;
  17. use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
  18. use Symfony\Bundle\FrameworkBundle\Console\Application;
  19. use Symfony\Component\Console\Input\InputArgument;
  20. use Symfony\Component\Console\Input\InputInterface;
  21. use Symfony\Component\Console\Input\InputOption;
  22. use Symfony\Component\Console\Output\OutputInterface;
  23. use Symfony\Component\DependencyInjection\Container;
  24. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  25. /**
  26. * @author Marek Stipek <mario.dweller@seznam.cz>
  27. * @author Simon Cosandey <simon.cosandey@simseo.ch>
  28. */
  29. class GenerateAdminCommand extends ContainerAwareCommand
  30. {
  31. /** @var string[] */
  32. private $managerTypes;
  33. /**
  34. * {@inheritDoc}
  35. */
  36. public function configure()
  37. {
  38. $this
  39. ->setName('sonata:admin:generate')
  40. ->setDescription('Generates an admin class based on the given entity class')
  41. ->addArgument('entity', InputArgument::REQUIRED, 'The entity name')
  42. ->addArgument('controller', InputArgument::OPTIONAL, 'The controller class name')
  43. ->addOption('bundle', 'b', InputOption::VALUE_OPTIONAL, 'The bundle name')
  44. ->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'The manager type')
  45. ->addOption('services', 'y', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yml')
  46. ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID')
  47. ;
  48. }
  49. /**
  50. * {@inheritDoc}
  51. */
  52. protected function execute(InputInterface $input, OutputInterface $output)
  53. {
  54. $skeletonDirectory = __DIR__ . '/../Resources/skeleton';
  55. $entity = $input->getArgument('entity');
  56. list($bundleName, $entityClassName) = Validators::validateEntityName($entity);
  57. $bundle = $this->getBundle($input->getOption('bundle') ?: $bundleName);
  58. $modelManager = $this->getModelManager($input->getOption('type') ?: $this->getDefaultManagerType());
  59. if (!$entityClass = $this->getEntityClass($modelManager, $entity)) {
  60. $entityClass = sprintf('%s\Entity\%s', $bundle->getNamespace(), $entityClassName);
  61. }
  62. $adminGenerator = new AdminGenerator($modelManager, $skeletonDirectory);
  63. try {
  64. $adminGenerator->generate($bundle, $entityClassName . 'Admin', $entityClass);
  65. $output->writeln(sprintf(
  66. '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
  67. "\n",
  68. $adminGenerator->getClass(),
  69. realpath($adminGenerator->getFile())
  70. ));
  71. } catch (\Exception $e) {
  72. $this->writeError($output, $e->getMessage());
  73. }
  74. if ($controller = $input->getArgument('controller')) {
  75. $controllerGenerator = new ControllerGenerator($skeletonDirectory);
  76. try {
  77. $controllerGenerator->generate($bundle, $entityClassName . 'AdminController');
  78. $output->writeln(sprintf(
  79. '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
  80. "\n",
  81. $controllerGenerator->getClass(),
  82. realpath($controllerGenerator->getFile())
  83. ));
  84. } catch (\Exception $e) {
  85. $this->writeError($output, $e->getMessage());
  86. }
  87. }
  88. if ($services = $input->getOption('services')) {
  89. $adminClass = $adminGenerator->getClass();
  90. $file = sprintf('%s/Resources/config/%s', $bundle->getPath(), $services);
  91. $servicesManipulator = new ServicesManipulator($file);
  92. $controllerName = $controller
  93. ? sprintf('%s:%s', $bundle->getName(), substr($controller, 0, -10))
  94. : 'SonataAdminBundle:CRUD'
  95. ;
  96. try {
  97. $id = $input->getOption('id') ?: $this->getAdminServiceId($bundle->getName(), $entityClassName);
  98. $servicesManipulator->addResource($id, $entityClass, $adminClass, $controllerName);
  99. $output->writeln(sprintf(
  100. '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".',
  101. "\n",
  102. $id,
  103. realpath($file)
  104. ));
  105. } catch (\Exception $e) {
  106. $this->writeError($output, $e->getMessage());
  107. }
  108. }
  109. return 0;
  110. }
  111. /**
  112. * {@inheritDoc}
  113. */
  114. protected function interact(InputInterface $input, OutputInterface $output)
  115. {
  116. $dialog = $this->getDialogHelper();
  117. $dialog->writeSection($output, 'Welcome to the Sonata admin generator');
  118. list($bundleName, $entity) = $this->askAndValidate(
  119. $output,
  120. 'The entity name',
  121. $input->getArgument('entity'),
  122. 'Sonata\AdminBundle\Command\Validators::validateEntityName'
  123. );
  124. $bundleName = $this->askAndValidate(
  125. $output,
  126. 'The bundle name',
  127. $input->getOption('bundle') ?: $bundleName,
  128. 'Sensio\Bundle\GeneratorBundle\Command\Validators::validateBundleName'
  129. );
  130. if (count($this->getManagerTypes()) > 1) {
  131. $managerType = $this->askAndValidate(
  132. $output,
  133. 'The manager type',
  134. $input->getOption('type') ?: $this->getDefaultManagerType(),
  135. array($this, 'validateManagerType')
  136. );
  137. $input->setOption('type', $managerType);
  138. }
  139. $question = $dialog->getQuestion('Do you want to generate a controller', 'no', '?');
  140. if ($dialog->askConfirmation($output, $question, false)) {
  141. $controller = $this->askAndValidate(
  142. $output,
  143. 'The controller class name',
  144. $input->getArgument('controller') ?: $entity . 'AdminController',
  145. 'Sonata\AdminBundle\Command\Validators::validateControllerClassName'
  146. );
  147. $input->setArgument('controller', $controller);
  148. }
  149. $question = $dialog->getQuestion('Do you want to update the services YAML configuration file', 'yes', '?');
  150. if ($dialog->askConfirmation($output, $question)) {
  151. $path = $this->getBundle($bundleName)->getPath() . '/Resources/config/';
  152. $services = $this->askAndValidate(
  153. $output,
  154. 'The services YAML configuration file',
  155. is_file($path . 'admin.yml') ? 'admin.yml' : 'services.yml',
  156. 'Sonata\AdminBundle\Command\Validators::validateServicesFile'
  157. );
  158. $id = $this->askAndValidate(
  159. $output,
  160. 'The admin service ID',
  161. $this->getAdminServiceId($bundleName, $entity),
  162. 'Sonata\AdminBundle\Command\Validators::validateServiceId'
  163. );
  164. $input->setOption('services', $services);
  165. $input->setOption('id', $id);
  166. }
  167. $input->setArgument('entity', sprintf('%s:%s', $bundleName, $entity));
  168. $input->setOption('bundle', $bundleName);
  169. }
  170. /**
  171. * @param string $managerType
  172. * @return string
  173. * @throws \InvalidArgumentException
  174. */
  175. public function validateManagerType($managerType)
  176. {
  177. $managerTypes = $this->getManagerTypes();
  178. if (!in_array($managerType, $managerTypes)) {
  179. throw new \InvalidArgumentException(sprintf(
  180. 'Invalid manager type "%s". Valid manager types are "%s".',
  181. $managerType,
  182. implode('", "', $managerTypes)
  183. ));
  184. }
  185. return $managerType;
  186. }
  187. /**
  188. * @param OutputInterface $output
  189. * @param string $question
  190. * @param mixed $default
  191. * @param callable $validator
  192. * @return mixed
  193. */
  194. private function askAndValidate(OutputInterface $output, $question, $default, $validator)
  195. {
  196. $dialog = $this->getDialogHelper();
  197. return $dialog->askAndValidate($output, $dialog->getQuestion($question, $default), $validator, false, $default);
  198. }
  199. /**
  200. * @param OutputInterface $output
  201. * @param string $message
  202. */
  203. private function writeError(OutputInterface $output, $message)
  204. {
  205. $output->writeln(sprintf("\n<error>%s</error>", $message));
  206. }
  207. /**
  208. * @param string $name
  209. * @return BundleInterface
  210. */
  211. private function getBundle($name)
  212. {
  213. $application = $this->getApplication();
  214. /* @var $application Application */
  215. return $application->getKernel()->getBundle($name);
  216. }
  217. /**
  218. * @param ModelManagerInterface $modelManager
  219. * @param string $entity
  220. * @return string|null
  221. */
  222. private function getEntityClass(ModelManagerInterface $modelManager, $entity)
  223. {
  224. if (is_callable(array($modelManager, 'getMetadata'))) {
  225. $metadata = $modelManager->getMetadata($entity);
  226. if ($metadata instanceof ClassMetadata) {
  227. return $metadata->name;
  228. };
  229. }
  230. return null;
  231. }
  232. /**
  233. * @return string[]
  234. */
  235. private function getManagerTypes()
  236. {
  237. $container = $this->getContainer();
  238. if (!$container instanceof Container) {
  239. return array();
  240. }
  241. if ($this->managerTypes === null) {
  242. $this->managerTypes = array();
  243. foreach ($container->getServiceIds() as $id) {
  244. if (!strncmp($id, 'sonata.admin.manager.', 21)) {
  245. $this->managerTypes[] = substr($id, 21);
  246. }
  247. }
  248. }
  249. return $this->managerTypes;
  250. }
  251. /**
  252. * @return string
  253. * @throws \RuntimeException
  254. */
  255. private function getDefaultManagerType()
  256. {
  257. if (!$managerTypes = $this->getManagerTypes()) {
  258. throw new \RuntimeException('There are no registered model managers.');
  259. }
  260. return $managerTypes[0];
  261. }
  262. /**
  263. * @param string $managerType
  264. * @return ModelManagerInterface
  265. */
  266. private function getModelManager($managerType)
  267. {
  268. return $this->getContainer()->get('sonata.admin.manager.' . $managerType);
  269. }
  270. /**
  271. * @param string $bundleName
  272. * @param string $entityClassName
  273. * @return string
  274. */
  275. private function getAdminServiceId($bundleName, $entityClassName)
  276. {
  277. return Container::underscore(sprintf('%s.admin.%s', substr($bundleName, 0, -6), $entityClassName));
  278. }
  279. /**
  280. * @return DialogHelper
  281. */
  282. private function getDialogHelper()
  283. {
  284. $dialogHelper = $this->getHelper('dialog');
  285. if (!$dialogHelper instanceof DialogHelper) {
  286. $dialogHelper = new DialogHelper();
  287. $this->getHelperSet()->set($dialogHelper);
  288. }
  289. return $dialogHelper;
  290. }
  291. }