diff --git a/DependencyInjection/SgDatatablesExtension.php b/DependencyInjection/SgDatatablesExtension.php index 37e1ee8f..a4de9a93 100644 --- a/DependencyInjection/SgDatatablesExtension.php +++ b/DependencyInjection/SgDatatablesExtension.php @@ -36,6 +36,12 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('services.yml'); $container->setParameter('sg_datatables.datatable.query', $config['datatable']['query']); + + $bundles = $container->getParameter('kernel.bundles'); + + if(isset($bundles['MakerBundle'])) { + $loader->load('maker.yml'); + } } /** diff --git a/Maker/MakeDatatable.php b/Maker/MakeDatatable.php new file mode 100644 index 00000000..762c671e --- /dev/null +++ b/Maker/MakeDatatable.php @@ -0,0 +1,237 @@ + 'ActionColumn']; + + public function __construct(DoctrineHelper $doctrineHelper) + { + $this->doctrineHelper = $doctrineHelper; + $this->baseSleleton = realpath(__DIR__.'/../Resources/views/skeleton'); + } + + + public static function getCommandName(): string + { + return 'make:datatable'; + } + + /** + * {@inheritdoc} + */ + public function configureCommand(Command $command, InputConfiguration $inputConfig) + { + $command + ->setDescription('Creates Datable for Doctrine entity class') + ->addArgument('entity-class', InputArgument::OPTIONAL, sprintf('The class name of the entity to create CRUD (e.g. %s)', Str::asClassName(Str::getRandomTerm()))) + ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeDatatable.txt')) + ; + $inputConfig->setArgumentAsNonInteractive('entity-class'); + } + + public function interact(InputInterface $input, ConsoleStyle $io, Command $command) + { + if (null === $input->getArgument('entity-class')) { + $argument = $command->getDefinition()->getArgument('entity-class'); + $entities = $this->doctrineHelper->getEntitiesForAutocomplete(); + $question = new Question($argument->getDescription()); + $question->setAutocompleterValues($entities); + $value = $io->askQuestion($question); + $input->setArgument('entity-class', $value); + } + } + + /** + * {@inheritdoc} + */ + public function configureDependencies(DependencyBuilder $dependencies) + { + } + + public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) + { + $entityClassDetails = $generator->createClassNameDetails( + Validator::entityExists($input->getArgument('entity-class'), $this->doctrineHelper->getEntitiesForAutocomplete()), + 'Entity\\' + ); + + + $entityDoctrineDetails = $this->doctrineHelper->createDoctrineDetails($entityClassDetails->getFullName()); + + $datatableClassDetails = $generator->createClassNameDetails( + $entityClassDetails->getRelativeNameWithoutSuffix(), + 'Datatables\\', + 'Datatable' + ); + + $className = $datatableClassDetails->getShortName(); + $entityClassLowerCase = strtolower($className); + + $metadata = $this->getEntityMetadata($entityClassDetails->getFullName()); + $fields = $this->getFieldsFromMetadata($metadata); + + sort($this->columnTypes); + $generator->generateClass( + $datatableClassDetails->getFullName(), + $this->baseSleleton . '/Datatable.tpl.php', + [ + 'bounded_full_class_name' => $entityClassDetails->getFullName(), + 'bounded_class_name' => $entityClassDetails->getShortName(), + 'fields' => $fields, + 'class_name' => $className, + 'datatable_name' => $entityClassLowerCase.'_datatable', + 'route_pref' => $entityClassLowerCase, + 'column_types' => $this->columnTypes, + 'id' => $this->getIdentifierFromMetadata($metadata), + ] + ); + + $generator->writeChanges(); + + } + + //------------------------------------------------- + // Helper + //------------------------------------------------- + + /** + * Parse fields. + * + * @param string $input + * + * @return array + */ + private static function parseFields($input) + { + $fields = array(); + + foreach (explode(' ', $input) as $value) { + $elements = explode(':', $value); + + $row = array(); + $row['property'] = $elements[0]; + $row['column_type'] = 'Column::class'; + $row['data'] = null; + $row['title'] = ucwords(str_replace('.', ' ', $elements[0])); + + if (isset($elements[1])) { + switch ($elements[1]) { + case 'datetime': + $row['column_type'] = 'DateTimeColumn::class'; + break; + case 'boolean': + $row['column_type'] = 'BooleanColumn::class'; + break; + } + } + + $fields[] = $row; + } + + return $fields; + } + + private function getEntityMetadata($class) + { + return $this->doctrineHelper->getMetadata($class, true); + } + + /** + * Get Id from metadata. + * + * @param array $metadata + * + * @return mixed + * @throws Exception + */ + private function getIdentifierFromMetadata(ClassMetadataInfo $metadata) + { + if (count($metadata->identifier) > 1) { + throw new Exception('CreateDatatableCommand::getIdentifierFromMetadata(): The Datatable generator does not support entities with multiple primary keys.'); + } + + return $metadata->identifier; + } + + /** + * Returns an array of fields. Fields can be both column fields and + * association fields. + * + * @param ClassMetadataInfo $metadata + * + * @return array $fields + */ + private function getFieldsFromMetadata(ClassMetadataInfo $metadata) + { + $fields = array(); + + foreach ($metadata->fieldMappings as $field) { + $row = array(); + $row['property'] = $field['fieldName']; + + switch ($field['type']) { + case 'datetime': + $row['column_type'] = 'DateTimeColumn::class'; + break; + case 'boolean': + $row['column_type'] = 'BooleanColumn::class'; + break; + default: + $row['column_type'] = 'Column::class'; + } + + $row['title'] = ucwords($field['fieldName']); + $row['data'] = null; + $fields[] = $row; + if(!isset($this->columnTypes[$row['column_type']])) { + $this->columnTypes[$row['column_type']] = substr($row['column_type'], 0, -7); + } + + } + + foreach ($metadata->associationMappings as $relation) { + $targetClass = $relation['targetEntity']; + $targetMetadata = $this->getEntityMetadata($targetClass, true); + + foreach ($targetMetadata->fieldMappings as $field) { + $row = array(); + $row['property'] = $relation['fieldName'].'.'.$field['fieldName']; + $row['column_type'] = 'Column::class'; + $row['title'] = ucwords(str_replace('.', ' ', $row['property'])); + if ($relation['type'] === ClassMetadataInfo::ONE_TO_MANY || $relation['type'] === ClassMetadataInfo::MANY_TO_MANY) { + $row['data'] = $relation['fieldName'].'[, ].'.$field['fieldName']; + } else { + $row['data'] = null; + } + $fields[] = $row; + } + } + + return $fields; + } +} \ No newline at end of file diff --git a/Resources/config/maker.yml b/Resources/config/maker.yml new file mode 100644 index 00000000..21915e19 --- /dev/null +++ b/Resources/config/maker.yml @@ -0,0 +1,8 @@ +services: + sg_datatables.maker: + class: Sg\DatatablesBundle\Maker\MakeDatatable + public: true + arguments: + - '@maker.doctrine_helper' + tags: + - { name: maker.command } \ No newline at end of file diff --git a/Resources/help/MakeDatatable.txt b/Resources/help/MakeDatatable.txt new file mode 100644 index 00000000..4ec2c3a9 --- /dev/null +++ b/Resources/help/MakeDatatable.txt @@ -0,0 +1,5 @@ +The %command.name% command generates datatable class for selected entity. + +php %command.full_name% BlogPost + +If the argument is missing, the command will ask for the entity class name interactively. \ No newline at end of file diff --git a/Resources/views/skeleton/Datatable.tpl.php b/Resources/views/skeleton/Datatable.tpl.php new file mode 100644 index 00000000..1e96dfee --- /dev/null +++ b/Resources/views/skeleton/Datatable.tpl.php @@ -0,0 +1,102 @@ + + +namespace ; + +use Sg\DatatablesBundle\Datatable\AbstractDatatable; + + +use Sg\DatatablesBundle\Datatable\Column\; + + +/** + * Class + * + * @package \Datatables + */ +class extends AbstractDatatable +{ + + /** + * {@inheritdoc} + * + * @throws \Exception + */ + public function buildDatatable(array $options = array()) + { + $this->language->set(array( + 'cdn_language_by_locale' => true + //'language' => 'de' + )); + + $this->ajax->set(array( + )); + + $this->options->set(array( + 'individual_filtering' => true, + 'individual_filtering_position' => 'head', + 'order_cells_top' => true, + )); + + $this->features->set(array( + )); + + $this->columnBuilder + + ->add('', , array( + 'title' => '', + + 'data' => '' + + )) + + ->add(null, ActionColumn::class, array( + 'title' => $this->translator->trans('sg.datatables.actions.title'), + 'actions' => array( + array( + 'route' => '_show', + 'route_parameters' => array( + 'id' => 'id' + ), + 'label' => $this->translator->trans('sg.datatables.actions.show'), + 'icon' => 'glyphicon glyphicon-eye-open', + 'attributes' => array( + 'rel' => 'tooltip', + 'title' => $this->translator->trans('sg.datatables.actions.show'), + 'class' => 'btn btn-primary btn-xs', + 'role' => 'button' + ), + ), + array( + 'route' => '_edit', + 'route_parameters' => array( + 'id' => 'id' + ), + 'label' => $this->translator->trans('sg.datatables.actions.edit'), + 'icon' => 'glyphicon glyphicon-edit', + 'attributes' => array( + 'rel' => 'tooltip', + 'title' => $this->translator->trans('sg.datatables.actions.edit'), + 'class' => 'btn btn-primary btn-xs', + 'role' => 'button' + ), + ) + ) + )); + } + + /** + * {@inheritdoc} + */ + public function getEntity() + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return ''; + } +} \ No newline at end of file