Initial commit with Symfony 2.1+Vendors

Signed-off-by: Gergely POLONKAI (W00d5t0ck) <polesz@w00d5t0ck.info>
This commit is contained in:
Polonkai Gergely
2012-07-01 09:52:20 +02:00
commit 082a0130c2
5381 changed files with 416709 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Composer;
use Symfony\Component\ClassLoader\ClassCollectionLoader;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\PhpExecutableFinder;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ScriptHandler
{
public static function buildBootstrap($event)
{
$options = self::getOptions($event);
$appDir = $options['symfony-app-dir'];
if (!is_dir($appDir)) {
echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not build bootstrap file.'.PHP_EOL;
return;
}
static::executeBuildBootstrap($appDir);
}
public static function clearCache($event)
{
$options = self::getOptions($event);
$appDir = $options['symfony-app-dir'];
if (!is_dir($appDir)) {
echo 'The symfony-app-dir ('.$appDir.') specified in composer.json was not found in '.getcwd().', can not clear the cache.'.PHP_EOL;
return;
}
static::executeCommand($event, $appDir, 'cache:clear --no-warmup');
}
public static function installAssets($event)
{
$options = self::getOptions($event);
$appDir = $options['symfony-app-dir'];
$webDir = $options['symfony-web-dir'];
$symlink = '';
if ($options['symfony-assets-install'] == 'symlink') {
$symlink = '--symlink ';
} elseif ($options['symfony-assets-install'] == 'relative') {
$symlink = '--symlink --relative ';
}
if (!is_dir($webDir)) {
echo 'The symfony-web-dir ('.$webDir.') specified in composer.json was not found in '.getcwd().', can not install assets.'.PHP_EOL;
return;
}
static::executeCommand($event, $appDir, 'assets:install '.$symlink.escapeshellarg($webDir));
}
public static function doBuildBootstrap($appDir)
{
$file = $appDir.'/bootstrap.php.cache';
if (file_exists($file)) {
unlink($file);
}
ClassCollectionLoader::load(array(
'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface',
// Cannot be included because annotations will parse the big compiled class file
//'Symfony\\Component\\DependencyInjection\\ContainerAware',
'Symfony\\Component\\DependencyInjection\\ContainerInterface',
'Symfony\\Component\\DependencyInjection\\Container',
'Symfony\\Component\\HttpKernel\\HttpKernelInterface',
'Symfony\\Component\\HttpKernel\\KernelInterface',
'Symfony\\Component\\HttpKernel\\Kernel',
'Symfony\\Component\\ClassLoader\\ClassCollectionLoader',
'Symfony\\Component\\ClassLoader\\ApcClassLoader',
'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface',
'Symfony\\Component\\HttpKernel\\Bundle\\Bundle',
'Symfony\\Component\\Config\\ConfigCache',
// cannot be included as commands are discovered based on the path to this class via Reflection
//'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
), dirname($file), basename($file, '.php.cache'), false, false, '.php.cache');
file_put_contents($file, sprintf("<?php
namespace { \$loader = require_once __DIR__.'/autoload.php'; }
%s
namespace { return \$loader; }
", substr(file_get_contents($file), 5)));
}
protected static function executeCommand($event, $appDir, $cmd)
{
$phpFinder = new PhpExecutableFinder;
$php = escapeshellarg($phpFinder->find());
$console = escapeshellarg($appDir.'/console');
if ($event->getIO()->isDecorated()) {
$console.= ' --ansi';
}
$process = new Process($php.' '.$console.' '.$cmd);
$process->run(function ($type, $buffer) { echo $buffer; });
}
protected static function executeBuildBootstrap($appDir)
{
$phpFinder = new PhpExecutableFinder;
$php = escapeshellarg($phpFinder->find());
$cmd = escapeshellarg(__DIR__.'/../Resources/bin/build_bootstrap.php');
$appDir = escapeshellarg($appDir);
$process = new Process($php.' '.$cmd.' '.$appDir);
$process->run(function ($type, $buffer) { echo $buffer; });
}
protected static function getOptions($event)
{
$options = array_merge(array(
'symfony-app-dir' => 'app',
'symfony-web-dir' => 'web',
'symfony-assets-install' => 'hard'
), $event->getComposer()->getPackage()->getExtra());
$options['symfony-assets-install'] = getenv('SYMFONY_ASSETS_INSTALL') ?: $options['symfony-assets-install'];
return $options;
}
}

View File

@@ -0,0 +1,186 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator;
use Sensio\Bundle\DistributionBundle\Configurator\Step\StepInterface;
use Symfony\Component\Yaml\Yaml;
/**
* Configurator.
*
* @author Marc Weistroff <marc.weistroff@gmail.com>
*/
class Configurator
{
protected $filename;
protected $steps;
protected $parameters;
public function __construct($kernelDir)
{
$this->kernelDir = $kernelDir;
$this->filename = $kernelDir.'/config/parameters.yml';
$this->steps = array();
$this->parameters = $this->read();
}
public function isFileWritable()
{
return is_writable($this->filename);
}
public function clean()
{
if (file_exists($this->getCacheFilename())) {
@unlink($this->getCacheFilename());
}
}
/**
* @param StepInterface $step
*/
public function addStep(StepInterface $step)
{
$this->steps[] = $step;
}
/**
* @param integer $index
*
* @return StepInterface
*/
public function getStep($index)
{
if (isset($this->steps[$index])) {
return $this->steps[$index];
}
}
/**
* @return array
*/
public function getSteps()
{
return $this->steps;
}
/**
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* @return integer
*/
public function getStepCount()
{
return count($this->steps);
}
/**
* @param array $parameters
*/
public function mergeParameters($parameters)
{
$this->parameters = array_merge($this->parameters, $parameters);
}
/**
* @return array
*/
public function getRequirements()
{
$majors = array();
foreach ($this->steps as $step) {
foreach ($step->checkRequirements() as $major) {
$majors[] = $major;
}
}
return $majors;
}
/**
* @return array
*/
public function getOptionalSettings()
{
$minors = array();
foreach ($this->steps as $step) {
foreach ($step->checkOptionalSettings() as $minor) {
$minors[] = $minor;
}
}
return $minors;
}
/**
* Renders parameters as a string.
*
* @return string
*/
public function render()
{
return Yaml::dump(array('parameters' => $this->parameters));
}
/**
* Writes parameters to parameters.yml or temporary in the cache directory.
*
* @return boolean
*/
public function write()
{
$filename = $this->isFileWritable() ? $this->filename : $this->getCacheFilename();
return file_put_contents($filename, $this->render());
}
/**
* Reads parameters from file.
*
* @return array
*/
protected function read()
{
$filename = $this->filename;
if (!$this->isFileWritable() && file_exists($this->getCacheFilename())) {
$filename = $this->getCacheFilename();
}
$ret = Yaml::parse($filename);
if (false === $ret || array() === $ret) {
throw new \InvalidArgumentException(sprintf('The %s file is not valid.', $filename));
}
if (isset($ret['parameters']) && is_array($ret['parameters'])) {
return $ret['parameters'];
} else {
return array();
}
}
/**
* getCacheFilename
*
* @return string
*/
protected function getCacheFilename()
{
return $this->kernelDir.'/cache/parameters.yml';
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator\Form;
use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Doctrine Form Type.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineStepType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('driver', 'choice', array('choices' => DoctrineStep::getDrivers()))
->add('name', 'text')
->add('host', 'text')
->add('port', 'text', array('required' => false))
->add('user', 'text')
->add('password', 'repeated', array(
'required' => false,
'type' => 'password',
'first_name' => 'password',
'second_name' => 'password_again',
'invalid_message' => 'The password fields must match.',
))
;
}
public function getName()
{
return 'distributionbundle_doctrine_step';
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Secret Form Type.
*
* @author Marc Weistroff <marc.weistroff@sensio.com>
*/
class SecretStepType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('secret', 'text');
}
public function getName()
{
return 'distributionbundle_secret_step';
}
}

View File

@@ -0,0 +1,143 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator\Step;
use Sensio\Bundle\DistributionBundle\Configurator\Form\DoctrineStepType;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Doctrine Step.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineStep implements StepInterface
{
/**
* @Assert\Choice(callback="getDriverKeys")
*/
public $driver;
/**
* @Assert\NotBlank
*/
public $host;
/**
* @Assert\Min(0)
*/
public $port;
/**
* @Assert\NotBlank
*/
public $name;
/**
* @Assert\NotBlank
*/
public $user;
public $password;
public function __construct(array $parameters)
{
foreach ($parameters as $key => $value) {
if (0 === strpos($key, 'database_')) {
$parameters[substr($key, 9)] = $value;
$key = substr($key, 9);
$this->$key = $value;
}
}
}
/**
* @see StepInterface
*/
public function getFormType()
{
return new DoctrineStepType();
}
/**
* @see StepInterface
*/
public function checkRequirements()
{
$messages = array();
if (!class_exists('\PDO')) {
$messages[] = 'PDO extension is mandatory.';
} else {
$drivers = \PDO::getAvailableDrivers();
if (0 == count($drivers)) {
$messages[] = 'Please install PDO drivers.';
}
}
return $messages;
}
/**
* @see StepInterface
*/
public function checkOptionalSettings()
{
return array();
}
/**
* @see StepInterface
*/
public function update(StepInterface $data)
{
$parameters = array();
foreach ($data as $key => $value) {
$parameters['database_'.$key] = $value;
}
return $parameters;
}
/**
* @see StepInterface
*/
public function getTemplate()
{
return 'SensioDistributionBundle:Configurator/Step:doctrine.html.twig';
}
/**
* @return array
*/
static public function getDriverKeys()
{
return array_keys(static::getDrivers());
}
/**
* @return array
*/
static public function getDrivers()
{
return array(
'pdo_mysql' => 'MySQL (PDO)',
'pdo_sqlite' => 'SQLite (PDO)',
'pdo_pgsql' => 'PosgreSQL (PDO)',
'oci8' => 'Oracle (native)',
'ibm_db2' => 'IBM DB2 (native)',
'pdo_oci' => 'Oracle (PDO)',
'pdo_ibm' => 'IBM DB2 (PDO)',
'pdo_sqlsrv' => 'SQLServer (PDO)',
);
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator\Step;
use Sensio\Bundle\DistributionBundle\Configurator\Form\SecretStepType;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Secret Step.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SecretStep implements StepInterface
{
/**
* @Assert\NotBlank
*/
public $secret;
public function __construct(array $parameters)
{
if (array_key_exists('secret', $parameters)){
$this->secret = $parameters['secret'];
if ('ThisTokenIsNotSoSecretChangeIt' == $this->secret) {
$this->secret = $this->generateRandomSecret();
}
} else {
$this->secret = $this->generateRandomSecret();
}
}
private function generateRandomSecret()
{
return hash('sha1', uniqid(mt_rand()));
}
/**
* @see StepInterface
*/
public function getFormType()
{
return new SecretStepType();
}
/**
* @see StepInterface
*/
public function checkRequirements()
{
return array();
}
/**
* checkOptionalSettings
*/
public function checkOptionalSettings()
{
return array();
}
/**
* @see StepInterface
*/
public function update(StepInterface $data)
{
return array('secret' => $data->secret);
}
/**
* @see StepInterface
*/
public function getTemplate()
{
return 'SensioDistributionBundle:Configurator/Step:secret.html.twig';
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Configurator\Step;
use Symfony\Component\Form\Type\FormTypeInterface;
/**
* StepInterface.
*
* @author Marc Weistroff <marc.weistroff@sensio.com>
*/
interface StepInterface
{
/**
* __construct
*
* @param array $parameters
*/
function __construct(array $parameters);
/**
* Returns the form used for configuration.
*
* @return FormTypeInterface
*/
function getFormType();
/**
* Checks for requirements.
*
* @return array
*/
function checkRequirements();
/**
* Checks for optional setting it could be nice to have.
*
* @return array
*/
function checkOptionalSettings();
/**
* Returns the template to be renderer for this step.
*
* @return string
*/
function getTemplate();
/**
* Updates form data parameters.
*
* @param array $parameters
* @return array
*/
function update(StepInterface $data);
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* ConfiguratorController.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConfiguratorController extends ContainerAware
{
/**
* @return Response A Response instance
*/
public function stepAction($index = 0)
{
$configurator = $this->container->get('sensio.distribution.webconfigurator');
$step = $configurator->getStep($index);
$form = $this->container->get('form.factory')->create($step->getFormType(), $step);
$request = $this->container->get('request');
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$configurator->mergeParameters($step->update($form->getData()));
$configurator->write();
$index++;
if ($index < $configurator->getStepCount()) {
return new RedirectResponse($this->container->get('router')->generate('_configurator_step', array('index' => $index)));
}
return new RedirectResponse($this->container->get('router')->generate('_configurator_final'));
}
}
return $this->container->get('templating')->renderResponse($step->getTemplate(), array(
'form' => $form->createView(),
'index' => $index,
'count' => $configurator->getStepCount(),
'version' => $this->getVersion(),
));
}
public function checkAction()
{
$configurator = $this->container->get('sensio.distribution.webconfigurator');
// Trying to get as much requirements as possible
$majors = $configurator->getRequirements();
$minors = $configurator->getOptionalSettings();
$url = $this->container->get('router')->generate('_configurator_step', array('index' => 0));
if (empty($majors) && empty($minors)) {
return new RedirectResponse($url);
}
return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/check.html.twig', array(
'majors' => $majors,
'minors' => $minors,
'url' => $url,
'version' => $this->getVersion(),
));
}
public function finalAction()
{
$configurator = $this->container->get('sensio.distribution.webconfigurator');
$configurator->clean();
try {
$welcomeUrl = $this->container->get('router')->generate('_welcome');
} catch (\Exception $e) {
$welcomeUrl = null;
}
return $this->container->get('templating')->renderResponse('SensioDistributionBundle::Configurator/final.html.twig', array(
'welcome_url' => $welcomeUrl,
'parameters' => $configurator->render(),
'yml_path' => $this->container->getParameter('kernel.root_dir').'/config/parameters.yml',
'is_writable' => $configurator->isFileWritable(),
'version' => $this->getVersion(),
));
}
public function getVersion()
{
$kernel = $this->container->get('kernel');
return $kernel::VERSION;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Sensio\Bundle\DistributionBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
/**
* SensioDistributionExtension.
*
* @author Marc Weistroff <marc.weistroff@sensio.com>
*/
class SensioDistributionExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('webconfigurator.xml');
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/symfony/sensiodistribution';
}
public function getAlias()
{
return 'sensio_distribution';
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2010,2011 Fabien Potencier
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,102 @@
#!/bin/sh
# This file is part of the Symfony Standard Edition.
#
# (c) Fabien Potencier <fabien@symfony.com>
#
# For the full copyright and license information, please view the LICENSE
# file that was distributed with this source code.
DIR=`php -r "echo realpath(dirname(\\$_SERVER['argv'][0]));"`
cd $DIR
VERSION=`grep ' VERSION ' vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php | sed -E "s/.*'(.+)'.*/\1/g"`
if [ ! -d "$DIR/build" ]; then
mkdir -p $DIR/build
fi
# Without vendors
rm -rf /tmp/Symfony
mkdir /tmp/Symfony
cp -r app /tmp/Symfony/
cp -r src /tmp/Symfony/
cp -r web /tmp/Symfony/
cp README.md /tmp/Symfony/
cp LICENSE /tmp/Symfony/
cp composer.json /tmp/Symfony/
cp composer.lock /tmp/Symfony/
cd /tmp/Symfony
sudo rm -rf app/cache/* app/logs/* .git*
chmod 777 app/cache app/logs
# DS_Store cleanup
find . -name .DS_Store | xargs rm -rf -
cd ..
# avoid the creation of ._* files
export COPY_EXTENDED_ATTRIBUTES_DISABLE=true
export COPYFILE_DISABLE=true
tar zcpf $DIR/build/Symfony_Standard_$VERSION.tgz Symfony
sudo rm -f $DIR/build/Symfony_Standard_$VERSION.zip
zip -rq $DIR/build/Symfony_Standard_$VERSION.zip Symfony
# With vendors
cd $DIR
rm -rf /tmp/vendor
mkdir /tmp/vendor
TARGET=/tmp/vendor
if [ ! -d "$DIR/vendor" ]; then
echo "The master vendor directory does not exist"
exit
fi
cp -r $DIR/vendor/* $TARGET/
# Doctrine ORM
cd $TARGET/doctrine/orm && rm -rf UPGRADE* build* bin tests tools lib/vendor
# Doctrine DBAL
cd $TARGET/doctrine/dbal && rm -rf bin build* tests lib/vendor
# Doctrine Common
cd $TARGET/doctrine/common && rm -rf build* tests lib/vendor
# Swiftmailer
cd $TARGET/swiftmailer/swiftmailer && rm -rf CHANGES README* build* docs notes test-suite tests create_pear_package.php package*
# Symfony
cd $TARGET/symfony/symfony && rm -rf README.md phpunit.xml* tests *.sh vendor
# Twig
cd $TARGET/twig/twig && rm -rf AUTHORS CHANGELOG README.markdown bin doc package.xml.tpl phpunit.xml* test
# Twig Extensions
cd $TARGET/twig/extensions && rm -rf README doc phpunit.xml* test
# Monolog
cd $TARGET/monolog/monolog && rm -rf README.markdown phpunit.xml* tests
# Sensio
cd $TARGET/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle/ && rm -rf phpunit.xml* Tests CHANGELOG*
cd $TARGET/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle/ && rm -rf phpunit.xml* Tests CHANGELOG*
cd $TARGET/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/ && rm -rf phpunit.xml* Tests CHANGELOG*
# JMS
cd $TARGET/jms/metadata && rm -rf README.rst phpunit.xml* tests
cd $TARGET/jms/cg && rm -rf README.rst phpunit.xml* tests
cd $TARGET/jms/aop-bundle/JMS/AopBundle && rm -rf phpunit.xml* Tests
cd $TARGET/jms/di-extra-bundle/JMS/DiExtraBundle && rm -rf phpunit.xml* Tests
cd $TARGET/jms/security-extra-bundle/JMS/SecurityExtraBundle/ && rm -rf phpunit.xml* Tests
# cleanup
find $TARGET -name .git | xargs rm -rf -
find $TARGET -name .gitignore | xargs rm -rf -
find $TARGET -name .gitmodules | xargs rm -rf -
find $TARGET -name .svn | xargs rm -rf -
cd /tmp/
mv /tmp/vendor /tmp/Symfony/
tar zcpf $DIR/build/Symfony_Standard_Vendors_$VERSION.tgz Symfony
sudo rm -f $DIR/build/Symfony_Standard_Vendors_$VERSION.zip
zip -rq $DIR/build/Symfony_Standard_Vendors_$VERSION.zip Symfony

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env php
<?php
/*
* This file is part of the Symfony Standard Edition.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
$argv = $_SERVER['argv'];
// allow the base path to be passed as the first argument, or default
if (isset($argv[1])) {
$appDir = $argv[1];
} else {
if (!$appDir = realpath(__DIR__.'/../../../../../../../app')) {
exit('Looks like you don\'t have a standard layout.');
}
}
require_once $appDir.'/autoload.php';
\Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::doBuildBootstrap($appDir);

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="_configurator_home" pattern="/">
<default key="_controller">SensioDistributionBundle:Configurator:check</default>
</route>
<route id="_configurator_step" pattern="/step/{index}">
<default key="_controller">SensioDistributionBundle:Configurator:step</default>
</route>
<route id="_configurator_final" pattern="/final">
<default key="_controller">SensioDistributionBundle:Configurator:final</default>
</route>
</routes>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio.distribution.webconfigurator.class">Sensio\Bundle\DistributionBundle\Configurator\Configurator</parameter>
</parameters>
<services>
<service id="sensio.distribution.webconfigurator" class="%sensio.distribution.webconfigurator.class%">
<argument>%kernel.root_dir%</argument>
</service>
</services>
</container>

View File

@@ -0,0 +1,153 @@
@import url("install.css");
h1 {
margin-top: 10px;
font-size: 26px;
}
#symfony-wrapper {
padding-top: 0;
}
#symfony-search {
position: absolute;
top: 50px;
right: 30px;
}
#symfony-search-field {
width: 190px;
}
#symfony-search label {
display: block;
float: left;
width: 20px;
height: 25px;
background: url(../images/search.png) no-repeat left 5px;
}
#symfony-search label span {
display: none;
}
input[type=text] {
border: 1px solid #DADADA;
background: white url(../images/field-background.gif) repeat-x left top;
padding: 5px 6px;
color: #565656;
font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
font-size: 12px;
}
.symfony-button-grey, .symfony-button-green {
font-size: 0.85em;
font-weight: bold;
cursor: pointer;
display: inline-block;
outline: none;
text-align: center;
text-transform: uppercase;
padding: 3px 10px;
text-shadow: 0 1px 1px rgba(0,0,0,.3);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.symfony-button-grey {
color: #868686;
font-weight: normal;
padding: 5px 10px;
border: solid 1px #d7d7d7;
background: #ffffff;
background: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#d7d7d7));
background: -moz-linear-gradient(top, #ffffff, #d7d7d7);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#d7d7d7');
}
.symfony-button-green {
padding: 5px 12px;
color: white;
border: solid 1px #a7da39;
background: #a7da39;
background: -webkit-gradient(linear, left top, left bottom, from(#a7da39), to(#6a9211));
background: -moz-linear-gradient(top, #a7da39, #6a9211);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a7da39', endColorstr='#6a9211');
}
.symfony-block-steps span {
display: inline-block;
padding: 2px 3px;
font-size: 11px;
line-height: 15px;
color: #868686;
font-weight: bold;
text-transform: uppercase;
}
.symfony-block-steps span.selected {
background-color: #aacd4e;
color: #FFFFFF;
}
.symfony-form-row {
padding-bottom: 40px;
}
.symfony-form-column {
width: 430px;
float: left;
}
.symfony-form-footer {
padding-top: 20px;
clear: both;
}
.symfony-form-field {
height: 20px;
}
.symfony-form-row label {
display: block;
padding-bottom: 8px;
}
.symfony-form-field input[type=text],
.symfony-form-field input[type=password],
.symfony-form-field textarea,
.symfony-form-field select {
font-size: 13px;
color: #565656;
width: 200px;
}
.symfony-form-field input[type=text],
.symfony-form-field input[type=password],
.symfony-form-field textarea {
border: 1px solid #dadada;
background: #FFFFFF url(../images/background-textfield.gif) repeat-x left top;
width: 194px;
padding: 5px 6px;
}
.symfony-form-errors ul {
padding: 0;
}
.symfony-form-errors li {
background: url(../images/notification.gif) no-repeat left 6px;
font-size: 11px;
line-height: 16px;
color: #759e1a;
padding: 10px 25px;
}
.symfony-configuration {
margin: 10px 0;
width: 100%;
height: 240px;
}

View File

@@ -0,0 +1,137 @@
/*
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
Reset
*/
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
html, body {
background-color: #EFEFEF;
}
body {
font-size: 14px;
font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
color: #313131;
}
a {
color: #08C;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
h1, h2, h3 {
font-family: Georgia, "Times New Roman", Times, serif;
color: #404040;
}
h1 {
font-size: 45px;
padding-bottom: 30px;
}
h2 {
font-weight: bold;
color: #FFFFFF;
/* Font is reset to sans-serif (like body) */
font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
margin-bottom: 10px;
background-color: #aacd4e;
padding: 2px 4px;
display: inline-block;
text-transform: uppercase;
}
p {
line-height: 20px;
padding-bottom: 20px;
}
ul a {
background: url(../images/blue-arrow.png) no-repeat right 6px;
padding-right: 10px;
}
ul, ol {
padding-left: 20px;
}
li {
padding-bottom: 18px;
}
ol li {
list-style-type: decimal;
}
ul li {
list-style-type: none;
}
#symfony-header {
position: relative;
padding: 30px 30px 20px 30px;
}
#symfony-wrapper {
width: 970px;
margin: 0 auto;
padding-top: 50px;
}
#symfony-content {
background-color: white;
border: 1px solid #DFDFDF;
padding: 50px;
-moz-border-radius: 16px;
-webkit-border-radius: 16px;
border-radius: 16px;
margin-bottom: 5px;
}
.symfony-blocks-install {
overflow: hidden;
}
.symfony-blocks-install .symfony-block-logo {
float: left;
width: 358px;
}
.symfony-blocks-install .symfony-block-content {
float: left;
width: 510px;
}
.symfony-install-continue {
font-size: 0.95em;
padding-left: 0;
}
.symfony-install-continue li {
padding-bottom: 10px;
}
.version {
text-align: right;
font-size: 10px;
margin-right: 20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View File

@@ -0,0 +1,31 @@
{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %}
{% block title %}Symfony - Configure database{% endblock %}
{% block content %}
{% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %}
{% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %}
<h1>Configure your Database</h1>
<p>If your website needs a database connection, please configure it here.</p>
{{ form_errors(form) }}
<form action="{{ path('_configurator_step', { 'index': index }) }}" method="POST">
<div class="symfony-form-column">
{{ form_row(form.driver) }}
{{ form_row(form.host) }}
{{ form_row(form.name) }}
</div>
<div class="symfony-form-column">
{{ form_row(form.user) }}
{{ form_row(form.password) }}
</div>
{{ form_rest(form) }}
<div class="symfony-form-footer">
<p><input type="submit" value="Next Step" class="symfony-button-grey" /></p>
<p>* mandatory fields</p>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %}
{% block title %}Symfony - Configure global Secret{% endblock %}
{% block content %}
{% form_theme form "SensioDistributionBundle::Configurator/form.html.twig" %}
{% include "SensioDistributionBundle::Configurator/steps.html.twig" with { "index": index, "count": count } %}
<h1>Global Secret</h1>
<p>Configure the global secret for your website (the secret is used for the CSRF protection among other things):</p>
{{ form_errors(form) }}
<form action="{{ path('_configurator_step', { 'index': index }) }} " method="POST">
<div class="symfony-form-row">
{{ form_label(form.secret) }}
<div class="symfony-form-field">
{{ form_widget(form.secret) }}
<a class="symfony-button-grey" href="#" onclick="generateSecret(); return false;">Generate</a>
<div class="symfony-form-errors">
{{ form_errors(form.secret) }}
</div>
</div>
</div>
{{ form_rest(form) }}
<div class="symfony-form-footer">
<p><input type="submit" value="Next Step" class="symfony-button-grey" /></p>
<p>* mandatory fields</p>
</div>
</form>
<script type="text/javascript">
function generateSecret()
{
var result = '';
for (i=0; i < 32; i++) {
result += Math.round(Math.random()*16).toString(16);
}
document.getElementById('distributionbundle_secret_step_secret').value = result;
}
</script>
{% endblock %}

View File

@@ -0,0 +1,43 @@
{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %}
{% block content %}
{% if majors|length %}
<h1>Major Problems that need to be fixed now</h1>
<p>
We have detected <strong>{{ majors|length }}</strong> major problems.
You <em>must</em> fix them before continuing:
</p>
<ol>
{% for message in majors %}
<li>{{ message }}</li>
{% endfor %}
</ol>
{% endif %}
{% if minors|length %}
<h1>Some Recommandations</h1>
<p>
{% if majors|length %}
Additionally, we
{% else %}
We
{% endif %}
have detected some minor problems that we <em>recommend</em> you to fix to have a better Symfony
experience:
<ol>
{% for message in minors %}
<li>{{ message }}</li>
{% endfor %}
</ol>
</p>
{% endif %}
{% if not majors|length %}
<ul class="symfony_list">
<li><a href="{{ url }}">Configure your Symfony Application online</a></li>
</ul>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends "SensioDistributionBundle::Configurator/layout.html.twig" %}
{% block content_class %}config_done{% endblock %}
{% block content %}
<h1>Well done!</h1>
{% if is_writable %}
<h2>Your distribution is configured!</h2>
{% else %}
<h2 class="configure-error">Your distribution is almost configured but...</h2>
{% endif %}
<h3>
<span>
{% if is_writable %}
Your parameters.yml file has been overwritten with these parameters (in <em>{{ yml_path }}</em>):
{% else %}
Your parameters.yml file is not writeable! Here are the parameters you can copy and paste in <em>{{ yml_path }}</em>:
{% endif %}
</span>
</h3>
<textarea class="symfony-configuration">{{ parameters }}</textarea>
{% if welcome_url %}
<ul>
<li><a href="{{ welcome_url }}">Go to the Welcome page</a></li>
</ul>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% extends "form_div_layout.html.twig" %}
{% block field_rows %}
<div class="symfony-form-errors">
{{ form_errors(form) }}
</div>
{% for child in form.children %}
{{ form_row(child) }}
{% endfor %}
{% endblock field_rows %}
{% block field_row %}
<div class="symfony-form-row">
{{ form_label(form) }}
<div class="symfony-form-field">
{{ form_widget(form) }}
<div class="symfony-form-errors">
{{ form_errors(form) }}
</div>
</div>
</div>
{% endblock %}
{% block field_label %}
<label for="{{ id }}">
{{ label|trans }}
{% if required %}
<span class="symfony-form-required" title="This field is required">*</span>
{% endif %}
</label>
{% endblock %}

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="{{ asset('bundles/sensiodistribution/webconfigurator/css/configure.css') }}" />
<title>{% block title %}Web Configurator Bundle{% endblock %}</title>
<link rel="icon" type="image/x-icon" sizes="16x16" href="{{ asset('bundles/sensiodistribution/webconfigurator/images/favicon.ico') }}" />
</head>
<body>
<div id="symfony-wrapper">
<div id="symfony-header">
<img src="{{ asset("bundles/sensiodistribution/webconfigurator/images/logo-small.gif") }}" alt="Symfony logo" />
</div>
<div id="symfony-content">
{% block content %}{% endblock %}
</div>
<div class="version">Symfony Standard Edition v.{{ version }}</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<div class="symfony-block-steps">
{% for i in 1..count %}
{% if i == index + 1 %}
<span class="selected">Step {{ i }}</span>
{% else %}
<span>Step {{ i }}</span>
{% endif %}
{% if i != count %}
&gt;
{% endif %}
{% endfor %}
</div>

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\DistributionBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Sensio\Bundle\DistributionBundle\Configurator\Step\DoctrineStep;
use Sensio\Bundle\DistributionBundle\Configurator\Step\SecretStep;
/**
* SensioDistributionBundle.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Marc Weistroff <marc.weistroff@sensio.com>
*/
class SensioDistributionBundle extends Bundle
{
public function boot()
{
$configurator = $this->container->get('sensio.distribution.webconfigurator');
$configurator->addStep(new DoctrineStep($configurator->getParameters()));
$configurator->addStep(new SecretStep($configurator->getParameters()));
}
}

View File

@@ -0,0 +1,20 @@
{
"name": "sensio/distribution-bundle",
"description": "The base bundle for the Symfony Distributions",
"keywords": ["distribution","configuration"],
"type": "symfony-bundle",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"symfony/framework-bundle": "2.1.*"
},
"autoload": {
"psr-0": { "Sensio\\Bundle\\DistributionBundle": "" }
},
"target-dir": "Sensio/Bundle/DistributionBundle"
}

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
before_script:
- wget http://getcomposer.org/composer.phar
- php composer.phar install

View File

@@ -0,0 +1,171 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Cache class handles the @Cache annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Cache extends ConfigurationAnnotation
{
/**
* The expiration date as a valid date for the strtotime() function.
*
* @var string
*/
protected $expires;
/**
* The number of seconds that the response is considered fresh by a private
* cache like a web browser.
*
* @var integer
*/
protected $maxage;
/**
* The number of seconds that the response is considered fresh by a public
* cache like a reverse proxy cache.
*
* @var integer
*/
protected $smaxage;
/**
* Whether or not the response is public or not.
*
* @var integer
*/
protected $public;
/**
* Additional "Vary:"-headers
*
* @var array
*/
protected $vary = array();
/**
* Returns the expiration date for the Expires header field.
*
* @return string
*/
public function getExpires()
{
return $this->expires;
}
/**
* Sets the expiration date for the Expires header field.
*
* @param string $expires A valid php date
*/
public function setExpires($expires)
{
$this->expires = $expires;
}
/**
* Sets the number of seconds for the max-age cache-control header field.
*
* @param integer $maxage A number of seconds
*/
public function setMaxAge($maxage)
{
$this->maxage = $maxage;
}
/**
* Returns the number of seconds the response is considered fresh by a
* private cache.
*
* @return integer
*/
public function getMaxAge()
{
return $this->maxage;
}
/**
* Sets the number of seconds for the s-maxage cache-control header field.
*
* @param integer $smaxage A number of seconds
*/
public function setSMaxAge($smaxage)
{
$this->smaxage = $smaxage;
}
/**
* Returns the number of seconds the response is considered fresh by a
* public cache.
*
* @return integer
*/
public function getSMaxAge()
{
return $this->smaxage;
}
/**
* Returns whether or not a response is public.
*
* @return Boolean
*/
public function isPublic()
{
return (Boolean) $this->public;
}
/**
* Sets a response public.
*
* @param Boolean $public A boolean value
*/
public function setPublic($public)
{
$this->public = (Boolean) $public;
}
/**
* Returns the custom "Vary"-headers
*
* @return array
*/
public function getVary()
{
return $this->vary;
}
/**
* Add additional "Vary:"-headers
*
* @param array $vary
*/
public function setVary($vary)
{
$this->vary = $vary;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'cache';
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/**
* Base configuration annotation.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class ConfigurationAnnotation implements ConfigurationInterface
{
public function __construct(array $values)
{
foreach ($values as $k => $v) {
if (!method_exists($this, $name = 'set'.$k)) {
throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this)));
}
$this->$name($v);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* ConfigurationInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ConfigurationInterface
{
/**
* Returns the alias name for an annotated configuration.
*
* @return string
*/
function getAliasName();
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Method class handles the @Method annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Method extends ConfigurationAnnotation
{
/**
* An array of restricted HTTP methods.
*
* @var array
*/
protected $methods = array();
/**
* Returns the array of HTTP methods.
*
* @return array
*/
public function getMethods()
{
return $this->methods;
}
/**
* Sets the HTTP methods.
*
* @param array|string $methods An HTTP method or an array of HTTP methods
*/
public function setMethods($methods)
{
$this->methods = is_array($methods) ? $methods : array($methods);
}
/**
* Sets the HTTP methods.
*
* @param array|string $methods An HTTP method or an array of HTTP methods
*/
public function setValue($methods)
{
$this->setMethods($methods);
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'method';
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The ParamConverter class handles the @ParamConverter annotation parts.
*
* @ParamConverter("post", class="BlogBundle:Post")
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class ParamConverter extends ConfigurationAnnotation
{
/**
* The parameter name.
*
* @var string
*/
protected $name;
/**
* The parameter class.
*
* @var string
*/
protected $class;
/**
* An array of options.
*
* @var array
*/
protected $options = array();
/**
* Whether or not the parameter is optional.
*
* @var Boolean
*/
protected $optional = false;
/**
* Returns the parameter name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Sets the parameter name.
*
* @param string $name The parameter name
*/
public function setValue($name)
{
$this->setName($name);
}
/**
* Sets the parameter name.
*
* @param string $name The parameter name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Returns the parameter class name.
*
* @return string $name
*/
public function getClass()
{
return $this->class;
}
/**
* Sets the parameter class name.
*
* @param string $class The parameter class name
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* Returns an array of options.
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Sets an array of options.
*
* @param array $options An array of options
*/
public function setOptions($options)
{
$this->options = $options;
}
/**
* Sets whether or not the parameter is optional.
*
* @param Boolean $optional Wether the parameter is optional
*/
public function setIsOptional($optional)
{
$this->optional = (Boolean) $optional;
}
/**
* Returns whether or not the parameter is optional.
*
* @return Boolean
*/
public function isOptional()
{
return $this->optional;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'converters';
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
use Symfony\Component\Routing\Annotation\Route as BaseRoute;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* @author Kris Wallsmith <kris@symfony.com>
* @Annotation
*/
class Route extends BaseRoute
{
protected $service;
public function setService($service)
{
$this->service = $service;
}
public function getService()
{
return $this->service;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Configuration;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Template class handles the @Template annotation parts.
*
* @author Fabien Potencier <fabien@symfony.com>
* @Annotation
*/
class Template extends ConfigurationAnnotation
{
/**
* The template reference.
*
* @var TemplateReference
*/
protected $template;
/**
* The template engine used when a specific template isnt specified
*
* @var string
*/
protected $engine = 'twig';
/**
* The associative array of template variables.
*
* @var array
*/
protected $vars = array();
/**
* Should the template be streamed?
*
* @var Boolean
*/
protected $streamable = false;
/**
* Returns the array of templates variables.
*
* @return array
*/
public function getVars()
{
return $this->vars;
}
/**
* @param Boolean $streamable
*/
public function setIsStreamable($streamable)
{
$this->streamable = $streamable;
}
/**
* @return Boolean
*/
public function isStreamable()
{
return (Boolean) $this->streamable;
}
/**
* Sets the template variables
*
* @param array $vars The template variables
*/
public function setVars($vars)
{
$this->vars = $vars;
}
/**
* Returns the engine used when guessing template names
*
* @return string
*/
public function getEngine()
{
return $this->engine;
}
/**
* Sets the engine used when guessing template names
*
* @param string
*/
public function setEngine($engine)
{
$this->engine = $engine;
}
/**
* Sets the template logic name.
*
* @param string $template The template logic name
*/
public function setValue($template)
{
$this->setTemplate($template);
}
/**
* Returns the template reference.
*
* @return TemplateReference
*/
public function getTemplate()
{
return $this->template;
}
/**
* Sets the template reference.
*
* @param TemplateReference|string $template The template reference
*/
public function setTemplate($template)
{
$this->template = $template;
}
/**
* Returns the annotation alias name.
*
* @return string
* @see ConfigurationInterface
*/
public function getAliasName()
{
return 'template';
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged request.param_converter services to converter.manager service
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class AddParamConverterPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('sensio_framework_extra.converter.manager')) {
return;
}
$definition = $container->getDefinition('sensio_framework_extra.converter.manager');
foreach ($container->findTaggedServiceIds('request.param_converter') as $id => $attributes) {
$definition->addMethodCall('add', array(new Reference($id), isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0));
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
/**
* FrameworkExtraBundle configuration structure.
*
* @author Henrik Bjornskov <hb@peytz.dk>
*/
class Configuration
{
/**
* Generates the configuration tree.
*
* @return Symfony\Component\Config\Definition\NodeInterface
*/
public function getConfigTree()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sensio_framework_extra', 'array');
$rootNode
->children()
->arrayNode('router')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->arrayNode('request')
->addDefaultsIfNotSet()
->children()
->booleanNode('converters')->defaultTrue()->end()
->end()
->end()
->arrayNode('view')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->arrayNode('cache')
->addDefaultsIfNotSet()
->children()
->booleanNode('annotations')->defaultTrue()->end()
->end()
->end()
->end()
;
return $treeBuilder->buildTree();
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Definition\Processor;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SensioFrameworkExtraExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SensioFrameworkExtraExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->process($configuration->getConfigTree(), $configs);
$loader->load('services.xml');
$annotationsToLoad = array();
if ($config['router']['annotations']) {
$annotationsToLoad[] = 'routing.xml';
}
if ($config['request']['converters']) {
$annotationsToLoad[] = 'converters.xml';
}
if ($config['view']['annotations']) {
$annotationsToLoad[] = 'view.xml';
}
if ($config['cache']['annotations']) {
$annotationsToLoad[] = 'cache.xml';
}
if ($annotationsToLoad) {
// must be first
$loader->load('annotations.xml');
foreach ($annotationsToLoad as $config) {
$loader->load($config);
}
}
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/symfony_extra';
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\Response;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The CacheListener class has the responsability to modify the
* Response object when a controller uses the @Cache annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CacheListener
{
/**
* Modifies the response to apply HTTP expiration header fields.
*
* @param FilterResponseEvent $event The notified event
*/
public function onKernelResponse(FilterResponseEvent $event)
{
if (!$configuration = $event->getRequest()->attributes->get('_cache')) {
return;
}
$response = $event->getResponse();
if (!$response->isSuccessful()) {
return;
}
if (null !== $configuration->getSMaxAge()) {
$response->setSharedMaxAge($configuration->getSMaxAge());
}
if (null !== $configuration->getMaxAge()) {
$response->setMaxAge($configuration->getMaxAge());
}
if (null !== $configuration->getExpires()) {
$date = \DateTime::createFromFormat('U', strtotime($configuration->getExpires()), new \DateTimeZone('UTC'));
$response->setExpires($date);
}
if (null !== $configuration->getVary()) {
$response->setVary($configuration->getVary());
}
if ($configuration->isPublic()) {
$response->setPublic();
}
$event->setResponse($response);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The ControllerListener class parses annotation blocks located in
* controller classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ControllerListener
{
/**
* @var \Doctrine\Common\Annotations\Reader
*/
protected $reader;
/**
* Constructor.
*
* @param Reader $reader An Reader instance
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* Modifies the Request object to apply configuration information found in
* controllers annotations like the template to render or HTTP caching
* configuration.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controller = $event->getController())) {
return;
}
$object = new \ReflectionObject($controller[0]);
$method = $object->getMethod($controller[1]);
$request = $event->getRequest();
foreach (array_merge($this->reader->getClassAnnotations($object), $this->reader->getMethodAnnotations($method)) as $configuration) {
if ($configuration instanceof ConfigurationInterface) {
$request->attributes->set('_'.$configuration->getAliasName(), $configuration);
}
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The ParamConverterListener handles the @ParamConverter annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParamConverterListener
{
/**
* @var Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager
*/
protected $manager;
/**
* Constructor.
*
* @param ParamConverterManager $manager A ParamConverterManager instance
*/
public function __construct(ParamConverterManager $manager)
{
$this->manager = $manager;
}
/**
* Modifies the ParamConverterManager instance.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
$request = $event->getRequest();
$configurations = array();
if ($configuration = $request->attributes->get('_converters')) {
foreach (is_array($configuration) ? $configuration : array($configuration) as $configuration) {
$configurations[$configuration->getName()] = $configuration;
}
}
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} else {
$r = new \ReflectionFunction($controller);
}
// automatically apply conversion for non-configured objects
foreach ($r->getParameters() as $param) {
if (!$param->getClass()) {
continue;
}
$name = $param->getName();
if (!isset($configurations[$name])) {
$configuration = new ParamConverter(array());
$configuration->setName($name);
$configuration->setClass($param->getClass()->getName());
$configurations[$name] = $configuration;
}
$configurations[$name]->setIsOptional($param->isOptional());
}
$this->manager->apply($request, $configurations);
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\EventListener;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The TemplateListener class handles the @Template annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateListener
{
/**
* @var Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* Constructor.
*
* @param ContainerInterface $container The service container instance
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Guesses the template name to render and its variables and adds them to
* the request object.
*
* @param FilterControllerEvent $event A FilterControllerEvent instance
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controller = $event->getController())) {
return;
}
$request = $event->getRequest();
if (!$configuration = $request->attributes->get('_template')) {
return;
}
if (!$configuration->getTemplate()) {
$guesser = $this->container->get('sensio_framework_extra.view.guesser');
$configuration->setTemplate($guesser->guessTemplateName($controller, $request, $configuration->getEngine()));
}
$request->attributes->set('_template', $configuration->getTemplate());
$request->attributes->set('_template_vars', $configuration->getVars());
$request->attributes->set('_template_streamable', $configuration->isStreamable());
// all controller method arguments
if (!$configuration->getVars()) {
$r = new \ReflectionObject($controller[0]);
$vars = array();
foreach ($r->getMethod($controller[1])->getParameters() as $param) {
$vars[] = $param->getName();
}
$request->attributes->set('_template_default_vars', $vars);
}
}
/**
* Renders the template and initializes a new response object with the
* rendered template content.
*
* @param GetResponseForControllerResultEvent $event A GetResponseForControllerResultEvent instance
*/
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$request = $event->getRequest();
$parameters = $event->getControllerResult();
$templating = $this->container->get('templating');
if (null === $parameters) {
if (!$vars = $request->attributes->get('_template_vars')) {
if (!$vars = $request->attributes->get('_template_default_vars')) {
return;
}
}
$parameters = array();
foreach ($vars as $var) {
$parameters[$var] = $request->attributes->get($var);
}
}
if (!is_array($parameters)) {
return $parameters;
}
if (!$template = $request->attributes->get('_template')) {
return $parameters;
}
if (!$request->attributes->get('_template_streamable')) {
$event->setResponse($templating->renderResponse($template, $parameters));
} else {
$callback = function () use ($templating, $template, $parameters) {
return $templating->stream($template, $parameters);
};
$event->setResponse(new StreamedResponse($callback));
}
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2010,2011 Fabien Potencier
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,6 @@
SensioFrameworkExtraBundle
==========================
This bundle provides a way to configure your controllers with annotations.
Read about it on its [official homepage](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html).

View File

@@ -0,0 +1,108 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Persistence\ManagerRegistry;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* DoctrineParamConverter.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineParamConverter implements ParamConverterInterface
{
/**
* @var ManagerRegistry
*/
protected $registry;
public function __construct(ManagerRegistry $registry = null)
{
$this->registry = $registry;
}
public function apply(Request $request, ConfigurationInterface $configuration)
{
$class = $configuration->getClass();
$options = $this->getOptions($configuration);
// find by identifier?
if (false === $object = $this->find($class, $request, $options)) {
// find by criteria
if (false === $object = $this->findOneBy($class, $request, $options)) {
throw new \LogicException('Unable to guess how to get a Doctrine instance from the request information.');
}
}
if (null === $object && false === $configuration->isOptional()) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$request->attributes->set($configuration->getName(), $object);
return true;
}
protected function find($class, Request $request, $options)
{
if (!$request->attributes->has('id')) {
return false;
}
return $this->registry->getRepository($class, $options['entity_manager'])->find($request->attributes->get('id'));
}
protected function findOneBy($class, Request $request, $options)
{
$criteria = array();
$metadata = $this->registry->getManager($options['entity_manager'])->getClassMetadata($class);
foreach ($request->attributes->all() as $key => $value) {
if ($metadata->hasField($key)) {
$criteria[$key] = $value;
}
}
if (!$criteria) {
return false;
}
return $this->registry->getRepository($class, $options['entity_manager'])->findOneBy($criteria);
}
public function supports(ConfigurationInterface $configuration)
{
if (null === $this->registry) {
return false;
}
if (null === $configuration->getClass()) {
return false;
}
$options = $this->getOptions($configuration);
// Doctrine Entity?
return ! $this->registry->getManager($options['entity_manager'])
->getMetadataFactory()
->isTransient($configuration->getClass());
}
protected function getOptions(ConfigurationInterface $configuration)
{
return array_replace(array(
'entity_manager' => null,
), $configuration->getOptions());
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpFoundation\Request;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
*
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ParamConverterInterface
{
function apply(Request $request, ConfigurationInterface $configuration);
function supports(ConfigurationInterface $configuration);
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Managers converters.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Henrik Bjornskov <henrik@bjrnskov.dk>
*/
class ParamConverterManager
{
/**
* @var array
*/
protected $converters = array();
/**
* Applies all converters to the passed configurations and stops when a
* converter is applied it will move on to the next configuration and so on.
*
* @param Request $request
* @param array|object $configurations
*/
public function apply(Request $request, $configurations)
{
if (is_object($configurations)) {
$configurations = array($configurations);
}
foreach ($configurations as $configuration) {
// If the value is already an instance of the class we are trying to convert it into
// we should continue as no convertion is required
$value = $request->attributes->get($configuration->getName());
$className = $configuration->getClass();
if (is_object($value) && $value instanceof $className) {
continue;
}
foreach ($this->all() as $converter) {
if ($converter->supports($configuration)) {
if ($converter->apply($request, $configuration)) {
continue 2;
}
}
}
}
}
/**
* Adds a parameter converter.
*
* @param ParamConverterInterface $converter A ParamConverterInterface instance
* @param integer $priority The priority (between -10 and 10)
*/
public function add(ParamConverterInterface $converter, $priority = 0)
{
if (!isset($this->converters[$priority])) {
$this->converters[$priority] = array();
}
$this->converters[$priority][] = $converter;
}
/**
* Returns all registered param converters.
*
* @return array An array of param converters
*/
public function all()
{
krsort($this->converters);
$converters = array();
foreach ($this->converters as $all) {
$converters = array_merge($converters, $all);
}
return $converters;
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio_framework_extra.controller.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\ControllerListener</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.controller.listener" class="%sensio_framework_extra.controller.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<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="sensio_framework_extra.cache.listener" class="Sensio\Bundle\FrameworkExtraBundle\EventListener\CacheListener">
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" />
</service>
</services>
</container>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio_framework_extra.converter.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener</parameter>
<parameter key="sensio_framework_extra.converter.manager.class">Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager</parameter>
<parameter key="sensio_framework_extra.converter.doctrine.class">Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.converter.listener" class="%sensio_framework_extra.converter.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<argument type="service" id="sensio_framework_extra.converter.manager" />
</service>
<service id="sensio_framework_extra.converter.manager" class="%sensio_framework_extra.converter.manager.class%" />
<service id="sensio_framework_extra.converter.doctrine.orm" class="%sensio_framework_extra.converter.doctrine.class%">
<tag name="request.param_converter" />
<argument type="service" id="doctrine" on-invalid="ignore" />
</service>
</services>
</container>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio_framework_extra.routing.loader.annot_dir.class">Symfony\Component\Routing\Loader\AnnotationDirectoryLoader</parameter>
<parameter key="sensio_framework_extra.routing.loader.annot_file.class">Symfony\Component\Routing\Loader\AnnotationFileLoader</parameter>
<parameter key="sensio_framework_extra.routing.loader.annot_class.class">Sensio\Bundle\FrameworkExtraBundle\Routing\AnnotatedRouteControllerLoader</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.routing.loader.annot_dir" class="%sensio_framework_extra.routing.loader.annot_dir.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="sensio_framework_extra.routing.loader.annot_class" />
</service>
<service id="sensio_framework_extra.routing.loader.annot_file" class="%sensio_framework_extra.routing.loader.annot_file.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="file_locator" />
<argument type="service" id="sensio_framework_extra.routing.loader.annot_class" />
</service>
<service id="sensio_framework_extra.routing.loader.annot_class" class="%sensio_framework_extra.routing.loader.annot_class.class%" public="false">
<tag name="routing.loader" />
<argument type="service" id="annotation_reader" />
</service>
</services>
</container>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio_framework_extra.view.guesser.class">Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.view.guesser" class="%sensio_framework_extra.view.guesser.class%">
<argument type="service" id="kernel" />
</service>
</services>
</container>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" ?>
<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">
<parameters>
<parameter key="sensio_framework_extra.view.listener.class">Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener</parameter>
</parameters>
<services>
<service id="sensio_framework_extra.view.listener" class="%sensio_framework_extra.view.listener.class%">
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" />
<argument type="service" id="service_container" />
</service>
</services>
</container>

View File

@@ -0,0 +1,60 @@
@Cache
======
Usage
-----
The ``@Cache`` annotation makes it easy to define HTTP caching::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
/**
* @Cache(expires="tomorrow")
*/
public function indexAction()
{
}
You can also use the annotation on a class to define caching for all methods::
/**
* @Cache(expires="tomorrow")
*/
class BlogController extends Controller
{
}
When there is a conflict between the class configuration and the method
configuration, the latter overrides the former::
/**
* @Cache(expires="tomorrow")
*/
class BlogController extends Controller
{
/**
* @Cache(expires="+2 days")
*/
public function indexAction()
{
}
}
Attributes
----------
Here is a list of accepted attributes and their HTTP header equivalent:
============================== ===============
Annotation Response Method
============================== ===============
``@Cache(expires="tomorrow")`` ``$response->setExpires()``
``@Cache(smaxage="15")`` ``$response->setSharedMaxAge()``
``@Cache(maxage="15")`` ``$response->setMaxAge()``
``@Cache(vary=["Cookie"])`` ``$response->setVary()``
============================== ===============
.. note::
The ``expires`` attribute takes any valid date understood by the PHP
``strtotime()`` function.

View File

@@ -0,0 +1,101 @@
@ParamConverter
===============
Usage
-----
The ``@ParamConverter`` annotation calls *converters* to convert request
parameters to objects. These objects are stored as request attributes and so
they can be injected as controller method arguments::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/blog/{id}")
* @ParamConverter("post", class="SensioBlogBundle:Post")
*/
public function showAction(Post $post)
{
}
Several things happens under the hood:
* The converter tries to get a ``SensioBlogBundle:Post`` object from the
request attributes (request attributes comes from route placeholders -- here
``id``);
* If no ``Post`` object is found, a ``404`` Response is generated;
* If a ``Post`` object is found, a new ``post`` request attribute is defined
(accessible via ``$request->attributes->get('post')``);
* As for any other request attribute, it is automatically injected in the
controller when present in the method signature.
If you use type hinting as in the example above, you can even omit the
``@ParamConverter`` annotation altogether::
// automatic with method signature
public function showAction(Post $post)
{
}
Built-in Converters
-------------------
The bundle has only one built-in converter, the Doctrine one.
Doctrine Converter
~~~~~~~~~~~~~~~~~~
By default, the Doctrine converter uses the *default* entity manager. This can
be configured with the ``entity_manager`` option::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/blog/{id}")
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"entity_manager" = "foo"})
*/
public function showAction(Post $post)
{
}
Creating a Converter
--------------------
All converters must implement the
:class:`Sensio\\Bundle\\FrameworkExtraBundle\\Request\\ParamConverter\\ParamConverterInterface`::
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Component\HttpFoundation\Request;
interface ParamConverterInterface
{
function apply(Request $request, ConfigurationInterface $configuration);
function supports(ConfigurationInterface $configuration);
}
The ``supports()`` method must return ``true`` when it is able to convert the
given configuration (a ``ParamConverter`` instance).
The ``ParamConverter`` instance has three information about the annotation:
* ``name``: The attribute name;
* ``class``: The attribute class name (can be any string representing a class
name);
* ``options``: An array of options
The ``apply()`` method is called whenever a configuration is supported. Based
on the request attributes, it should set an attribute named
``$configuration->getName()``, which stores an object of class
``$configuration->getClass()``.
.. tip::
Use the ``DoctrineParamConverter`` class as a template for your own converters.

View File

@@ -0,0 +1,166 @@
@Route and @Method
==================
Usage
-----
The @Route annotation maps a route pattern with a controller::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class PostController extends Controller
{
/**
* @Route("/")
*/
public function indexAction()
{
// ...
}
}
The ``index`` action of the ``Post`` controller is now mapped to the ``/``
URL. This is equivalent to the following YAML configuration:
.. code-block:: yaml
blog_home:
pattern: /
defaults: { _controller: SensioBlogBundle:Post:index }
Like any route pattern, you can define placeholders, requirements, and default
values::
/**
* @Route("/{id}", requirements={"id" = "\d+"}, defaults={"foo" = "bar"})
*/
public function showAction($id)
{
}
You can also match more than one URL by defining additional ``@Route``
annotations::
/**
* @Route("/", defaults={"id" = 1})
* @Route("/{id}")
*/
public function showAction($id)
{
}
Activation
----------
The routes need to be imported to be active as any other routing resources
(note the ``annotation`` type):
.. code-block:: yaml
# app/config/routing.yml
# import routes from a controller class
post:
resource: "@SensioBlogBundle/Controller/PostController.php"
type: annotation
You can also import a whole directory:
.. code-block:: yaml
# import routes from a controller directory
blog:
resource: "@SensioBlogBundle/Controller"
type: annotation
As for any other resource, you can "mount" the routes under a given prefix:
.. code-block:: yaml
post:
resource: "@SensioBlogBundle/Controller/PostController.php"
prefix: /blog
type: annotation
Route Name
----------
A route defined with the ``@Route`` annotation is given a default name composed
of the bundle name, the controller name and the action name. That would be
``sensio_blog_post_index`` for the above example;
The ``name`` attribute can be used to override this default route name::
/**
* @Route("/", name="blog_home")
*/
public function indexAction()
{
// ...
}
Route Prefix
------------
A ``@Route`` annotation on a controller class defines a prefix for all action
routes::
/**
* @Route("/blog")
*/
class PostController extends Controller
{
/**
* @Route("/{id}")
*/
public function showAction($id)
{
}
}
The ``show`` action is now mapped to the ``/blog/{id}`` pattern.
Route Method
------------
There is a shortcut ``@Method`` annotation to specify the HTTP method allowed
for the route. To use it, import the ``Method`` annotation namespace::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* @Route("/blog")
*/
class PostController extends Controller
{
/**
* @Route("/edit/{id}")
* @Method({"GET", "POST"})
*/
public function editAction($id)
{
}
}
The ``edit`` action is now mapped to the ``/blog/edit/{id}`` pattern if the HTTP
method used is either GET or POST.
The ``@Method`` annotation is only considered when an action is annotated with
``@Route``.
Controller as Service
---------------------
The ``@Route`` annotation on a controller class can also be used to assign the
controller class to a service so that the controller resolver will instantiate
the controller by fetching it from the DI container instead of calling ``new
PostController()`` itself::
/**
* @Route(service="my_post_controller_service")
*/
class PostController extends Controller
{
// ...
}

View File

@@ -0,0 +1,74 @@
@Template
=========
Usage
-----
The ``@Template`` annotation associates a controller with a template name::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* @Template("SensioBlogBundle:Post:show.html.twig")
*/
public function showAction($id)
{
// get the Post
$post = ...;
return array('post' => $post);
}
When using the ``@Template`` annotation, the controller should return an
array of parameters to pass to the view instead of a ``Response`` object.
.. tip::
If the action returns a ``Response`` object, the ``@Template``
annotation is simply ignored.
If the template is named after the controller and action names, which is the
case for the above example, you can even omit the annotation value::
/**
* @Template
*/
public function showAction($id)
{
// get the Post
$post = ...;
return array('post' => $post);
}
And if the only parameters to pass to the template are method arguments, you
can use the ``vars`` attribute instead of returning an array. This is very
useful in combination with the ``@ParamConverter`` :doc:`annotation
<converters>`::
/**
* @ParamConverter("post", class="SensioBlogBundle:Post")
* @Template("SensioBlogBundle:Post:show.html.twig", vars={"post"})
*/
public function showAction(Post $post)
{
}
which, thanks to conventions, is equivalent to the following configuration::
/**
* @Template(vars={"post"})
*/
public function showAction(Post $post)
{
}
You can make it even more concise as all method arguments are automatically
passed to the template if the method returns ``null`` and no ``vars``
attribute is defined::
/**
* @Template
*/
public function showAction(Post $post)
{
}

View File

@@ -0,0 +1,145 @@
SensioFrameworkExtraBundle
==========================
The default Symfony2 ``FrameworkBundle`` implements a basic but robust and
flexible MVC framework. `SensioFrameworkExtraBundle`_ extends it to add sweet
conventions and annotations. It allows for more concise controllers.
Installation
------------
`Download`_ the bundle and put it under the ``Sensio\Bundle\`` namespace.
Then, like for any other bundle, include it in your Kernel class::
public function registerBundles()
{
$bundles = array(
...
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
);
...
}
Configuration
-------------
All features provided by the bundle are enabled by default when the bundle is
registered in your Kernel class.
The default configuration is as follow:
.. configuration-block::
.. code-block:: yaml
sensio_framework_extra:
router: { annotations: true }
request: { converters: true }
view: { annotations: true }
cache: { annotations: true }
.. code-block:: xml
<!-- xmlns:sensio-framework-extra="http://symfony.com/schema/dic/symfony_extra" -->
<sensio-framework-extra:config>
<router annotations="true" />
<request converters="true" />
<view annotations="true" />
<cache annotations="true" />
</sensio-framework-extra:config>
.. code-block:: php
// load the profiler
$container->loadFromExtension('sensio_framework_extra', array(
'router' => array('annotations' => true),
'request' => array('converters' => true),
'view' => array('annotations' => true),
'cache' => array('annotations' => true),
));
You can disable some annotations and conventions by defining one or more
settings to false.
Annotations for Controllers
---------------------------
Annotations are a great way to easily configure your controllers, from the
routes to the cache configuration.
Even if annotations are not a native feature of PHP, it still has several
advantages over the classic Symfony2 configuration methods:
* Code and configuration are in the same place (the controller class);
* Simple to learn and to use;
* Concise to write;
* Makes your Controller thin (as its sole responsibility is to get data from
the Model).
.. tip::
If you use view classes, annotations are a great way to avoid creating
view classes for simple and common use cases.
The following annotations are defined by the bundle:
.. toctree::
:maxdepth: 1
annotations/routing
annotations/converters
annotations/view
annotations/cache
This example shows all the available annotations in action::
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* @Route("/blog")
* @Cache(expires="tomorrow")
*/
class AnnotController extends Controller
{
/**
* @Route("/")
* @Template
*/
public function indexAction()
{
$posts = ...;
return array('posts' => $posts);
}
/**
* @Route("/{id}")
* @Method("GET")
* @ParamConverter("post", class="SensioBlogBundle:Post")
* @Template("SensioBlogBundle:Annot:post.html.twig", vars={"post"})
* @Cache(smaxage="15")
*/
public function showAction(Post $post)
{
}
}
As the ``showAction`` method follows some conventions, you can omit some
annotations::
/**
* @Route("/{id}")
* @Cache(smaxage="15")
*/
public function showAction(Post $post)
{
}
.. _`SensioFrameworkExtraBundle`: https://github.com/sensio/SensioFrameworkExtraBundle
.. _`Download`: http://github.com/sensio/SensioFrameworkExtraBundle

View File

@@ -0,0 +1,75 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Routing;
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
use Symfony\Component\Routing\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* AnnotatedRouteControllerLoader is an implementation of AnnotationClassLoader
* that sets the '_controller' default based on the class and method names.
*
* It also parse the @Method annotation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class AnnotatedRouteControllerLoader extends AnnotationClassLoader
{
/**
* Configures the _controller default parameter and eventually the _method
* requirement of a given Route instance.
*
* @param Route $route A Route instance
* @param ReflectionClass $class A ReflectionClass instance
* @param ReflectionMethod $method A ReflectionClass method
*/
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot)
{
// controller
$classAnnot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass);
if ($classAnnot && $service = $classAnnot->getService()) {
$route->setDefault('_controller', $service.':'.$method->getName());
} else {
$route->setDefault('_controller', $class->getName().'::'.$method->getName());
}
// requirements (@Method)
foreach ($this->reader->getMethodAnnotations($method) as $configuration) {
if ($configuration instanceof Method) {
$route->setRequirement('_method', implode('|', $configuration->getMethods()));
}
}
}
/**
* Makes the default route name more sane by removing common keywords.
*
* @param ReflectionClass $class A ReflectionClass instance
* @param ReflectionMethod $method A ReflectionMethod instance
* @return string
*/
protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method)
{
$routeName = parent::getDefaultRouteName($class, $method);
return preg_replace(array(
'/(bundle|controller)_/',
'/action(_\d+)?$/',
'/__/'
), array(
'_',
'\\1',
'_'
), $routeName);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Sensio\Bundle\FrameworkExtraBundle\DependencyInjection\Compiler\AddParamConverterPass;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* SensioFrameworkExtraBundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SensioFrameworkExtraBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new AddParamConverterPass());
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Sensio\Bundle\FrameworkExtraBundle\Templating;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use CG\Core\ClassUtils;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* The TemplateGuesser class handles the guessing of template name based on controller
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateGuesser
{
/**
* @var Symfony\Component\HttpKernel\KernelInterface
*/
protected $kernel;
/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
/**
* Guesses and returns the template name to render based on the controller
* and action names.
*
* @param array $controller An array storing the controller object and action method
* @param Request $request A Request instance
* @param string $engine
* @return TemplateReference template reference
* @throws \InvalidArgumentException
*/
public function guessTemplateName($controller, Request $request, $engine = 'twig')
{
$className = get_class($controller[0]);
// When JMSSecurityExtraBundle is used it generates Controller classes as MyAccountController__CG__
if (class_exists('CG\\Core\\ClassUtils')) {
$className = ClassUtils::getUserClass($className);
}
if (!preg_match('/Controller\\\(.+)Controller$/', $className, $matchController)) {
throw new \InvalidArgumentException(sprintf('The "%s" class does not look like a controller class (it must be in a "Controller" sub-namespace and the class name must end with "Controller")', get_class($controller[0])));
}
if (!preg_match('/^(.+)Action$/', $controller[1], $matchAction)) {
throw new \InvalidArgumentException(sprintf('The "%s" method does not look like an action method (it does not end with Action)', $controller[1]));
}
$bundle = $this->getBundleForClass($className);
return new TemplateReference($bundle->getName(), $matchController[1], $matchAction[1], $request->getRequestFormat(), $engine);
}
/**
* Returns the Bundle instance in which the given class name is located.
*
* @param string $class A fully qualified controller class name
* @param Bundle $bundle A Bundle instance
* @throws \InvalidArgumentException
*/
protected function getBundleForClass($class)
{
$reflectionClass = new \ReflectionClass($class);
$bundles = $this->kernel->getBundles();
do {
$namespace = $reflectionClass->getNamespaceName();
foreach ($bundles as $bundle) {
if (0 === strpos($namespace, $bundle->getNamespace())) {
return $bundle;
}
}
$reflectionClass = $reflectionClass->getParentClass();
} while ($reflectionClass);
throw new \InvalidArgumentException(sprintf('The "%s" class does not belong to a registered bundle.', $class));
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "sensio/framework-extra-bundle",
"description": "This bundle provides a way to configure your controllers with annotations",
"keywords": ["annotations","controllers"],
"type": "symfony-bundle",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"symfony/framework-bundle": "2.1.*",
"doctrine/common": ">=2.1,<2.4-dev"
},
"autoload": {
"psr-0": { "Sensio\\Bundle\\FrameworkExtraBundle": "" }
},
"target-dir": "Sensio/Bundle/FrameworkExtraBundle",
"extra": {
"branch-alias": {
"dev-master": "2.1.x-dev"
}
}
}

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
before_script:
- wget http://getcomposer.org/composer.phar
- php composer.phar install

View File

@@ -0,0 +1,316 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator;
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
/**
* Generates bundles.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateBundleCommand extends ContainerAwareCommand
{
private $generator;
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'),
new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'),
new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'),
new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'),
))
->setDescription('Generates a bundle')
->setHelp(<<<EOT
The <info>generate:bundle</info> command helps you generates new bundles.
By default, the command interacts with the developer to tweak the generation.
Any passed option will be used as a default value for the interaction
(<comment>--namespace</comment> is the only one needed if you follow the
conventions):
<info>php app/console generate:bundle --namespace=Acme/BlogBundle</info>
Note that you can use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any
problem.
If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
<info>php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
Note that the bundle namespace must end with "Bundle".
EOT
)
->setName('generate:bundle')
;
}
/**
* @see Command
*
* @throws \InvalidArgumentException When namespace doesn't end with Bundle
* @throws \RuntimeException When bundle can't be executed
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
foreach (array('namespace', 'dir') as $option) {
if (null === $input->getOption($option)) {
throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option));
}
}
$namespace = Validators::validateBundleNamespace($input->getOption('namespace'));
if (!$bundle = $input->getOption('bundle-name')) {
$bundle = strtr($namespace, array('\\' => ''));
}
$bundle = Validators::validateBundleName($bundle);
$dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace);
$format = Validators::validateFormat($input->getOption('format'));
$structure = $input->getOption('structure');
$dialog->writeSection($output, 'Bundle generation');
if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
$dir = getcwd().'/'.$dir;
}
$generator = $this->getGenerator();
$generator->generate($namespace, $bundle, $dir, $format, $structure);
$output->writeln('Generating the bundle code: <info>OK</info>');
$errors = array();
$runner = $dialog->getRunner($output, $errors);
// check that the namespace is already autoloaded
$runner($this->checkAutoloader($output, $namespace, $bundle, $dir));
// register the bundle in the Kernel class
$runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle));
// routing
$runner($this->updateRouting($dialog, $input, $output, $bundle, $format));
$dialog->writeGeneratorSummary($output, $errors);
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator');
// namespace
$output->writeln(array(
'',
'Your application code must be written in <comment>bundles</comment>. This command helps',
'you generate them easily.',
'',
'Each bundle is hosted under a namespace (like <comment>Acme/Bundle/BlogBundle</comment>).',
'The namespace should begin with a "vendor" name like your company name, your',
'project name, or your client name, followed by one or more optional category',
'sub-namespaces, and it should end with the bundle name itself',
'(which must have <comment>Bundle</comment> as a suffix).',
'',
'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more',
'details on bundle naming conventions.',
'',
'Use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any problem.',
'',
));
$namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace'));
$input->setOption('namespace', $namespace);
// bundle name
$bundle = $input->getOption('bundle-name') ?: strtr($namespace, array('\\Bundle\\' => '', '\\' => ''));
$output->writeln(array(
'',
'In your code, a bundle is often referenced by its name. It can be the',
'concatenation of all namespace parts but it\'s really up to you to come',
'up with a unique name (a good practice is to start with the vendor name).',
'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
'',
));
$bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle);
$input->setOption('bundle-name', $bundle);
// target dir
$dir = $input->getOption('dir') ?: dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src';
$output->writeln(array(
'',
'The bundle can be generated anywhere. The suggested default directory uses',
'the standard conventions.',
'',
));
$dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir);
$input->setOption('dir', $dir);
// format
$output->writeln(array(
'',
'Determine the format to use for the generated configuration.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format'));
$input->setOption('format', $format);
// optional files to generate
$output->writeln(array(
'',
'To help you get started faster, the command can generate some',
'code snippets for you.',
'',
));
$structure = $input->getOption('structure');
if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) {
$structure = true;
}
$input->setOption('structure', $structure);
// summary
$output->writeln(array(
'',
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
'',
sprintf("You are going to generate a \"<info>%s\\%s</info>\" bundle\nin \"<info>%s</info>\" using the \"<info>%s</info>\" format.", $namespace, $bundle, $dir, $format),
'',
));
}
protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir)
{
$output->write('Checking that the bundle is autoloaded: ');
if (!class_exists($namespace.'\\'.$bundle)) {
return array(
'- Edit the <comment>composer.json</comment> file and register the bundle',
' namespace in the "autoload" section:',
'',
);
}
}
protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true);
}
$output->write('Enabling the bundle inside the Kernel: ');
$manip = new KernelManipulator($kernel);
try {
$ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false;
if (!$ret) {
$reflected = new \ReflectionObject($kernel);
return array(
sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()),
' and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
'',
sprintf(' <comment>new %s(),</comment>', $namespace.'\\'.$bundle),
'',
);
}
} catch (\RuntimeException $e) {
return array(
sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $namespace.'\\'.$bundle),
'',
);
}
}
protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
}
$output->write('Importing the bundle routing resource: ');
$routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml');
try {
$ret = $auto ? $routing->addResource($bundle, $format) : false;
if (!$ret) {
if ('annotation' === $format) {
$help = sprintf(" <comment>resource: \"@%s/Controller/\"</comment>\n <comment>type: annotation</comment>\n", $bundle);
} else {
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n", $bundle, $format);
}
$help .= " <comment>prefix: /</comment>\n";
return array(
'- Import the bundle\'s routing resource in the app main routing file:',
'',
sprintf(' <comment>%s:</comment>', $bundle),
$help,
'',
);
}
} catch (\RuntimeException $e) {
return array(
sprintf('Bundle <comment>%s</comment> is already imported.', $bundle),
'',
);
}
}
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new BundleGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle');
}
return $this->generator;
}
public function setGenerator(BundleGenerator $generator)
{
$this->generator = $generator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Mapping\MetadataFactory;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
abstract class GenerateDoctrineCommand extends ContainerAwareCommand
{
public function isEnabled()
{
return class_exists('Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle');
}
protected function parseShortcutNotation($shortcut)
{
$entity = str_replace('/', '\\', $shortcut);
if (false === $pos = strpos($entity, ':')) {
throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity));
}
return array(substr($entity, 0, $pos), substr($entity, $pos + 1));
}
protected function getEntityMetadata($entity)
{
$factory = new MetadataFactory($this->getContainer()->get('doctrine'));
return $factory->getClassMetadata($entity)->getMetadata();
}
}

View File

@@ -0,0 +1,274 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Command\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
/**
* Generates a CRUD for a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateDoctrineCrudCommand extends GenerateDoctrineCommand
{
private $generator;
private $formGenerator;
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'),
new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'),
new InputOption('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'),
))
->setDescription('Generates a CRUD based on a Doctrine entity')
->setHelp(<<<EOT
The <info>doctrine:generate:crud</info> command generates a CRUD based on a Doctrine entity.
The default command only generates the list and show actions.
<info>php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin</info>
Using the --with-write option allows to generate the new, edit and delete actions.
<info>php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write</info>
EOT
)
->setName('doctrine:generate:crud')
->setAliases(array('generate:doctrine:crud'))
;
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
$entity = Validators::validateEntityName($input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$format = Validators::validateFormat($input->getOption('format'));
$prefix = $this->getRoutePrefix($input, $entity);
$withWrite = $input->getOption('with-write');
$dialog->writeSection($output, 'CRUD generation');
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator();
$generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite);
$output->writeln('Generating the CRUD code: <info>OK</info>');
$errors = array();
$runner = $dialog->getRunner($output, $errors);
// form
if ($withWrite) {
$this->generateForm($bundle, $entity, $metadata);
$output->writeln('Generating the Form code: <info>OK</info>');
}
// routing
if ('annotation' != $format) {
$runner($this->updateRouting($dialog, $input, $output, $bundle, $format, $entity, $prefix));
}
$dialog->writeGeneratorSummary($output, $errors);
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Doctrine2 CRUD generator');
// namespace
$output->writeln(array(
'',
'This command helps you generate CRUD controllers and templates.',
'',
'First, you need to give the entity for which you want to generate a CRUD.',
'You can give an entity that does not exist yet and the wizard will help',
'you defining it.',
'',
'You must use the shortcut notation like <comment>AcmeBlogBundle:Post</comment>.',
'',
));
$entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity'));
$input->setOption('entity', $entity);
list($bundle, $entity) = $this->parseShortcutNotation($entity);
// Entity exists?
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
// write?
$withWrite = $input->getOption('with-write') ?: false;
$output->writeln(array(
'',
'By default, the generator creates two actions: list and show.',
'You can also ask it to generate "write" actions: new, update, and delete.',
'',
));
$withWrite = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?'), $withWrite);
$input->setOption('with-write', $withWrite);
// format
$format = $input->getOption('format');
$output->writeln(array(
'',
'Determine the format to use for the generated CRUD.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $format), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $format);
$input->setOption('format', $format);
// route prefix
$prefix = $this->getRoutePrefix($input, $entity);
$output->writeln(array(
'',
'Determine the routes prefix (all the routes will be "mounted" under this',
'prefix: /prefix/, /prefix/new, ...).',
'',
));
$prefix = $dialog->ask($output, $dialog->getQuestion('Routes prefix', '/'.$prefix), '/'.$prefix);
$input->setOption('route-prefix', $prefix);
// summary
$output->writeln(array(
'',
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
'',
sprintf("You are going to generate a CRUD controller for \"<info>%s:%s</info>\"", $bundle, $entity),
sprintf("using the \"<info>%s</info>\" format.", $format),
'',
));
}
/**
* Tries to generate forms if they don't exist yet and if we need write operations on entities.
*/
private function generateForm($bundle, $entity, $metadata)
{
try {
$this->getFormGenerator()->generate($bundle, $entity, $metadata[0]);
} catch (\RuntimeException $e ) {
// form already exists
}
}
private function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format, $entity, $prefix)
{
$auto = true;
if ($input->isInteractive()) {
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true);
}
$output->write('Importing the CRUD routes: ');
$this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/');
$routing = new RoutingManipulator($bundle->getPath().'/Resources/config/routing.yml');
try {
$ret = $auto ? $routing->addResource($bundle->getName(), $format, '/'.$prefix, 'routing/'.strtolower(str_replace('\\', '_', $entity))) : false;
} catch (\RuntimeException $exc) {
$ret = false;
}
if (!$ret) {
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing/%s.%s\"</comment>\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format);
$help .= sprintf(" <comment>prefix: /%s</comment>\n", $prefix);
return array(
'- Import the bundle\'s routing resource in the bundle routing file',
sprintf(' (%s).', $bundle->getPath().'/Resources/config/routing.yml'),
'',
sprintf(' <comment>%s:</comment>', $bundle->getName().('' !== $prefix ? '_'.str_replace('/', '_', $prefix) : '')),
$help,
'',
);
}
}
protected function getRoutePrefix(InputInterface $input, $entity)
{
$prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity));
if ($prefix && '/' === $prefix[0]) {
$prefix = substr($prefix, 1);
}
return $prefix;
}
protected function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/crud');
}
return $this->generator;
}
public function setGenerator(DoctrineCrudGenerator $generator)
{
$this->generator = $generator;
}
protected function getFormGenerator()
{
if (null === $this->formGenerator) {
$this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form');
}
return $this->formGenerator;
}
public function setFormGenerator(DoctrineFormGenerator $formGenerator)
{
$this->formGenerator = $formGenerator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,312 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator;
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\Types\Type;
/**
* Initializes a Doctrine entity inside a bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GenerateDoctrineEntityCommand extends GenerateDoctrineCommand
{
private $generator;
protected function configure()
{
$this
->setName('doctrine:generate:entity')
->setAliases(array('generate:doctrine:entity'))
->setDescription('Generates a new Doctrine entity inside a bundle')
->addOption('entity', null, InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)')
->addOption('fields', null, InputOption::VALUE_REQUIRED, 'The fields to create with the new entity')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation')
->addOption('with-repository', null, InputOption::VALUE_NONE, 'Whether to generate the entity repository or not')
->setHelp(<<<EOT
The <info>doctrine:generate:entity</info> task generates a new Doctrine
entity inside a bundle:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post</info>
The above command would initialize a new entity in the following entity
namespace <info>Acme\BlogBundle\Entity\Blog\Post</info>.
You can also optionally specify the fields you want to generate in the new
entity:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --fields="title:string(255) body:text"</info>
The command can also generate the corresponding entity repository class with the
<comment>--with-repository</comment> option:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --with-repository</info>
By default, the command uses annotations for the mapping information; change it
with <comment>--format</comment>:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=yml</info>
To deactivate the interaction mode, simply use the `--no-interaction` option
without forgetting to pass all needed options:
<info>php app/console doctrine:generate:entity --entity=AcmeBlogBundle:Blog/Post --format=annotation --fields="title:string(255) body:text" --with-repository --no-interaction</info>
EOT
);
}
/**
* @throws \InvalidArgumentException When the bundle doesn't end with Bundle (Example: "Bundle/MySampleBundle")
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
if ($input->isInteractive()) {
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) {
$output->writeln('<error>Command aborted</error>');
return 1;
}
}
$entity = Validators::validateEntityName($input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$format = Validators::validateFormat($input->getOption('format'));
$fields = $this->parseFields($input->getOption('fields'));
$dialog->writeSection($output, 'Entity generation');
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$generator = $this->getGenerator();
$generator->generate($bundle, $entity, $format, array_values($fields), $input->getOption('with-repository'));
$output->writeln('Generating the entity code: <info>OK</info>');
$dialog->writeGeneratorSummary($output, array());
}
protected function interact(InputInterface $input, OutputInterface $output)
{
$dialog = $this->getDialogHelper();
$dialog->writeSection($output, 'Welcome to the Doctrine2 entity generator');
// namespace
$output->writeln(array(
'',
'This command helps you generate Doctrine2 entities.',
'',
'First, you need to give the entity name you want to generate.',
'You must use the shortcut notation like <comment>AcmeBlogBundle:Post</comment>.',
''
));
while (true) {
$entity = $dialog->askAndValidate($output, $dialog->getQuestion('The Entity shortcut name', $input->getOption('entity')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName'), false, $input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
// check reserved words
if ($this->getGenerator()->isReservedKeyword($entity)){
$output->writeln(sprintf('<bg=red> "%s" is a reserved word</>.', $entity));
continue;
}
try {
$b = $this->getContainer()->get('kernel')->getBundle($bundle);
if (!file_exists($b->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php')) {
break;
}
$output->writeln(sprintf('<bg=red>Entity "%s:%s" already exists</>.', $bundle, $entity));
} catch (\Exception $e) {
$output->writeln(sprintf('<bg=red>Bundle "%s" does not exist.</>', $bundle));
}
}
$input->setOption('entity', $bundle.':'.$entity);
// format
$output->writeln(array(
'',
'Determine the format to use for the mapping information.',
'',
));
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format'));
$input->setOption('format', $format);
// fields
$input->setOption('fields', $this->addFields($input, $output, $dialog));
// repository?
$output->writeln('');
$withRepository = $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate an empty repository class', $input->getOption('with-repository') ? 'yes' : 'no', '?'), $input->getOption('with-repository'));
$input->setOption('with-repository', $withRepository);
// summary
$output->writeln(array(
'',
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true),
'',
sprintf("You are going to generate a \"<info>%s:%s</info>\" Doctrine2 entity", $bundle, $entity),
sprintf("using the \"<info>%s</info>\" format.", $format),
'',
));
}
private function parseFields($input)
{
if (is_array($input)) {
return $input;
}
$fields = array();
foreach (explode(' ', $input) as $value) {
$elements = explode(':', $value);
$name = $elements[0];
if (strlen($name)) {
$type = isset($elements[1]) ? $elements[1] : 'string';
preg_match_all('/(.*)\((.*)\)/', $type, $matches);
$type = isset($matches[1][0]) ? $matches[1][0] : $type;
$length = isset($matches[2][0]) ? $matches[2][0] : null;
$fields[$name] = array('fieldName' => $name, 'type' => $type, 'length' => $length);
}
}
return $fields;
}
private function addFields(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
$fields = $this->parseFields($input->getOption('fields'));
$output->writeln(array(
'',
'Instead of starting with a blank entity, you can add some fields now.',
'Note that the primary key will be added automatically (named <comment>id</comment>).',
'',
));
$output->write('<info>Available types:</info> ');
$types = array_keys(Type::getTypesMap());
$count = 20;
foreach ($types as $i => $type) {
if ($count > 50) {
$count = 0;
$output->writeln('');
}
$count += strlen($type);
$output->write(sprintf('<comment>%s</comment>', $type));
if (count($types) != $i + 1) {
$output->write(', ');
} else {
$output->write('.');
}
}
$output->writeln('');
$fieldValidator = function ($type) use ($types) {
// FIXME: take into account user-defined field types
if (!in_array($type, $types)) {
throw new \InvalidArgumentException(sprintf('Invalid type "%s".', $type));
}
return $type;
};
$lengthValidator = function ($length) {
if (!$length) {
return $length;
}
$result = filter_var($length, FILTER_VALIDATE_INT, array(
'options' => array('min_range' => 1)
));
if (false === $result) {
throw new \InvalidArgumentException(sprintf('Invalid length "%s".', $length));
}
return $length;
};
while (true) {
$output->writeln('');
$self = $this;
$name = $dialog->askAndValidate($output, $dialog->getQuestion('New field name (press <return> to stop adding fields)', null), function ($name) use ($fields, $self) {
if (isset($fields[$name]) || 'id' == $name) {
throw new \InvalidArgumentException(sprintf('Field "%s" is already defined.', $name));
}
// check reserved words
if ($self->getGenerator()->isReservedKeyword($name)){
throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word.', $name));
}
return $name;
});
if (!$name) {
break;
}
$defaultType = 'string';
if (substr($name, -3) == '_at') {
$defaultType = 'datetime';
} elseif (substr($name, -3) == '_id') {
$defaultType = 'integer';
}
$type = $dialog->askAndValidate($output, $dialog->getQuestion('Field type', $defaultType), $fieldValidator, false, $defaultType);
$data = array('fieldName' => $name, 'type' => $type);
if ($type == 'string') {
$data['length'] = $dialog->askAndValidate($output, $dialog->getQuestion('Field length', 255), $lengthValidator, false, 255);
}
$fields[$name] = $data;
}
return $fields;
}
public function getGenerator()
{
if (null === $this->generator) {
$this->generator = new DoctrineEntityGenerator($this->getContainer()->get('filesystem'), $this->getContainer()->get('doctrine'));
}
return $this->generator;
}
public function setGenerator(DoctrineEntityGenerator $generator)
{
$this->generator = $generator;
}
protected function getDialogHelper()
{
$dialog = $this->getHelperSet()->get('dialog');
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') {
$this->getHelperSet()->set($dialog = new DialogHelper());
}
return $dialog;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Command\Command;
use Sensio\Bundle\GeneratorBundle\Generator\DoctrineFormGenerator;
/**
* Generates a form type class for a given Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*/
class GenerateDoctrineFormCommand extends GenerateDoctrineCommand
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputArgument('entity', InputArgument::REQUIRED, 'The entity class name to initialize (shortcut notation)'),
))
->setDescription('Generates a form type class based on a Doctrine entity')
->setHelp(<<<EOT
The <info>doctrine:generate:form</info> command generates a form class based on a Doctrine entity.
<info>php app/console doctrine:generate:form AcmeBlogBundle:Post</info>
EOT
)
->setName('doctrine:generate:form')
->setAliases(array('generate:doctrine:form'))
;
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$entity = Validators::validateEntityName($input->getArgument('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
$entityClass = $this->getContainer()->get('doctrine')->getEntityNamespace($bundle).'\\'.$entity;
$metadata = $this->getEntityMetadata($entityClass);
$bundle = $this->getApplication()->getKernel()->getBundle($bundle);
$generator = new DoctrineFormGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/form');
$generator->generate($bundle, $entity, $metadata[0]);
$output->writeln(sprintf(
'The new %s.php class file has been created under %s.',
$generator->getClassName(),
$generator->getClassPath()
));
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command\Helper;
use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Generates bundles.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DialogHelper extends BaseDialogHelper
{
public function writeGeneratorSummary(OutputInterface $output, $errors)
{
if (!$errors) {
$this->writeSection($output, 'You can now start using the generated code!');
} else {
$this->writeSection($output, array(
'The command was not able to configure everything automatically.',
'You must do the following changes manually.',
), 'error');
$output->writeln($errors);
}
}
public function getRunner(OutputInterface $output, &$errors)
{
$runner = function ($err) use ($output, &$errors) {
if ($err) {
$output->writeln('<fg=red>FAILED</>');
$errors = array_merge($errors, $err);
} else {
$output->writeln('<info>OK</info>');
}
};
return $runner;
}
public function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
{
$output->writeln(array(
'',
$this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
'',
));
}
public function getQuestion($question, $default, $sep = ':')
{
return $default ? sprintf('<info>%s</info> [<comment>%s</comment>]%s ', $question, $default, $sep) : sprintf('<info>%s</info>%s ', $question, $sep);
}
}

View File

@@ -0,0 +1,160 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Command;
/**
* Validator functions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Validators
{
static public function validateBundleNamespace($namespace)
{
if (!preg_match('/Bundle$/', $namespace)) {
throw new \InvalidArgumentException('The namespace must end with Bundle.');
}
$namespace = strtr($namespace, '/', '\\');
if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\?)+$/', $namespace)) {
throw new \InvalidArgumentException('The namespace contains invalid characters.');
}
// validate reserved keywords
$reserved = self::getReservedWords();
foreach (explode('\\', $namespace) as $word) {
if (in_array(strtolower($word), $reserved)) {
throw new \InvalidArgumentException(sprintf('The namespace cannot contain PHP reserved words ("%s").', $word));
}
}
// validate that the namespace is at least one level deep
if (false === strpos($namespace, '\\')) {
$msg = array();
$msg[] = sprintf('The namespace must contain a vendor namespace (e.g. "VendorName\%s" instead of simply "%s").', $namespace, $namespace);
$msg[] = 'If you\'ve specified a vendor namespace, did you forget to surround it with quotes (init:bundle "Acme\BlogBundle")?';
throw new \InvalidArgumentException(implode("\n\n", $msg));
}
return $namespace;
}
static public function validateBundleName($bundle)
{
if (!preg_match('/Bundle$/', $bundle)) {
throw new \InvalidArgumentException('The bundle name must end with Bundle.');
}
return $bundle;
}
static public function validateTargetDir($dir, $bundle, $namespace)
{
// add trailing / if necessary
return '/' === substr($dir, -1, 1) ? $dir : $dir.'/';
}
static public function validateFormat($format)
{
$format = strtolower($format);
if (!in_array($format, array('php', 'xml', 'yml', 'annotation'))) {
throw new \RuntimeException(sprintf('Format "%s" is not supported.', $format));
}
return $format;
}
static public function validateEntityName($entity)
{
if (false === $pos = strpos($entity, ':')) {
throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity));
}
return $entity;
}
static public function getReservedWords()
{
return array(
'abstract',
'and',
'array',
'as',
'break',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'do',
'else',
'elseif',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'extends',
'final',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'interface',
'instanceof',
'namespace',
'new',
'or',
'private',
'protected',
'public',
'static',
'switch',
'throw',
'try',
'use',
'var',
'while',
'xor',
'__CLASS__',
'__DIR__',
'__FILE__',
'__LINE__',
'__FUNCTION__',
'__METHOD__',
'__NAMESPACE__',
'die',
'echo',
'empty',
'exit',
'eval',
'include',
'include_once',
'isset',
'list',
'require',
'require_once',
'return',
'print',
'unset',
);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\DependencyInjection\Container;
/**
* Generates a bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class BundleGenerator extends Generator
{
private $filesystem;
private $skeletonDir;
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
public function generate($namespace, $bundle, $dir, $format, $structure)
{
$dir .= '/'.strtr($namespace, '\\', '/');
if (file_exists($dir)) {
throw new \RuntimeException(sprintf('Unable to generate the bundle as the target directory "%s" is not empty.', realpath($dir)));
}
$basename = substr($bundle, 0, -6);
$parameters = array(
'namespace' => $namespace,
'bundle' => $bundle,
'format' => $format,
'bundle_basename' => $basename,
'extension_alias' => Container::underscore($basename),
);
$this->renderFile($this->skeletonDir, 'Bundle.php', $dir.'/'.$bundle.'.php', $parameters);
$this->renderFile($this->skeletonDir, 'Extension.php', $dir.'/DependencyInjection/'.$basename.'Extension.php', $parameters);
$this->renderFile($this->skeletonDir, 'Configuration.php', $dir.'/DependencyInjection/Configuration.php', $parameters);
$this->renderFile($this->skeletonDir, 'DefaultController.php', $dir.'/Controller/DefaultController.php', $parameters);
$this->renderFile($this->skeletonDir, 'DefaultControllerTest.php', $dir.'/Tests/Controller/DefaultControllerTest.php', $parameters);
$this->renderFile($this->skeletonDir, 'index.html.twig', $dir.'/Resources/views/Default/index.html.twig', $parameters);
if ('xml' === $format || 'annotation' === $format) {
$this->renderFile($this->skeletonDir, 'services.xml', $dir.'/Resources/config/services.xml', $parameters);
} else {
$this->renderFile($this->skeletonDir, 'services.'.$format, $dir.'/Resources/config/services.'.$format, $parameters);
}
if ('annotation' != $format) {
$this->renderFile($this->skeletonDir, 'routing.'.$format, $dir.'/Resources/config/routing.'.$format, $parameters);
}
if ($structure) {
$this->filesystem->mkdir($dir.'/Resources/doc');
$this->filesystem->touch($dir.'/Resources/doc/index.rst');
$this->filesystem->mkdir($dir.'/Resources/translations');
$this->filesystem->copy($this->skeletonDir.'/messages.fr.xlf', $dir.'/Resources/translations/messages.fr.xlf');
$this->filesystem->mkdir($dir.'/Resources/public/css');
$this->filesystem->mkdir($dir.'/Resources/public/images');
$this->filesystem->mkdir($dir.'/Resources/public/js');
}
}
}

View File

@@ -0,0 +1,290 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Generates a CRUD controller.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DoctrineCrudGenerator extends Generator
{
protected $filesystem;
protected $skeletonDir;
protected $routePrefix;
protected $routeNamePrefix;
protected $bundle;
protected $entity;
protected $metadata;
protected $format;
protected $actions;
/**
* Constructor.
*
* @param Filesystem $filesystem A Filesystem instance
* @param string $skeletonDir Path to the skeleton directory
*/
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
/**
* Generate the CRUD controller.
*
* @param BundleInterface $bundle A bundle object
* @param string $entity The entity relative class name
* @param ClassMetadataInfo $metadata The entity class metadata
* @param string $format The configuration format (xml, yaml, annotation)
* @param string $routePrefix The route name prefix
* @param array $needWriteActions Wether or not to generate write actions
*
* @throws \RuntimeException
*/
public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $format, $routePrefix, $needWriteActions)
{
$this->routePrefix = $routePrefix;
$this->routeNamePrefix = str_replace('/', '_', $routePrefix);
$this->actions = $needWriteActions ? array('index', 'show', 'new', 'edit', 'delete') : array('index', 'show');
if (count($metadata->identifier) > 1) {
throw new \RuntimeException('The CRUD generator does not support entity classes with multiple primary keys.');
}
if (!in_array('id', $metadata->identifier)) {
throw new \RuntimeException('The CRUD generator expects the entity object has a primary key field named "id" with a getId() method.');
}
$this->entity = $entity;
$this->bundle = $bundle;
$this->metadata = $metadata;
$this->setFormat($format);
$this->generateControllerClass();
$dir = sprintf('%s/Resources/views/%s', $this->bundle->getPath(), str_replace('\\', '/', $this->entity));
if (!file_exists($dir)) {
$this->filesystem->mkdir($dir, 0777);
}
$this->generateIndexView($dir);
if (in_array('show', $this->actions)) {
$this->generateShowView($dir);
}
if (in_array('new', $this->actions)) {
$this->generateNewView($dir);
}
if (in_array('edit', $this->actions)) {
$this->generateEditView($dir);
}
$this->generateTestClass();
$this->generateConfiguration();
}
/**
* Sets the configuration format.
*
* @param string $format The configuration format
*/
private function setFormat($format)
{
switch ($format) {
case 'yml':
case 'xml':
case 'php':
case 'annotation':
$this->format = $format;
break;
default:
$this->format = 'yml';
break;
}
}
/**
* Generates the routing configuration.
*
*/
private function generateConfiguration()
{
if (!in_array($this->format, array('yml', 'xml', 'php'))) {
return;
}
$target = sprintf(
'%s/Resources/config/routing/%s.%s',
$this->bundle->getPath(),
strtolower(str_replace('\\', '_', $this->entity)),
$this->format
);
$this->renderFile($this->skeletonDir, 'config/routing.'.$this->format, $target, array(
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
));
}
/**
* Generates the controller class only.
*
*/
private function generateControllerClass()
{
$dir = $this->bundle->getPath();
$parts = explode('\\', $this->entity);
$entityClass = array_pop($parts);
$entityNamespace = implode('\\', $parts);
$target = sprintf(
'%s/Controller/%s/%sController.php',
$dir,
str_replace('\\', '/', $entityNamespace),
$entityClass
);
if (file_exists($target)) {
throw new \RuntimeException('Unable to generate the controller as it already exists.');
}
$this->renderFile($this->skeletonDir, 'controller.php', $target, array(
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'dir' => $this->skeletonDir,
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'entity_class' => $entityClass,
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
'format' => $this->format,
));
}
/**
* Generates the functional test class only.
*
*/
private function generateTestClass()
{
$parts = explode('\\', $this->entity);
$entityClass = array_pop($parts);
$entityNamespace = implode('\\', $parts);
$dir = $this->bundle->getPath() .'/Tests/Controller';
$target = $dir .'/'. str_replace('\\', '/', $entityNamespace).'/'. $entityClass .'ControllerTest.php';
$this->renderFile($this->skeletonDir, 'tests/test.php', $target, array(
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'entity_class' => $entityClass,
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
'actions' => $this->actions,
'dir' => $this->skeletonDir,
));
}
/**
* Generates the index.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateIndexView($dir)
{
$this->renderFile($this->skeletonDir, 'views/index.html.twig', $dir.'/index.html.twig', array(
'dir' => $this->skeletonDir,
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'record_actions' => $this->getRecordActions(),
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
/**
* Generates the show.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateShowView($dir)
{
$this->renderFile($this->skeletonDir, 'views/show.html.twig', $dir.'/show.html.twig', array(
'dir' => $this->skeletonDir,
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
/**
* Generates the new.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateNewView($dir)
{
$this->renderFile($this->skeletonDir, 'views/new.html.twig', $dir.'/new.html.twig', array(
'dir' => $this->skeletonDir,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'actions' => $this->actions,
));
}
/**
* Generates the edit.html.twig template in the final bundle.
*
* @param string $dir The path to the folder that hosts templates in the bundle
*/
private function generateEditView($dir)
{
$this->renderFile($this->skeletonDir, 'views/edit.html.twig', $dir.'/edit.html.twig', array(
'dir' => $this->skeletonDir,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
'actions' => $this->actions,
));
}
/**
* Returns an array of record actions to generate (edit, show).
*
* @return array
*/
private function getRecordActions()
{
return array_filter($this->actions, function($item) {
return in_array($item, array('show', 'edit'));
});
}
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Tools\EntityGenerator;
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
/**
* Generates a form class based on a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
*/
class DoctrineEntityGenerator extends Generator
{
private $filesystem;
private $registry;
public function __construct(Filesystem $filesystem, RegistryInterface $registry)
{
$this->filesystem = $filesystem;
$this->registry = $registry;
}
public function generate(BundleInterface $bundle, $entity, $format, array $fields, $withRepository)
{
// configure the bundle (needed if the bundle does not contain any Entities yet)
$config = $this->registry->getEntityManager(null)->getConfiguration();
$config->setEntityNamespaces(array_merge(
array($bundle->getName() => $bundle->getNamespace().'\\Entity'),
$config->getEntityNamespaces()
));
$entityClass = $this->registry->getEntityNamespace($bundle->getName()).'\\'.$entity;
$entityPath = $bundle->getPath().'/Entity/'.str_replace('\\', '/', $entity).'.php';
if (file_exists($entityPath)) {
throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass));
}
$class = new ClassMetadataInfo($entityClass);
if ($withRepository) {
$class->customRepositoryClassName = $entityClass.'Repository';
}
$class->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
$class->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
foreach ($fields as $field) {
$class->mapField($field);
}
$entityGenerator = $this->getEntityGenerator();
if ('annotation' === $format) {
$entityGenerator->setGenerateAnnotations(true);
$entityCode = $entityGenerator->generateEntityClass($class);
$mappingPath = $mappingCode = false;
} else {
$cme = new ClassMetadataExporter();
$exporter = $cme->getExporter('yml' == $format ? 'yaml' : $format);
$mappingPath = $bundle->getPath().'/Resources/config/doctrine/'.str_replace('\\', '.', $entity).'.orm.'.$format;
if (file_exists($mappingPath)) {
throw new \RuntimeException(sprintf('Cannot generate entity when mapping "%s" already exists.', $mappingPath));
}
$mappingCode = $exporter->exportClassMetadata($class);
$entityGenerator->setGenerateAnnotations(false);
$entityCode = $entityGenerator->generateEntityClass($class);
}
$this->filesystem->mkdir(dirname($entityPath));
file_put_contents($entityPath, $entityCode);
if ($mappingPath) {
$this->filesystem->mkdir(dirname($mappingPath));
file_put_contents($mappingPath, $mappingCode);
}
if ($withRepository) {
$path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\'));
$this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path);
}
}
public function isReservedKeyword($keyword)
{
return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword);
}
protected function getEntityGenerator()
{
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(false);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->setNumSpaces(4);
$entityGenerator->setAnnotationPrefix('ORM\\');
return $entityGenerator;
}
protected function getRepositoryGenerator()
{
return new EntityRepositoryGenerator();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
/**
* Generates a form class based on a Doctrine entity.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Hugo Hamon <hugo.hamon@sensio.com>
*/
class DoctrineFormGenerator extends Generator
{
private $filesystem;
private $skeletonDir;
private $className;
private $classPath;
public function __construct(Filesystem $filesystem, $skeletonDir)
{
$this->filesystem = $filesystem;
$this->skeletonDir = $skeletonDir;
}
public function getClassName()
{
return $this->className;
}
public function getClassPath()
{
return $this->classPath;
}
/**
* Generates the entity form class if it does not exist.
*
* @param BundleInterface $bundle The bundle in which to create the class
* @param string $entity The entity relative class name
* @param ClassMetadataInfo $metadata The entity metadata class
*/
public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata)
{
$parts = explode('\\', $entity);
$entityClass = array_pop($parts);
$this->className = $entityClass.'Type';
$dirPath = $bundle->getPath().'/Form';
$this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Type.php';
if (file_exists($this->classPath)) {
throw new \RuntimeException(sprintf('Unable to generate the %s form class as it already exists under the %s file', $this->className, $this->classPath));
}
if (count($metadata->identifier) > 1) {
throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.');
}
$parts = explode('\\', $entity);
array_pop($parts);
$this->renderFile($this->skeletonDir, 'FormType.php', $this->classPath, array(
'dir' => $this->skeletonDir,
'fields' => $this->getFieldsFromMetadata($metadata),
'namespace' => $bundle->getNamespace(),
'entity_namespace' => implode('\\', $parts),
'entity_class' => $entityClass,
'form_class' => $this->className,
'form_type_name' => strtolower(str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$this->className),
));
}
/**
* 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) $metadata->fieldNames;
// Remove the primary key field if it's not managed manually
if (!$metadata->isIdentifierNatural()) {
$fields = array_diff($fields, $metadata->identifier);
}
foreach ($metadata->associationMappings as $fieldName => $relation) {
if ($relation['type'] !== ClassMetadataInfo::ONE_TO_MANY) {
$fields[] = $fieldName;
}
}
return $fields;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Generator;
/**
* Generator is the base class for all generators.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Generator
{
protected function render($skeletonDir, $template, $parameters)
{
$twig = new \Twig_Environment(new \Twig_Loader_Filesystem($skeletonDir), array(
'debug' => true,
'cache' => false,
'strict_variables' => true,
'autoescape' => false,
));
return $twig->render($template, $parameters);
}
protected function renderFile($skeletonDir, $template, $target, $parameters)
{
if (!is_dir(dirname($target))) {
mkdir(dirname($target), 0777, true);
}
return file_put_contents($target, $this->render($skeletonDir, $template, $parameters));
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2011 Fabien Potencier
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,105 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Changes the PHP code of a Kernel.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class KernelManipulator extends Manipulator
{
private $kernel;
private $reflected;
/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
$this->reflected = new \ReflectionObject($kernel);
}
/**
* Adds a bundle at the end of the existing ones.
*
* @param string $bundle The bundle class name
*
* @return Boolean true if it worked, false otherwise
*
* @throws \RuntimeException If bundle is already defined
*/
public function addBundle($bundle)
{
if (!$this->reflected->getFilename()) {
return false;
}
$src = file($this->reflected->getFilename());
$method = $this->reflected->getMethod('registerBundles');
$lines = array_slice($src, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1);
// Don't add same bundle twice
if (false !== strpos(implode('', $lines), $bundle)) {
throw new \RuntimeException(sprintf('Bundle "%s" is already defined in "AppKernel::registerBundles()".', $bundle));
}
$this->setCode(token_get_all('<?php '.implode('', $lines)), $method->getStartLine());
while ($token = $this->next()) {
// $bundles
if (T_VARIABLE !== $token[0] || '$bundles' !== $token[1]) {
continue;
}
// =
$this->next();
// array
$token = $this->next();
if (T_ARRAY !== $token[0]) {
return false;
}
// add the bundle at the end of the array
while ($token = $this->next()) {
// look for );
if (')' !== $this->value($token)) {
continue;
}
if (';' !== $this->value($this->peek())) {
continue;
}
// ;
$this->next();
$lines = array_merge(
array_slice($src, 0, $this->line - 2),
// Appends a separator comma to the current last position of the array
array(rtrim(rtrim($src[$this->line - 2]), ',') . ",\n"),
array(sprintf(" new %s(),\n", $bundle)),
array_slice($src, $this->line - 1)
);
file_put_contents($this->reflected->getFilename(), implode('', $lines));
return true;
}
}
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
/**
* Changes the PHP code of a Kernel.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Manipulator
{
protected $tokens;
protected $line;
/**
* Sets the code to manipulate.
*
* @param array $tokens An array of PHP tokens
* @param integer $line The start line of the code
*/
protected function setCode(array $tokens, $line = 0)
{
$this->tokens = $tokens;
$this->line = $line;
}
/**
* Gets the next token.
*
* @param mixed A PHP token
*/
protected function next()
{
while ($token = array_shift($this->tokens)) {
$this->line += substr_count($this->value($token), "\n");
if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
return $token;
}
}
/**
* Peeks the next token.
*
* @param mixed A PHP token
*/
protected function peek($nb = 1)
{
$i = 0;
$tokens = $this->tokens;
while ($token = array_shift($tokens)) {
if (is_array($token) && in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
continue;
}
++$i;
if ($i == $nb) {
return $token;
}
}
}
/**
* Gets the value of a token.
*
* @param string The token value
*/
protected function value($token)
{
return is_array($token) ? $token[1] : $token;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sensio\Bundle\GeneratorBundle\Manipulator;
/**
* Changes the PHP code of a YAML routing file.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RoutingManipulator extends Manipulator
{
private $file;
/**
* Constructor.
*
* @param string $file The YAML routing file path
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* Adds a routing resource at the top of the existing ones.
*
* @param string $bundle
* @param string $format
* @param string $prefix
* @param string $path
*
* @return Boolean true if it worked, false otherwise
*
* @throws \RuntimeException If bundle is already imported
*/
public function addResource($bundle, $format, $prefix = '/', $path = 'routing')
{
$current = '';
if (file_exists($this->file)) {
$current = file_get_contents($this->file);
// Don't add same bundle twice
if (false !== strpos($current, $bundle)) {
throw new \RuntimeException(sprintf('Bundle "%s" is already imported.', $bundle));
}
} elseif (!is_dir($dir = dirname($this->file))) {
mkdir($dir, 0777, true);
}
$code = sprintf("%s:\n", $bundle.('/' !== $prefix ? '_'.str_replace('/', '_', substr($prefix, 1)) : ''));
if ('annotation' == $format) {
$code .= sprintf(" resource: \"@%s/Controller/\"\n type: annotation\n", $bundle);
} else {
$code .= sprintf(" resource: \"@%s/Resources/config/%s.%s\"\n", $bundle, $path, $format);
}
$code .= sprintf(" prefix: %s\n", $prefix);
$code .= "\n";
$code .= $current;
if (false === file_put_contents($this->file, $code)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,10 @@
SensioGeneratorBundle
=====================
The `SensioGeneratorBundle` extends the default Symfony2 command line
interface by providing new interactive and intuitive commands for generating
code skeletons like bundles, form classes, or CRUD controllers based on a
Doctrine 2 schema.
More information in the official
[documentation](http://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html).

View File

@@ -0,0 +1,68 @@
Generating a New Bundle Skeleton
================================
Usage
-----
The ``generate:bundle`` generates a new bundle structure and automatically
activates it in the application.
By default the command is run in the interactive mode and asks questions to
determine the bundle name, location, configuration format and default
structure:
.. code-block:: bash
php app/console generate:bundle
To deactivate the interactive mode, use the `--no-interaction` option but don't
forget to pass all needed options:
.. code-block:: bash
php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle --no-interaction
Available Options
-----------------
* ``--namespace``: The namespace of the bundle to create. The namespace should
begin with a "vendor" name like your company name, your project name, or
your client name, followed by one or more optional category sub-namespaces,
and it should end with the bundle name itself (which must have Bundle as a
suffix):
.. code-block:: bash
php app/console generate:bundle --namespace=Acme/Bundle/BlogBundle
* ``--bundle-name``: The optional bundle name. It must be a string ending with
the ``Bundle`` suffix:
.. code-block:: bash
php app/console generate:bundle --bundle-name=AcmeBlogBundle
* ``--dir``: The directory in which to store the bundle. By convention, the
command detects and uses the applications's ``src/`` folder:
.. code-block:: bash
php app/console generate:bundle --dir=/var/www/myproject/src
* ``--format``: (**annotation**) [values: yml, xml, php or annotation]
Determine the format to use for the generated configuration files like
routing. By default, the command uses the ``annotation`` format. Choosing
the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is
already installed:
.. code-block:: bash
php app/console generate:bundle --format=annotation
* ``--structure``: (**no**) [values: yes|no] Whether or not to generate a
complete default directory structure including empty public folders for
documentation, web assets and translations dictionaries:
.. code-block:: bash
php app/console generate:bundle --structure=yes

View File

@@ -0,0 +1,65 @@
Generating a CRUD Controller Based on a Doctrine Entity
=======================================================
Usage
-----
The ``generate:doctrine:crud`` generates a basic controller for a given entity
located in a given bundle. This controller allows to perform the five basic
operations on a model.
* Listing all records,
* Showing one given record identified by its primary key,
* Creating a new record,
* Editing an existing record,
* Deleting an existing record.
By default the command is run in the interactive mode and asks questions to
determine the entity name, the route prefix or whether or not to generate write
actions:
.. code-block:: bash
php app/console generate:doctrine:crud
To deactivate the interactive mode, use the `--no-interaction` option but don't
forget to pass all needed options:
.. code-block:: bash
php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post --format=annotation --with-write --no-interaction
Available Options
-----------------
* ``--entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:crud --entity=AcmeBlogBundle:Post
* ``--route-prefix``: The prefix to use for each route that identifies an
action:
.. code-block:: bash
php app/console generate:doctrine:crud --route-prefix=acme_post
* ``--with-write``: (**no**) [values: yes|no] Whether or not to generate the
`new`, `create`, `edit`, `update` and `delete` actions:
.. code-block:: bash
php app/console generate:doctrine:crud --with-write
* ``--format``: (**annotation**) [values: yml, xml, php or annotation]
Determine the format to use for the generated configuration files like
routing. By default, the command uses the ``annotation`` format. Choosing
the ``annotation`` format expects the ``SensioFrameworkExtraBundle`` is
already installed:
.. code-block:: bash
php app/console generate:doctrine:crud --format=annotation

View File

@@ -0,0 +1,55 @@
Generating a New Doctrine Entity Stub
=====================================
Usage
-----
The ``generate:doctrine:entity`` command generates a new Doctrine entity stub
including the mapping definition and the class properties, getters and setters.
By default the command is run in the interactive mode and asks questions to
determine the bundle name, location, configuration format and default
structure:
.. code-block:: bash
php app/console generate:doctrine:entity
The command can be run in a non interactive mode by using the
``--non-interaction`` option without forgetting all needed options:
.. code-block:: bash
php app/console generate:doctrine:entity --non-interaction --entity=AcmeBlogBundle:Post --fields="title:string(100) body:text" --format=xml
Available Options
-----------------
* ``--entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:entity --entity=AcmeBlogBundle:Post
* ``--fields``: The list of fields to generate in the entity class:
.. code-block:: bash
php app/console generate:doctrine:entity --fields="title:string(100) body:text"
* ``--format``: (**annotation**) [values: yml, xml, php or annotation] This
option determines the format to use for the generated configuration files
like routing. By default, the command uses the ``annotation`` format:
.. code-block:: bash
php app/console generate:doctrine:entity --format=annotation
* ``--with-repository``: This option tells whether or not to generate the
related Doctrine `EntityRepository` class:
.. code-block:: bash
php app/console generate:doctrine:entity --with-repository

View File

@@ -0,0 +1,23 @@
Generating a New Form Type Class Based on a Doctrine Entity
===========================================================
Usage
-----
The ``generate:doctrine:form`` generates a basic form type class by using the
metadata mapping of a given entity class:
.. code-block:: bash
php app/console generate:doctrine:form AcmeBlogBundle:Post
Required Arguments
------------------
* ``entity``: The entity name given as a shortcut notation containing the
bundle name in which the entity is located and the name of the entity. For
example: ``AcmeBlogBundle:Post``:
.. code-block:: bash
php app/console generate:doctrine:form AcmeBlogBundle:Post

View File

@@ -0,0 +1,42 @@
SensioGeneratorBundle
==========================
The ``SensioGeneratorBundle`` extends the default Symfony2 command line
interface by providing new interactive and intuitive commands for generating
code skeletons like bundles, form classes or CRUD controllers based on a
Doctrine 2 schema.
Installation
------------
`Download`_ the bundle and put it under the ``Sensio\\Bundle\\`` namespace.
Then, like for any other bundle, include it in your Kernel class::
public function registerBundles()
{
$bundles = array(
...
new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(),
);
...
}
List of Available Commands
--------------------------
The ``SensioGeneratorBundle`` comes with four new commands that can be run in
interactive mode or not. The interactive mode asks you some questions to
configure the command parameters to generate the definitive code. The list of
new commands are listed below:
.. toctree::
:maxdepth: 1
commands/generate_bundle
commands/generate_doctrine_crud
commands/generate_doctrine_entity
commands/generate_doctrine_form
.. _Download: http://github.com/sensio/SensioGeneratorBundle

View File

@@ -0,0 +1,9 @@
<?php
namespace {{ namespace }};
use Symfony\Component\HttpKernel\Bundle\Bundle;
class {{ bundle }} extends Bundle
{
}

View File

@@ -0,0 +1,29 @@
<?php
namespace {{ namespace }}\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('{{ extension_alias }}');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace {{ namespace }}\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
{% if 'annotation' == format -%}
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
{% endif %}
class DefaultController extends Controller
{
{% if 'annotation' == format -%}
/**
* @Route("/hello/{name}")
* @Template()
*/
{% endif -%}
public function indexAction($name)
{
{% if 'annotation' != format -%}
return $this->render('{{ bundle }}:Default:index.html.twig', array('name' => $name));
{%- else -%}
return array('name' => $name);
{%- endif %}
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace {{ namespace }}\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/hello/Fabien');
$this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace {{ namespace }}\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class {{ bundle_basename }}Extension extends Extension
{
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
{% if format == 'yml' -%}
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
{%- elseif format == 'xml' or format == 'annotation' -%}
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
{%- elseif format == 'php' -%}
$loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.php');
{%- endif %}
}
}

View File

@@ -0,0 +1 @@
Hello {% raw %}{{ name }}{% endraw %}!

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>J'aime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -0,0 +1,11 @@
<?php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('{{ bundle }}_homepage', new Route('/hello/{name}', array(
'_controller' => '{{ bundle }}:Default:index',
)));
return $collection;

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="{{ bundle }}_homepage" pattern="/hello/{name}">
<default key="_controller">{{ bundle }}:Default:index</default>
</route>
</routes>

View File

@@ -0,0 +1,3 @@
{{ bundle }}_homepage:
pattern: /hello/{name}
defaults: { _controller: {{ bundle }}:Default:index }

View File

@@ -0,0 +1,21 @@
<?php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
/*
$container->setDefinition(
'{{ extension_alias }}.example',
new Definition(
'{{ namespace }}\Example',
array(
new Reference('service_id'),
"plain_value",
new Parameter('parameter_name'),
)
)
);
*/

Some files were not shown because too many files have changed in this diff Show More