Skip to content
This repository has been archived by the owner on Feb 4, 2023. It is now read-only.

Add symfony4 maker bundle support (Fix #814) #833

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DependencyInjection/SgDatatablesExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}

/**
Expand Down
237 changes: 237 additions & 0 deletions Maker/MakeDatatable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?php

namespace Sg\DatatablesBundle\Maker;


use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\MakerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
Use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Bundle\MakerBundle\Validator;
use Symfony\Bundle\TwigBundle;
use Doctrine\Bundle\DoctrineBundle;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Console\Question\Question;
use Doctrine\ORM\Mapping\ClassMetadataInfo;


class MakeDatatable extends AbstractMaker
{
private $doctrineHelper;
private $baseSleleton;
private $columnTypes = [ 'ActionColumn' => '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. <fg=yellow>%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;
}
}
8 changes: 8 additions & 0 deletions Resources/config/maker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
sg_datatables.maker:
class: Sg\DatatablesBundle\Maker\MakeDatatable
public: true
arguments:
- '@maker.doctrine_helper'
tags:
- { name: maker.command }
5 changes: 5 additions & 0 deletions Resources/help/MakeDatatable.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The <info>%command.name%</info> command generates datatable class for selected entity.

<info>php %command.full_name% BlogPost</info>

If the argument is missing, the command will ask for the entity class name interactively.
102 changes: 102 additions & 0 deletions Resources/views/skeleton/Datatable.tpl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?= "<?php" . PHP_EOL ?>

namespace <?= $namespace ?>;

use Sg\DatatablesBundle\Datatable\AbstractDatatable;

<?php foreach ($column_types as $type): ?>
use Sg\DatatablesBundle\Datatable\Column\<?= $type ?>;
<?php endforeach; ?>

/**
* Class <?= $class_name.PHP_EOL ?>
*
* @package <?= $namespace ?>\Datatables
*/
class <?= $class_name ?> 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
<?php foreach ($fields as $field): ?>
->add('<?= $field['property'] ?>', <?= $field['column_type'] ?>, array(
'title' => '<?= $field['title'] ?>',
<?php if (isset($field['data']) && $field['data']!== null ): ?>
'data' => '<?= $field['data'] ?>'
<?php endif ?>
))
<?php endforeach; ?>
->add(null, ActionColumn::class, array(
'title' => $this->translator->trans('sg.datatables.actions.title'),
'actions' => array(
array(
'route' => '<?= $route_pref ?>_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' => '<?= $route_pref ?>_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 '<?= $bounded_full_class_name ?>';
}

/**
* {@inheritdoc}
*/
public function getName()
{
return '<?= $datatable_name ?>';
}
}