This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Bundle;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
class BundleMetadata
{
/**
* @var BundleInterface
*/
protected $bundle;
/**
* @var string|bool
*/
protected $vendor = false;
/**
* @var bool
*/
protected $valid = false;
/**
* @var string
*/
protected $namespace;
/**
* @var string
*/
protected $name;
/**
* @var bool
*/
protected $extendedDirectory = false;
/**
* @var bool
*/
protected $extendedNamespace = false;
/**
* @var array
*/
protected $configuration = array();
/**
* @var OrmMetadata
*/
protected $ormMetadata = null;
/**
* @var OdmMetadata
*/
protected $odmMetadata = null;
/**
* @var PhpcrMetadata
*/
protected $phpcrMetadata = null;
/**
* @param BundleInterface $bundle
* @param array $configuration
*/
public function __construct(BundleInterface $bundle, array $configuration = array())
{
$this->bundle = $bundle;
$this->configuration = $configuration;
$this->buildInformation();
}
/**
* @return bool
*/
public function isExtendable()
{
// does not extends Application bundle ...
return !(
strpos($this->getClass(), $this->configuration['namespace']) === 0
|| strpos($this->getClass(), 'Symfony') === 0
);
}
/**
* @return string
*/
public function getClass()
{
return get_class($this->bundle);
}
/**
* @return bool
*/
public function isValid()
{
return $this->valid;
}
/**
* @return string
*/
public function getExtendedDirectory()
{
return $this->extendedDirectory;
}
/**
* @return string
*/
public function getVendor()
{
return $this->vendor;
}
/**
* @return string
*/
public function getExtendedNamespace()
{
return $this->extendedNamespace;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* return the bundle name.
*
* @return string return the bundle name
*/
public function getName()
{
return $this->name;
}
/**
* @return BundleInterface
*/
public function getBundle()
{
return $this->bundle;
}
/**
* @return OdmMetadata
*/
public function getOdmMetadata()
{
return $this->odmMetadata;
}
/**
* @return OrmMetadata
*/
public function getOrmMetadata()
{
return $this->ormMetadata;
}
/**
* @return PhpcrMetadata
*/
public function getPhpcrMetadata()
{
return $this->phpcrMetadata;
}
/**
* build basic information and check if the bundle respect the following convention
* Vendor/BundleNameBundle/VendorBundleNameBundle.
*
* if the bundle does not respect this convention then the easy extends command will ignore
* this bundle
*/
protected function buildInformation()
{
$information = explode('\\', $this->getClass());
if (!$this->isExtendable()) {
$this->valid = false;
return;
}
if (count($information) != 3) {
$this->valid = false;
return;
}
if ($information[0].$information[1] != $information[2]) {
$this->valid = false;
return;
}
$this->name = $information[count($information) - 1];
$this->vendor = $information[0];
$this->namespace = sprintf('%s\\%s', $this->vendor, $information[1]);
$this->extendedDirectory =
str_replace(':vendor', $this->vendor, $this->configuration['application_dir']).
DIRECTORY_SEPARATOR.
$information[1];
$this->extendedNamespace = sprintf('%s\\%s',
str_replace(':vendor', $this->vendor, $this->configuration['namespace']),
$information[1]
);
$this->valid = true;
$this->ormMetadata = new OrmMetadata($this);
$this->odmMetadata = new OdmMetadata($this);
$this->phpcrMetadata = new PhpcrMetadata($this);
}
}

View File

@@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Bundle;
use Symfony\Component\Finder\Finder;
class OdmMetadata
{
/**
* @var string
*/
protected $mappingDocumentDirectory;
/**
* @var string
*/
protected $extendedMappingDocumentDirectory;
/**
* @var string
*/
protected $documentDirectory;
/**
* @var string
*/
protected $extendedDocumentDirectory;
/**
* @var string
*/
protected $extendedSerializerDirectory;
/**
* @param BundleMetadata $bundleMetadata
*/
public function __construct(BundleMetadata $bundleMetadata)
{
$this->mappingDocumentDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getBundle()->getPath());
$this->extendedMappingDocumentDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getExtendedDirectory());
$this->documentDirectory = sprintf('%s/Document', $bundleMetadata->getBundle()->getPath());
$this->extendedDocumentDirectory = sprintf('%s/Document', $bundleMetadata->getExtendedDirectory());
$this->extendedSerializerDirectory = sprintf('%s/Resources/config/serializer', $bundleMetadata->getExtendedDirectory());
}
/**
* @return string
*/
public function getMappingDocumentDirectory()
{
return $this->mappingDocumentDirectory;
}
/**
* @return string
*/
public function getExtendedMappingDocumentDirectory()
{
return $this->extendedMappingDocumentDirectory;
}
/**
* @return string
*/
public function getDocumentDirectory()
{
return $this->documentDirectory;
}
/**
* @return string
*/
public function getExtendedDocumentDirectory()
{
return $this->extendedDocumentDirectory;
}
/**
* @return string
*/
public function getExtendedSerializerDirectory()
{
return $this->extendedSerializerDirectory;
}
/**
* @return array|\Iterator
*/
public function getDocumentMappingFiles()
{
try {
$f = new Finder();
$f->name('*.mongodb.xml.skeleton');
$f->in($this->getMappingDocumentDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
/**
* @return array
*/
public function getDocumentNames()
{
$names = array();
try {
$f = new Finder();
$f->name('*.mongodb.xml.skeleton');
$f->in($this->getMappingDocumentDirectory());
foreach ($f->getIterator() as $file) {
$name = explode('.', basename($file));
$names[] = $name[0];
}
} catch (\Exception $e) {
}
return $names;
}
/**
* @return array|\Iterator
*/
public function getRepositoryFiles()
{
try {
$f = new Finder();
$f->name('*Repository.php');
$f->in($this->getDocumentDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Bundle;
use Symfony\Component\Finder\Finder;
class OrmMetadata
{
/**
* @var string
*/
protected $mappingEntityDirectory;
/**
* @var string
*/
protected $extendedMappingEntityDirectory;
/**
* @var string
*/
protected $entityDirectory;
/**
* @var string
*/
protected $extendedEntityDirectory;
/**
* @var string
*/
protected $extendedSerializerDirectory;
/**
* @param BundleMetadata $bundleMetadata
*/
public function __construct(BundleMetadata $bundleMetadata)
{
$this->mappingEntityDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getBundle()->getPath());
$this->extendedMappingEntityDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getExtendedDirectory());
$this->entityDirectory = sprintf('%s/Entity', $bundleMetadata->getBundle()->getPath());
$this->extendedEntityDirectory = sprintf('%s/Entity', $bundleMetadata->getExtendedDirectory());
$this->extendedSerializerDirectory = sprintf('%s/Resources/config/serializer', $bundleMetadata->getExtendedDirectory());
}
/**
* @return string
*/
public function getMappingEntityDirectory()
{
return $this->mappingEntityDirectory;
}
/**
* @return string
*/
public function getExtendedMappingEntityDirectory()
{
return $this->extendedMappingEntityDirectory;
}
/**
* @return string
*/
public function getEntityDirectory()
{
return $this->entityDirectory;
}
/**
* @return string
*/
public function getExtendedEntityDirectory()
{
return $this->extendedEntityDirectory;
}
/**
* @return string
*/
public function getExtendedSerializerDirectory()
{
return $this->extendedSerializerDirectory;
}
/**
* @return array|\Iterator
*/
public function getEntityMappingFiles()
{
try {
$f = new Finder();
$f->name('*.orm.xml.skeleton');
$f->name('*.orm.yml.skeleton');
$f->in($this->getMappingEntityDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
/**
* @return array
*/
public function getEntityNames()
{
$names = array();
try {
$f = new Finder();
$f->name('*.orm.xml.skeleton');
$f->name('*.orm.yml.skeleton');
$f->in($this->getMappingEntityDirectory());
foreach ($f->getIterator() as $file) {
$name = explode('.', basename($file));
$names[] = $name[0];
}
} catch (\Exception $e) {
}
return $names;
}
/**
* @return array|\Iterator
*/
public function getRepositoryFiles()
{
try {
$f = new Finder();
$f->name('*Repository.php');
$f->in($this->getEntityDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
}

View File

@@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Bundle;
use Symfony\Component\Finder\Finder;
class PhpcrMetadata
{
/**
* @var string
*/
protected $mappingDocumentDirectory;
/**
* @var string
*/
protected $extendedMappingDocumentDirectory;
/**
* @var string
*/
protected $documentDirectory;
/**
* @var string
*/
protected $extendedDocumentDirectory;
/**
* @var string
*/
protected $extendedSerializerDirectory;
/**
* @param BundleMetadata $bundleMetadata
*/
public function __construct(BundleMetadata $bundleMetadata)
{
$this->mappingDocumentDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getBundle()->getPath());
$this->extendedMappingDocumentDirectory = sprintf('%s/Resources/config/doctrine/', $bundleMetadata->getExtendedDirectory());
$this->documentDirectory = sprintf('%s/PHPCR', $bundleMetadata->getBundle()->getPath());
$this->extendedDocumentDirectory = sprintf('%s/PHPCR', $bundleMetadata->getExtendedDirectory());
$this->extendedSerializerDirectory = sprintf('%s/Resources/config/serializer', $bundleMetadata->getExtendedDirectory());
}
/**
* @return string
*/
public function getMappingDocumentDirectory()
{
return $this->mappingDocumentDirectory;
}
/**
* @return string
*/
public function getExtendedMappingDocumentDirectory()
{
return $this->extendedMappingDocumentDirectory;
}
/**
* @return string
*/
public function getDocumentDirectory()
{
return $this->documentDirectory;
}
/**
* @return string
*/
public function getExtendedDocumentDirectory()
{
return $this->extendedDocumentDirectory;
}
/**
* @return string
*/
public function getExtendedSerializerDirectory()
{
return $this->extendedSerializerDirectory;
}
/**
* @return array|\Iterator
*/
public function getDocumentMappingFiles()
{
try {
$f = new Finder();
$f->name('*.phpcr.xml.skeleton');
$f->in($this->getMappingDocumentDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
/**
* @return array
*/
public function getDocumentNames()
{
$names = array();
try {
$f = new Finder();
$f->name('*.phpcr.xml.skeleton');
$f->in($this->getMappingDocumentDirectory());
foreach ($f->getIterator() as $file) {
$name = explode('.', basename($file));
$names[] = $name[0];
}
} catch (\Exception $e) {
}
return $names;
}
/**
* @return array|\Iterator
*/
public function getRepositoryFiles()
{
try {
$f = new Finder();
$f->name('*Repository.php');
$f->in($this->getDocumentDirectory());
return $f->getIterator();
} catch (\Exception $e) {
return array();
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Command;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Generate Application entities from bundle entities.
*
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class DumpMappingCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('sonata:easy-extends:dump-mapping');
$this->setDescription('Dump some mapping information (debug only)');
$this->addArgument('manager', InputArgument::OPTIONAL, 'The manager name to use', false);
$this->addArgument('model', InputArgument::OPTIONAL, 'The class to dump', false);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$factory = $this->getContainer()->get('doctrine')->getManager($input->getArgument('manager'))->getMetadataFactory();
$metadata = $factory->getMetadataFor($input->getArgument('model'));
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter('php');
$output->writeln($exporter->exportClassMetadata($metadata));
$output->writeln('Done!');
}
}

View File

@@ -0,0 +1,171 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Command;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
/**
* Generate Application entities from bundle entities.
*
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class GenerateCommand extends ContainerAwareCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('sonata:easy-extends:generate')
->setHelp(<<<'EOT'
The <info>easy-extends:generate:entities</info> command generating a valid bundle structure from a Vendor Bundle.
<info>ie: ./app/console sonata:easy-extends:generate SonataUserBundle</info>
EOT
);
$this->setDescription('Create entities used by Sonata\'s bundles');
$this->addArgument('bundle', InputArgument::IS_ARRAY, 'The bundle name to "easy-extends"');
$this->addOption('dest', 'd', InputOption::VALUE_OPTIONAL, 'The base folder where the Application will be created', false);
$this->addOption('namespace', 'ns', InputOption::VALUE_OPTIONAL, 'The namespace for the classes', false);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$destOption = $input->getOption('dest');
if ($destOption) {
$dest = realpath($destOption);
if (false === $dest) {
throw new \RuntimeException(sprintf('The provided destination folder \'%s\' does not exist!', $destOption));
}
} else {
$dest = $this->getContainer()->get('kernel')->getRootDir();
}
$namespace = $input->getOption('namespace');
if ($namespace) {
if (!preg_match('/^(?:(?:[[:alnum:]]+|:vendor)\\\\?)+$/', $namespace)) {
throw new \InvalidArgumentException('The provided namespace \'%s\' is not a valid namespace!', $namespace);
}
} else {
$namespace = 'Application\:vendor';
}
$configuration = array(
'application_dir' => sprintf('%s%s%s', $dest, DIRECTORY_SEPARATOR, str_replace('\\', DIRECTORY_SEPARATOR, $namespace)),
'namespace' => $namespace,
);
$bundleNames = $input->getArgument('bundle');
if (empty($bundleNames)) {
$output->writeln('');
$output->writeln('<error>You must provide a bundle name!</error>');
$output->writeln('');
$output->writeln(' Bundles availables :');
/** @var BundleInterface $bundle */
foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
$bundleMetadata = new BundleMetadata($bundle, $configuration);
if (!$bundleMetadata->isExtendable()) {
continue;
}
$output->writeln(sprintf(' - %s', $bundle->getName()));
}
$output->writeln('');
} else {
foreach ($bundleNames as $bundleName) {
$processed = $this->generate($bundleName, $configuration, $output);
if (!$processed) {
throw new \RuntimeException(sprintf('<error>The bundle \'%s\' does not exist or not defined in the kernel file!</error>', $bundleName));
}
}
}
$output->writeln('done!');
}
/**
* Generates a bundle entities from a bundle name.
*
* @param string $bundleName
* @param array $configuration
* @param OutputInterface $output
*
* @return bool
*/
protected function generate($bundleName, array $configuration, $output)
{
$processed = false;
/** @var BundleInterface $bundle */
foreach ($this->getContainer()->get('kernel')->getBundles() as $bundle) {
if ($bundle->getName() != $bundleName) {
continue;
}
$processed = true;
$bundleMetadata = new BundleMetadata($bundle, $configuration);
// generate the bundle file
if (!$bundleMetadata->isExtendable()) {
$output->writeln(sprintf('Ignoring bundle : "<comment>%s</comment>"', $bundleMetadata->getClass()));
continue;
}
// generate the bundle file
if (!$bundleMetadata->isValid()) {
$output->writeln(sprintf('%s : <comment>wrong folder structure</comment>', $bundleMetadata->getClass()));
continue;
}
$output->writeln(sprintf('Processing bundle : "<info>%s</info>"', $bundleMetadata->getName()));
$this->getContainer()->get('sonata.easy_extends.generator.bundle')
->generate($output, $bundleMetadata);
$output->writeln(sprintf('Processing Doctrine ORM : "<info>%s</info>"', $bundleMetadata->getName()));
$this->getContainer()->get('sonata.easy_extends.generator.orm')
->generate($output, $bundleMetadata);
$output->writeln(sprintf('Processing Doctrine ODM : "<info>%s</info>"', $bundleMetadata->getName()));
$this->getContainer()->get('sonata.easy_extends.generator.odm')
->generate($output, $bundleMetadata);
$output->writeln(sprintf('Processing Doctrine PHPCR : "<info>%s</info>"', $bundleMetadata->getName()));
$this->getContainer()->get('sonata.easy_extends.generator.phpcr')
->generate($output, $bundleMetadata);
$output->writeln(sprintf('Processing Serializer config : "<info>%s</info>"', $bundleMetadata->getName()));
$this->getContainer()->get('sonata.easy_extends.generator.serializer')
->generate($output, $bundleMetadata);
$output->writeln('');
}
return $processed;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\DependencyInjection\Compiler;
use Sonata\EasyExtendsBundle\Mapper\DoctrineCollector;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class AddMapperInformationCompilerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('doctrine')) {
$container->removeDefinition('sonata.easy_extends.doctrine.mapper');
return;
}
$mapper = $container->getDefinition('sonata.easy_extends.doctrine.mapper');
foreach (DoctrineCollector::getInstance()->getAssociations() as $class => $associations) {
foreach ($associations as $field => $options) {
$mapper->addMethodCall('addAssociation', array($class, $field, $options));
}
}
foreach (DoctrineCollector::getInstance()->getDiscriminatorColumns() as $class => $columnDefinition) {
$mapper->addMethodCall('addDiscriminatorColumn', array($class, $columnDefinition));
}
foreach (DoctrineCollector::getInstance()->getDiscriminators() as $class => $discriminators) {
foreach ($discriminators as $key => $discriminatorClass) {
$mapper->addMethodCall('addDiscriminator', array($class, $key, $discriminatorClass));
}
}
foreach (DoctrineCollector::getInstance()->getInheritanceTypes() as $class => $type) {
$mapper->addMethodCall('addInheritanceType', array($class, $type));
}
foreach (DoctrineCollector::getInstance()->getIndexes() as $class => $indexes) {
foreach ($indexes as $field => $options) {
$mapper->addMethodCall('addIndex', array($class, $field, $options));
}
}
foreach (DoctrineCollector::getInstance()->getUniques() as $class => $uniques) {
foreach ($uniques as $field => $options) {
$mapper->addMethodCall('addUnique', array($class, $field, $options));
}
}
foreach (DoctrineCollector::getInstance()->getOverrides() as $class => $overrides) {
foreach ($overrides as $type => $options) {
$mapper->addMethodCall('addOverride', array($class, $type, $options));
}
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* EasyExtendsExtension.
*
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class SonataEasyExtendsExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('generator.xml');
$loader->load('mapper.xml');
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
class BundleGenerator implements GeneratorInterface
{
/**
* @var string
*/
protected $bundleTemplate;
public function __construct()
{
$this->bundleTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/bundle/bundle.mustache');
}
/**
* {@inheritdoc}
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$this->generateBundleDirectory($output, $bundleMetadata);
$this->generateBundleFile($output, $bundleMetadata);
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateBundleDirectory(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$directories = array(
'',
'Resources/config/serializer',
'Resources/config/doctrine',
'Resources/config/routing',
'Resources/views',
'Command',
'DependencyInjection',
'Entity',
'Document',
'PHPCR',
'Controller',
);
foreach ($directories as $directory) {
$dir = sprintf('%s/%s', $bundleMetadata->getExtendedDirectory(), $directory);
if (!is_dir($dir)) {
$output->writeln(sprintf(' > generating bundle directory <comment>%s</comment>', $dir));
mkdir($dir, 0755, true);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateBundleFile(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$application = explode('\\', $bundleMetadata->getExtendedNamespace())[0];
$file = sprintf('%s/%s%s.php', $bundleMetadata->getExtendedDirectory(), $application, $bundleMetadata->getName());
if (is_file($file)) {
return;
}
$output->writeln(sprintf(' > generating bundle file <comment>%s</comment>', $file));
$string = Mustache::replace($this->getBundleTemplate(), array(
'application' => $application,
'bundle' => $bundleMetadata->getName(),
'namespace' => $bundleMetadata->getExtendedNamespace(),
));
file_put_contents($file, $string);
}
/**
* @return string
*/
protected function getBundleTemplate()
{
return $this->bundleTemplate;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
interface GeneratorInterface
{
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata);
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
class Mustache
{
/**
* @param string $string
* @param array $parameters
*
* @return mixed
*/
public static function replace($string, array $parameters)
{
$replacer = function ($match) use ($parameters) {
return isset($parameters[$match[1]]) ? $parameters[$match[1]] : $match[0];
};
return preg_replace_callback('/{{\s*(.+?)\s*}}/', $replacer, $string);
}
/**
* @param string $file
* @param array $parameters
*
* @return mixed
*/
public static function replaceFromFile($file, array $parameters)
{
return self::replace(file_get_contents($file), $parameters);
}
}

View File

@@ -0,0 +1,172 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
class OdmGenerator implements GeneratorInterface
{
/**
* @var string
*/
protected $documentTemplate;
/**
* @var string
*/
protected $documentRepositoryTemplate;
public function __construct()
{
$this->documentTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/odm/document.mustache');
$this->documentRepositoryTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/odm/repository.mustache');
}
/**
* {@inheritdoc}
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$this->generateMappingDocumentFiles($output, $bundleMetadata);
$this->generateDocumentFiles($output, $bundleMetadata);
$this->generateDocumentRepositoryFiles($output, $bundleMetadata);
}
/**
* @return string
*/
public function getDocumentTemplate()
{
return $this->documentTemplate;
}
/**
* @return string
*/
public function getDocumentRepositoryTemplate()
{
return $this->documentRepositoryTemplate;
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateMappingDocumentFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Copy document files');
$files = $bundleMetadata->getOdmMetadata()->getDocumentMappingFiles();
foreach ($files as $file) {
// copy mapping definition
$fileName = substr($file->getFileName(), 0, strrpos($file->getFileName(), '.'));
$dest_file = sprintf('%s/%s', $bundleMetadata->getOdmMetadata()->getExtendedMappingDocumentDirectory(), $fileName);
$src_file = sprintf('%s/%s.skeleton', $bundleMetadata->getOdmMetadata()->getMappingDocumentDirectory(), $fileName);
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $fileName));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $fileName));
$mappingEntityTemplate = file_get_contents($src_file);
$string = Mustache::replace($mappingEntityTemplate, array(
'namespace' => $bundleMetadata->getExtendedNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateDocumentFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating document files');
$names = $bundleMetadata->getOdmMetadata()->getDocumentNames();
foreach ($names as $name) {
$extendedName = $name;
$dest_file = sprintf('%s/%s.php', $bundleMetadata->getOdmMetadata()->getExtendedDocumentDirectory(), $name);
$src_file = sprintf('%s/%s.php', $bundleMetadata->getOdmMetadata()->getDocumentDirectory(), $extendedName);
if (!is_file($src_file)) {
$extendedName = 'Base'.$name;
$src_file = sprintf('%s/%s.php', $bundleMetadata->getOdmMetadata()->getDocumentDirectory(), $extendedName);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%s</info>', $extendedName));
continue;
}
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $name));
$string = Mustache::replace($this->getDocumentTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name != $extendedName ? $extendedName : $name,
'class' => $name,
'extended_name' => $name == $extendedName ? 'Base'.$name : $extendedName,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateDocumentRepositoryFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating document repository files');
$names = $bundleMetadata->getOdmMetadata()->getDocumentNames();
foreach ($names as $name) {
$dest_file = sprintf('%s/%sRepository.php', $bundleMetadata->getOdmMetadata()->getExtendedDocumentDirectory(), $name);
$src_file = sprintf('%s/Base%sRepository.php', $bundleMetadata->getOdmMetadata()->getDocumentDirectory(), $name);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%sRepository</info>', $name));
continue;
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%sRepository</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%sRepository</info>', $name));
$string = Mustache::replace($this->getDocumentRepositoryTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
}

View File

@@ -0,0 +1,172 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
class OrmGenerator implements GeneratorInterface
{
/**
* @var string
*/
protected $entityTemplate;
/**
* @var string
*/
protected $entityRepositoryTemplate;
public function __construct()
{
$this->entityTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/orm/entity.mustache');
$this->entityRepositoryTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/orm/repository.mustache');
}
/**
* {@inheritdoc}
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$this->generateMappingEntityFiles($output, $bundleMetadata);
$this->generateEntityFiles($output, $bundleMetadata);
$this->generateEntityRepositoryFiles($output, $bundleMetadata);
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateMappingEntityFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Copy entity files');
$files = $bundleMetadata->getOrmMetadata()->getEntityMappingFiles();
foreach ($files as $file) {
// copy mapping definition
$fileName = substr($file->getFileName(), 0, strrpos($file->getFileName(), '.'));
$dest_file = sprintf('%s/%s', $bundleMetadata->getOrmMetadata()->getExtendedMappingEntityDirectory(), $fileName);
$src_file = sprintf('%s/%s', $bundleMetadata->getOrmMetadata()->getMappingEntityDirectory(), $file->getFileName());
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $fileName));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $fileName));
$mappingEntityTemplate = file_get_contents($src_file);
$string = Mustache::replace($mappingEntityTemplate, array(
'namespace' => $bundleMetadata->getExtendedNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateEntityFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating entity files');
$names = $bundleMetadata->getOrmMetadata()->getEntityNames();
foreach ($names as $name) {
$extendedName = $name;
$dest_file = sprintf('%s/%s.php', $bundleMetadata->getOrmMetadata()->getExtendedEntityDirectory(), $name);
$src_file = sprintf('%s/%s.php', $bundleMetadata->getOrmMetadata()->getEntityDirectory(), $extendedName);
if (!is_file($src_file)) {
$extendedName = 'Base'.$name;
$src_file = sprintf('%s/%s.php', $bundleMetadata->getOrmMetadata()->getEntityDirectory(), $extendedName);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%s</info>', $extendedName));
continue;
}
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $name));
$string = Mustache::replace($this->getEntityTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name != $extendedName ? $extendedName : $name,
'class' => $name,
'extended_name' => $name == $extendedName ? 'Base'.$name : $extendedName,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateEntityRepositoryFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating entity repository files');
$names = $bundleMetadata->getOrmMetadata()->getEntityNames();
foreach ($names as $name) {
$dest_file = sprintf('%s/%sRepository.php', $bundleMetadata->getOrmMetadata()->getExtendedEntityDirectory(), $name);
$src_file = sprintf('%s/Base%sRepository.php', $bundleMetadata->getOrmMetadata()->getEntityDirectory(), $name);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%sRepository</info>', $name));
continue;
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%sRepository</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%sRepository</info>', $name));
$string = Mustache::replace($this->getEntityRepositoryTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @return string
*/
public function getEntityTemplate()
{
return $this->entityTemplate;
}
/**
* @return string
*/
public function getEntityRepositoryTemplate()
{
return $this->entityRepositoryTemplate;
}
}

View File

@@ -0,0 +1,172 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
class PHPCRGenerator implements GeneratorInterface
{
/**
* @var string
*/
protected $DocumentTemplate;
/**
* @var string
*/
protected $DocumentRepositoryTemplate;
public function __construct()
{
$this->DocumentTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/phpcr/document.mustache');
$this->DocumentRepositoryTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/phpcr/repository.mustache');
}
/**
* {@inheritdoc}
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$this->generateMappingDocumentFiles($output, $bundleMetadata);
$this->generateDocumentFiles($output, $bundleMetadata);
$this->generateDocumentRepositoryFiles($output, $bundleMetadata);
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateMappingDocumentFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Copy Document files');
$files = $bundleMetadata->getPhpcrMetadata()->getDocumentMappingFiles();
foreach ($files as $file) {
// copy mapping definition
$fileName = substr($file->getFileName(), 0, strrpos($file->getFileName(), '.'));
$dest_file = sprintf('%s/%s', $bundleMetadata->getPhpcrMetadata()->getExtendedMappingDocumentDirectory(), $fileName);
$src_file = sprintf('%s/%s', $bundleMetadata->getPhpcrMetadata()->getMappingDocumentDirectory(), $file->getFileName());
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $fileName));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $fileName));
$mappingEntityTemplate = file_get_contents($src_file);
$string = Mustache::replace($mappingEntityTemplate, array(
'namespace' => $bundleMetadata->getExtendedNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateDocumentFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating Document files');
$names = $bundleMetadata->getPhpcrMetadata()->getDocumentNames();
foreach ($names as $name) {
$extendedName = $name;
$dest_file = sprintf('%s/%s.php', $bundleMetadata->getPhpcrMetadata()->getExtendedDocumentDirectory(), $name);
$src_file = sprintf('%s/%s.php', $bundleMetadata->getPhpcrMetadata()->getDocumentDirectory(), $extendedName);
if (!is_file($src_file)) {
$extendedName = 'Base'.$name;
$src_file = sprintf('%s/%s.php', $bundleMetadata->getPhpcrMetadata()->getDocumentDirectory(), $extendedName);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%s</info>', $extendedName));
continue;
}
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $name));
$string = Mustache::replace($this->getDocumentTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name != $extendedName ? $extendedName : $name,
'class' => $name,
'extended_name' => $name == $extendedName ? 'Base'.$name : $extendedName,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
public function generateDocumentRepositoryFiles(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$output->writeln(' - Generating Document repository files');
$names = $bundleMetadata->getPhpcrMetadata()->getDocumentNames();
foreach ($names as $name) {
$dest_file = sprintf('%s/%sRepository.php', $bundleMetadata->getPhpcrMetadata()->getExtendedDocumentDirectory(), $name);
$src_file = sprintf('%s/Base%sRepository.php', $bundleMetadata->getPhpcrMetadata()->getDocumentDirectory(), $name);
if (!is_file($src_file)) {
$output->writeln(sprintf(' ! <info>%sRepository</info>', $name));
continue;
}
if (is_file($dest_file)) {
$output->writeln(sprintf(' ~ <info>%sRepository</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%sRepository</info>', $name));
$string = Mustache::replace($this->getDocumentRepositoryTemplate(), array(
'extended_namespace' => $bundleMetadata->getExtendedNamespace(),
'name' => $name,
'namespace' => $bundleMetadata->getNamespace(),
));
file_put_contents($dest_file, $string);
}
}
}
/**
* @return string
*/
public function getDocumentTemplate()
{
return $this->DocumentTemplate;
}
/**
* @return string
*/
public function getDocumentRepositoryTemplate()
{
return $this->DocumentRepositoryTemplate;
}
}

View File

@@ -0,0 +1,125 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Generator;
use Sonata\EasyExtendsBundle\Bundle\BundleMetadata;
use Symfony\Component\Console\Output\OutputInterface;
class SerializerGenerator implements GeneratorInterface
{
/**
* @var string
*/
protected $entitySerializerTemplate;
/**
* @var string
*/
protected $documentSerializerTemplate;
public function __construct()
{
$this->entitySerializerTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/serializer/entity.mustache');
$this->documentSerializerTemplate = file_get_contents(__DIR__.'/../Resources/skeleton/serializer/document.mustache');
}
/**
* {@inheritdoc}
*/
public function generate(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$this->generateOrmSerializer($output, $bundleMetadata);
$this->generateOdmSerializer($output, $bundleMetadata);
$this->generatePhpcrSerializer($output, $bundleMetadata);
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateOrmSerializer(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$names = $bundleMetadata->getOrmMetadata()->getEntityNames();
if (is_array($names) && count($names) > 0) {
$output->writeln(' - Generating ORM serializer files');
foreach ($names as $name) {
$destFile = sprintf('%s/Entity.%s.xml', $bundleMetadata->getOrmMetadata()->getExtendedSerializerDirectory(), $name);
$this->writeSerializerFile($output, $bundleMetadata, $this->entitySerializerTemplate, $destFile, $name);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generateOdmSerializer(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$names = $bundleMetadata->getOdmMetadata()->getDocumentNames();
if (is_array($names) && count($names) > 0) {
$output->writeln(' - Generating ODM serializer files');
foreach ($names as $name) {
$destFile = sprintf('%s/Document.%s.xml', $bundleMetadata->getOdmMetadata()->getExtendedSerializerDirectory(), $name);
$this->writeSerializerFile($output, $bundleMetadata, $this->documentSerializerTemplate, $destFile, $name);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
*/
protected function generatePhpcrSerializer(OutputInterface $output, BundleMetadata $bundleMetadata)
{
$names = $bundleMetadata->getPhpcrMetadata()->getDocumentNames();
if (is_array($names) && count($names) > 0) {
$output->writeln(' - Generating PHPCR serializer files');
foreach ($names as $name) {
$destFile = sprintf('%s/Document.%s.xml', $bundleMetadata->getPhpcrMetadata()->getExtendedSerializerDirectory(), $name);
$this->writeSerializerFile($output, $bundleMetadata, $this->documentSerializerTemplate, $destFile, $name);
}
}
}
/**
* @param OutputInterface $output
* @param BundleMetadata $bundleMetadata
* @param string $template
* @param string $destFile
* @param string $name
*/
protected function writeSerializerFile(OutputInterface $output, BundleMetadata $bundleMetadata, $template, $destFile, $name)
{
if (is_file($destFile)) {
$output->writeln(sprintf(' ~ <info>%s</info>', $name));
} else {
$output->writeln(sprintf(' + <info>%s</info>', $name));
$string = Mustache::replace($template, array(
'name' => $name,
'namespace' => $bundleMetadata->getExtendedNamespace(),
'root_name' => strtolower(preg_replace('/[A-Z]/', '_\\0', $name)),
));
file_put_contents($destFile, $string);
}
}
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2010-2016 Thomas Rabaix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,22 @@
# DO NOT EDIT THIS FILE!
#
# It's auto-generated by sonata-project/dev-kit package.
.PHONY: test docs
all:
@echo "Please choose a task."
lint:
composer validate
find . -name '*.yml' -not -path './vendor/*' -not -path './Resources/public/vendor/*' | xargs yaml-lint
find . \( -name '*.xml' -or -name '*.xliff' \) \
-not -path './vendor/*' -not -path './Resources/public/vendor/*' -type f \
-exec xmllint --encode UTF-8 --output '{}' --format '{}' \;
git diff --exit-code
test:
phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
docs:
cd Resources/doc && sphinx-build -W -b html -d _build/doctrees . _build/html

View File

@@ -0,0 +1,252 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Mapper;
class DoctrineCollector
{
/**
* @var array
*/
protected $associations;
/**
* @var array
*/
protected $indexes;
/**
* @var array
*/
protected $uniques;
/**
* @var array
*/
protected $discriminators;
/**
* @var array
*/
protected $discriminatorColumns;
/**
* @var array
*/
protected $inheritanceTypes;
/**
* @var array
*/
protected $overrides;
/**
* @var DoctrineCollector
*/
private static $instance;
public function __construct()
{
$this->associations = array();
$this->indexes = array();
$this->uniques = array();
$this->discriminatorColumns = array();
$this->inheritanceTypes = array();
$this->discriminators = array();
$this->overrides = array();
}
/**
* @return DoctrineCollector
*/
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Add a discriminator to a class.
*
* @param string $class The Class
* @param string $key Key is the database value and values are the classes
* @param string $discriminatorClass The mapped class
*/
public function addDiscriminator($class, $key, $discriminatorClass)
{
if (!isset($this->discriminators[$class])) {
$this->discriminators[$class] = array();
}
if (!isset($this->discriminators[$class][$key])) {
$this->discriminators[$class][$key] = $discriminatorClass;
}
}
/**
* Add the Discriminator Column.
*
* @param string $class
* @param array $columnDef
*/
public function addDiscriminatorColumn($class, array $columnDef)
{
if (!isset($this->discriminatorColumns[$class])) {
$this->discriminatorColumns[$class] = $columnDef;
}
}
/**
* @param string $class
* @param string $type
*/
public function addInheritanceType($class, $type)
{
if (!isset($this->inheritanceTypes[$class])) {
$this->inheritanceTypes[$class] = $type;
}
}
/**
* @param string $class
* @param string $type
* @param array $options
*/
public function addAssociation($class, $type, array $options)
{
if (!isset($this->associations[$class])) {
$this->associations[$class] = array();
}
if (!isset($this->associations[$class][$type])) {
$this->associations[$class][$type] = array();
}
$this->associations[$class][$type][] = $options;
}
/**
* @param string $class
* @param string $name
* @param array $columns
*/
public function addIndex($class, $name, array $columns)
{
if (!isset($this->indexes[$class])) {
$this->indexes[$class] = array();
}
if (isset($this->indexes[$class][$name])) {
return;
}
$this->indexes[$class][$name] = $columns;
}
/**
* @param string $class
* @param string $name
* @param array $columns
*/
public function addUnique($class, $name, array $columns)
{
if (!isset($this->indexes[$class])) {
$this->uniques[$class] = array();
}
if (isset($this->uniques[$class][$name])) {
return;
}
$this->uniques[$class][$name] = $columns;
}
/**
* Adds new override.
*
* @param string $class
* @param string $type
* @param array $options
*/
final public function addOverride($class, $type, array $options)
{
if (!isset($this->overrides[$class])) {
$this->overrides[$class] = array();
}
if (!isset($this->overrides[$class][$type])) {
$this->overrides[$class][$type] = array();
}
$this->overrides[$class][$type][] = $options;
}
/**
* @return array
*/
public function getAssociations()
{
return $this->associations;
}
/**
* @return array
*/
public function getDiscriminators()
{
return $this->discriminators;
}
/**
* @return array
*/
public function getDiscriminatorColumns()
{
return $this->discriminatorColumns;
}
/**
* @return array
*/
public function getInheritanceTypes()
{
return $this->inheritanceTypes;
}
/**
* @return array
*/
public function getIndexes()
{
return $this->indexes;
}
/**
* @return array
*/
public function getUniques()
{
return $this->uniques;
}
/**
* Get all overrides.
*
* @return array
*/
final public function getOverrides()
{
return $this->overrides;
}
}

View File

@@ -0,0 +1,360 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle\Mapper;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
class DoctrineORMMapper implements EventSubscriber
{
/**
* @var array
*/
protected $associations;
/**
* @var array
*/
protected $discriminators;
/**
* @var array
*/
protected $discriminatorColumns;
/**
* @var array
*/
protected $inheritanceTypes;
/**
* @var ManagerRegistry
*/
protected $doctrine;
/**
* @var array
*/
protected $indexes;
/**
* @var array
*/
protected $uniques;
/**
* @var array
*/
protected $overrides;
/**
* @param ManagerRegistry $doctrine
* @param array $associations
* @param array $indexes
* @param array $discriminators
* @param array $discriminatorColumns
* @param array $inheritanceTypes
* @param array $uniques
* @param array $overrides
*/
public function __construct(ManagerRegistry $doctrine, array $associations = array(), array $indexes = array(), array $discriminators = array(), array $discriminatorColumns = array(), array $inheritanceTypes = array(), array $uniques = array(), array $overrides = array())
{
$this->doctrine = $doctrine;
$this->associations = $associations;
$this->indexes = $indexes;
$this->uniques = $uniques;
$this->discriminatorColumns = $discriminatorColumns;
$this->discriminators = $discriminators;
$this->inheritanceTypes = $inheritanceTypes;
$this->overrides = $overrides;
}
/**
* @return array
*/
public function getSubscribedEvents()
{
return array(
'loadClassMetadata',
);
}
/**
* @param string $class
* @param string $field
* @param array $options
*/
public function addAssociation($class, $field, array $options)
{
if (!isset($this->associations[$class])) {
$this->associations[$class] = array();
}
$this->associations[$class][$field] = $options;
}
/**
* Add a discriminator to a class.
*
* @param string $class The Class
* @param string $key Key is the database value and values are the classes
* @param string $discriminatorClass The mapped class
*/
public function addDiscriminator($class, $key, $discriminatorClass)
{
if (!isset($this->discriminators[$class])) {
$this->discriminators[$class] = array();
}
if (!isset($this->discriminators[$class][$key])) {
$this->discriminators[$class][$key] = $discriminatorClass;
}
}
/**
* @param string $class
* @param array $columnDef
*/
public function addDiscriminatorColumn($class, array $columnDef)
{
if (!isset($this->discriminatorColumns[$class])) {
$this->discriminatorColumns[$class] = $columnDef;
}
}
/**
* @param string $class
* @param string $type
*/
public function addInheritanceType($class, $type)
{
if (!isset($this->inheritanceTypes[$class])) {
$this->inheritanceTypes[$class] = $type;
}
}
/**
* @param string $class
* @param string $name
* @param array $columns
*/
public function addIndex($class, $name, array $columns)
{
if (!isset($this->indexes[$class])) {
$this->indexes[$class] = array();
}
if (isset($this->indexes[$class][$name])) {
return;
}
$this->indexes[$class][$name] = $columns;
}
/**
* @param string $class
* @param string $name
* @param array $columns
*/
public function addUnique($class, $name, array $columns)
{
if (!isset($this->uniques[$class])) {
$this->uniques[$class] = array();
}
if (isset($this->uniques[$class][$name])) {
return;
}
$this->uniques[$class][$name] = $columns;
}
/**
* Adds new ORM override.
*
* @param string $class
* @param string $type
* @param array $options
*/
final public function addOverride($class, $type, array $options)
{
if (!isset($this->overrides[$class])) {
$this->overrides[$class] = array();
}
$this->overrides[$class][$type] = $options;
}
/**
* @param $eventArgs
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$metadata = $eventArgs->getClassMetadata();
$this->loadAssociations($metadata);
$this->loadIndexes($metadata);
$this->loadUniques($metadata);
$this->loadDiscriminatorColumns($metadata);
$this->loadDiscriminators($metadata);
$this->loadInheritanceTypes($metadata);
$this->loadOverrides($metadata);
}
/**
* @param ClassMetadataInfo $metadata
*
* @throws \RuntimeException
*/
private function loadAssociations(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->associations)) {
return;
}
try {
foreach ($this->associations[$metadata->name] as $type => $mappings) {
foreach ($mappings as $mapping) {
// the association is already set, skip the native one
if ($metadata->hasAssociation($mapping['fieldName'])) {
continue;
}
call_user_func(array($metadata, $type), $mapping);
}
}
} catch (\ReflectionException $e) {
throw new \RuntimeException(sprintf('Error with class %s : %s', $metadata->name, $e->getMessage()), 404, $e);
}
}
/**
* @param ClassMetadataInfo $metadata
*
* @throws \RuntimeException
*/
private function loadDiscriminatorColumns(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->discriminatorColumns)) {
return;
}
try {
if (isset($this->discriminatorColumns[$metadata->name])) {
$arrayDiscriminatorColumns = $this->discriminatorColumns[$metadata->name];
if (isset($metadata->discriminatorColumn)) {
$arrayDiscriminatorColumns = array_merge($metadata->discriminatorColumn, $this->discriminatorColumns[$metadata->name]);
}
$metadata->setDiscriminatorColumn($arrayDiscriminatorColumns);
}
} catch (\ReflectionException $e) {
throw new \RuntimeException(sprintf('Error with class %s : %s', $metadata->name, $e->getMessage()), 404, $e);
}
}
/**
* @param ClassMetadataInfo $metadata
*
* @throws \RuntimeException
*/
private function loadInheritanceTypes(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->inheritanceTypes)) {
return;
}
try {
if (isset($this->inheritanceTypes[$metadata->name])) {
$metadata->setInheritanceType($this->inheritanceTypes[$metadata->name]);
}
} catch (\ReflectionException $e) {
throw new \RuntimeException(sprintf('Error with class %s : %s', $metadata->name, $e->getMessage()), 404, $e);
}
}
/**
* @param ClassMetadataInfo $metadata
*
* @throws \RuntimeException
*/
private function loadDiscriminators(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->discriminators)) {
return;
}
try {
foreach ($this->discriminators[$metadata->name] as $key => $class) {
if (in_array($key, $metadata->discriminatorMap)) {
continue;
}
$metadata->setDiscriminatorMap(array($key => $class));
}
} catch (\ReflectionException $e) {
throw new \RuntimeException(sprintf('Error with class %s : %s', $metadata->name, $e->getMessage()), 404, $e);
}
}
/**
* @param ClassMetadataInfo $metadata
*/
private function loadIndexes(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->indexes)) {
return;
}
foreach ($this->indexes[$metadata->name] as $name => $columns) {
$metadata->table['indexes'][$name] = array('columns' => $columns);
}
}
/**
* @param ClassMetadataInfo $metadata
*/
private function loadUniques(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->uniques)) {
return;
}
foreach ($this->uniques[$metadata->name] as $name => $columns) {
$metadata->table['uniqueConstraints'][$name] = array('columns' => $columns);
}
}
/**
* @param ClassMetadataInfo $metadata
*
* @throws \RuntimeException
*/
private function loadOverrides(ClassMetadataInfo $metadata)
{
if (!array_key_exists($metadata->name, $this->overrides)) {
return;
}
try {
foreach ($this->overrides[$metadata->name] as $type => $overrides) {
foreach ($overrides as $override) {
call_user_func(array($metadata, $type), $override['fieldName'], $override);
}
}
} catch (\ReflectionException $e) {
throw new \RuntimeException(
sprintf('Error with class %s : %s', $metadata->name, $e->getMessage()), 404, $e
);
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.easy_extends.generator.bundle" class="Sonata\EasyExtendsBundle\Generator\BundleGenerator"/>
<service id="sonata.easy_extends.generator.orm" class="Sonata\EasyExtendsBundle\Generator\OrmGenerator"/>
<service id="sonata.easy_extends.generator.odm" class="Sonata\EasyExtendsBundle\Generator\OdmGenerator"/>
<service id="sonata.easy_extends.generator.phpcr" class="Sonata\EasyExtendsBundle\Generator\PHPCRGenerator"/>
<service id="sonata.easy_extends.generator.serializer" class="Sonata\EasyExtendsBundle\Generator\SerializerGenerator"/>
</services>
</container>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.easy_extends.doctrine.mapper" class="Sonata\EasyExtendsBundle\Mapper\DoctrineORMMapper">
<tag name="doctrine.event_subscriber"/>
<argument type="service" id="doctrine"/>
<argument type="collection"/>
</service>
</services>
</container>

View File

@@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IoC.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IoC.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/IoC"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/IoC"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View File

@@ -0,0 +1,243 @@
# -*- coding: utf-8 -*-
#
# IoC documentation build configuration file, created by
# sphinx-quickstart on Fri Mar 29 01:43:00 2013.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Sonata ~ AdminBundle'
copyright = u'2010-2015, Thomas Rabaix'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
#version = '0.0.1'
# The full version, including alpha/beta/rc tags.
#release = '0.0.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
import sphinx_rtd_theme
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'doc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
#latex_documents = [
# ('index', 'PythonElement.tex', u'Python Documentation',
# u'Thomas Rabaix', 'manual'),
#]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
#(source start file, name, description, authors, manual section).
#man_pages = [
# ('index', 'ioc', u'IoC Documentation',
# [u'Thomas Rabaix'], 1)
#]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
#texinfo_documents = [
# ('index', 'IoC', u'IoC Documentation',
# u'Thomas Rabaix', 'IoC', 'One line description of project.',
# 'Miscellaneous'),
#]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'

View File

@@ -0,0 +1,13 @@
EasyExtends Bundle
==================
.. toctree::
:caption: Reference Guide
:name: reference-guide
:maxdepth: 1
:numbered:
reference/introduction
reference/installation
reference/why

View File

@@ -0,0 +1,28 @@
.. index::
double: Reference; Installation
Installation
============
To begin, add the dependent bundle:
.. code-block:: bash
php composer.phar require sonata-project/easy-extends-bundle
Next, be sure to enable the new bundle in your application kernel:
.. code-block:: php
<?php
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),
// ...
);
}

View File

@@ -0,0 +1,22 @@
.. index::
double: Reference; Introduction
single: Command line
Introduction
============
``SonataEasyExtendsBundle`` is a prototype for generating a valid bundle structure from a `Vendor` Bundle.
The tool is started with the simple command line: ``sonata:easy-extends:generate``.
The command line will generate:
* all required directories for one bundle (controller, config, doctrine, views, ...),
* the mapping and entity files from those defined in the CPB. The `SuperClass` must be prefixed by ``BaseXXXXXX``,
* the table name from the bundle name + entity name. For instance, ``blog__post``, where blog is the BlogBundle and Post the entity name.
You can optionally define a ``--dest`` option to the command with the target directory for the extended bundle creation.
By default, this is set to ``app`` but you should probably set it to ``src``.
You can optionally define a ``--namespace`` option to the command with the namespace for the extended bundle classes and directory structure.
A special placeholder ``:vendor`` could be used and will be substitued with the bundle's `Vendor`.
By default, this is set to ``Application\:vendor``.

View File

@@ -0,0 +1,108 @@
.. index::
double: Reference; Installation
single: Doctrine
single: Tour
Make your Symfony2 bundle extendable
====================================
.. note::
This post is not part of the Symfony2 documentation; it is just a state about how things work now (end of 2010)
between Doctrine2 and Symfony2. It is not a complaint about the architecture, it just exposes how I solve a
recurrent problem I have.
Lets have a quick Symfony2 and Doctrine tour
---------------------------------------------
A quick Doctrine tour:
* `Doctrine2` entities are plain PHP objects; there is no database layer information. An ``Comment::post_id`` property is part of the database layer and not part of the domain layer. So a ``comment`` entity will have a ``post`` property and not a ``post_id`` property.
* `Doctrine2` entities are mapped through mapping information: `yaml`, `xml`, `annotation` or php code. There is one mapping per class, so if `Blog` extends `SuperBlog`, which extends `SuperEntity`, you will have 3 classes and so 3 information mappings and you will be able to use 3 different tables to save these entities,
* A mapped entity is final (from Doctrine2 point of view), it cannot be extended unless you create a new mapping definition for the new child class,
* Each entity is linked to a `ClassMetadata` information which contains all the mapping information and the final class name,
* An entity can extend a `SuperClass`. A `SuperClass` is just a mapping definition, a `SuperClass` cannot be persisted.
A quick `Symfony2` bundle tour:
* There are two types of bundles:
* Application Bundle (AB),
* Vendor Bundle (VB), that should not be modified inside a project.
* The AB directory is where developers implement the project requirements,
* An AB can overwrite almost everything from a VB, example: you can redefine a VB template at the AB level.
A namespace tour:
* “a namespace is an abstract container providing context for the items” (`Source <http://en.wikipedia.org/wiki/Namespace>`_),
* An entity is defined by a namespace,
* A bundle is defined by a namespace,
* A VB and AB are defined with two different namespaces.
Lets start to mix these points together
----------------------------------------
* If an AB bundle A wants to use an entity from a VB bundle B, the fully qualify namespace must be used,
* If a developer wants to add a new property into a VB entity, the developer needs to create a new child entity with a custom mapping.
At this point, you have 2 entities with 2 different namespaces. The VB bundles code refers to its own namespace to
instantiate the model, BUT ... how ... you just create a new entity. Your VB will be unable to use this new model ... too bad.
Can this problem be solved with the Alternate syntax?
-----------------------------------------------------
There is actually a start of a solution, the ``DoctrineBundle`` allows us to use an alternate syntax, ie (``BlogBundle:Blog`` instead of ``Bundle\BlogBundle\Entity\Blog``).
As you can guess this syntax only works for string, inside a query for instance.
So if you want to instantiate a new model, you need first to get the `ClassMetadata` instance, retrieve the class
name and create the model. Its not really nice and creates a dependency to the class metadata.
Last issue, the entitys mapping association required fully qualifies namespace: no alternate syntax. (I suppose,
this last point can be fixed).
At this point, we are stuck with no solution to fully extend a bundle. (Dont take this for granted; this might
change in a near future, as Symfony2 is not complete yet)
A pragmatic way to solve this issue
-----------------------------------
The easiest way to solve this problem is to use global namespace inside your VB, the global namespace is the only
namespace allowed ``Application\YourBundle\Entity``.
So, inside your mapping definition or inside your VB code, you will use one final namespace: ``problem solved``.
How to achieve this:
* Declare only SuperClass inside a VB, dont use final entity,
* Call your entity ``BaseXXXX`` and make it abstract, change the properties from private to protected,
* The same goes for a repository,
* Always use ``Application\YourBundle\Entity\XXXX`` inside your code.
Of course, you need to create for each VB bundle:
* a valid structure inside the Application directory,
* a valid entity mapping definition,
* a model inside the entity folder.
The last part is quite inefficient without an efficient tool to generate for you this structure: ``EasyExtendsBundle`` to the rescue.
How to make your bundle easy extendable?
----------------------------------------
Mainly all you need is to follow instructions in previous paragraph:
* Declare you entity/repository as described above,
* Use your entity/repository as described above,
* Before generation you also need "skeleton" file that will describe AB entity. Skeleton file can either `xml` or `yml`. For fully working example see ``SonataMediaBundle``.
At last you can run:
.. code-block:: bash
php app/console sonata:easy-extends:generate YourVBBundleName
.. note::
Note that the `--dest` option allows you to choose the target directory, such as `src`. Default destination is `app/`.
Also note that the `--namespace` option allows you to choose the base namespace, such as `Application\\Sonata`. Default destination is `Application\\:vendor`.

View File

@@ -0,0 +1,3 @@
Sphinx
git+https://github.com/fabpot/sphinx-php.git
sphinx_rtd_theme

View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010-2013 thomas.rabaix@sonata-project.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,32 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ namespace }};
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/easy-extends )
*
* References :
* bundles : http://symfony.com/doc/current/book/bundles.html
*
* @author <yourname> <youremail>
*/
class {{ application }}{{ bundle }} extends Bundle
{
/**
* {@inheritdoc}
*/
public function getParent()
{
return '{{ bundle }}';
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\Document;
use {{ namespace }}\Document\{{ name }} as {{ extended_name }};
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
*
* References :
* working with object : http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/reference/working-with-objects.html
*
* @author <yourname> <youremail>
*/
class {{ class }} extends {{ extended_name }}
{
/**
* @var integer $id
*/
protected $id;
/**
* Get id
*
* @return integer $id
*/
public function getId()
{
return $this->id;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\Document;
use {{ namespace}}\Document\Base{{ name }}Repository;
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
*
* References :
* query builder : http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/reference/query-builder-api.html
*
* @author <yourname> <youremail>
*/
class {{ name }}Repository extends Base{{ name }}Repository
{
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\Entity;
use {{ namespace }}\Entity\{{ name }} as {{ extended_name }};
/**
* This file has been generated by the Sonata EasyExtends bundle.
*
* @link https://sonata-project.org/bundles/easy-extends
*
* References :
* working with object : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-objects/en
*
* @author <yourname> <youremail>
*/
class {{ class }} extends {{ extended_name }}
{
/**
* @var int $id
*/
protected $id;
/**
* Get id
*
* @return int $id
*/
public function getId()
{
return $this->id;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\Entity;
use {{ namespace}}\Entity\Base{{ name }}Repository;
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
*
* References :
* custom repository : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-objects/en#querying:custom-repositories
* query builder : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/query-builder/en
* dql : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/dql-doctrine-query-language/en
*
* @author <yourname> <youremail>
*/
class {{ name }}Repository extends Base{{ name }}Repository
{
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\PHPCR;
use {{ namespace }}\PHPCR\{{ name }} as {{ extended_name }};
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
*
* References :
* working with object : http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/index.html
*
* @author <yourname> <youremail>
*/
class {{ class }} extends {{ extended_name }}
{
/**
* @var integer $id
*/
protected $id;
/**
* Get id
*
* @return integer $id
*/
public function getId()
{
return $this->id;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* This file is part of the <name> project.
*
* (c) <yourname> <youremail>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace {{ extended_namespace }}\PHPCR;
use {{ namespace}}\PHPCR\Base{{ name }}Repository;
/**
* This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
*
* References :
* query builder : http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/reference/query-builder-api.html
*
* @author <yourname> <youremail>
*/
class {{ name }}Repository extends Base{{ name }}Repository
{
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<serializer>
<!--
This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
@author <yourname> <youremail>
-->
<class name="{{ namespace }}\Document\{{ name }}" exclusion-policy="all" xml-root-name="{{ root_name }}">
<property xml-attribute-map="true" name="id" type="integer" expose="true" since-version="1.0" groups="sonata_api_read,sonata_api_write,sonata_search" />
</class>
</serializer>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<serializer>
<!--
This file has been generated by the EasyExtends bundle ( https://sonata-project.org/bundles/easy-extends )
@author <yourname> <youremail>
-->
<class name="{{ namespace }}\Entity\{{ name }}" exclusion-policy="all" xml-root-name="{{ root_name }}">
<property xml-attribute-map="true" name="id" type="integer" expose="true" since-version="1.0" groups="sonata_api_read,sonata_api_write,sonata_search" />
</class>
</serializer>

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\EasyExtendsBundle;
use Sonata\EasyExtendsBundle\DependencyInjection\Compiler\AddMapperInformationCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class SonataEasyExtendsBundle extends Bundle
{
/**
* Builds the bundle.
*
* It is only ever called once when the cache is empty.
*
* This method can be overridden to register compilation passes,
* other extensions, ...
*
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new AddMapperInformationCompilerPass());
}
}

View File

@@ -0,0 +1,44 @@
{
"name": "sonata-project/easy-extends-bundle",
"type": "symfony-bundle",
"description": "Symfony SonataEasyExtendsBundle",
"keywords": ["sonata", "easy extends"],
"homepage": "https://sonata-project.org/bundles/easy-extends",
"license": "MIT",
"authors": [
{
"name": "Thomas Rabaix",
"email": "thomas.rabaix@sonata-project.org",
"homepage": "https://sonata-project.org"
},
{
"name": "Sonata Community",
"homepage": "https://github.com/sonata-project/SonataEasyExtendsBundle/contributors"
}
],
"require": {
"php": "^5.3.2 || ^7.0",
"symfony/framework-bundle": "^2.1 || ^3.0",
"symfony/console": "^2.1 || ^3.0",
"symfony/finder": "^2.1 || ^3.0"
},
"require-dev": {
"doctrine/orm": "^2.2",
"symfony/phpunit-bridge": "^2.7 || ^3.0",
"sllh/php-cs-fixer-styleci-bridge": "^2.0"
},
"autoload": {
"psr-4": { "Sonata\\EasyExtendsBundle\\": "" },
"exclude-from-classmap": [
"Tests/"
]
},
"autoload-dev": {
"psr-4": { "Sonata\\EasyExtendsBundle\\Tests\\": "Tests/" }
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
}
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="Tests/tests/bootstrap.php"
>
<testsuites>
<testsuite name="EasyExtends Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Tests/</directory>
<directory>./DataFixtures/</directory>
<directory>./Resources/</directory>
<directory>./DependencyInjection/</directory>
<directory>./vendor/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>