¿Cuantos bundles deberían haber?
¡Uno para toda la App!
¡Buuuu!
¡Fueraaaaa!
¡Un Bundle por grupo!
1 Bundle - src/AppBundle
¿Cuantos bundles deberían haber... si hago...?
Nos hemos olvidado lo más importante de todo...
Salir de nuestra zona de confort
namespace AppBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* This bundle will rule the world
*/
class AppBundle extends Bundle
{
}
namespace AppBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* This bundle will rule the world
*/
class AppBundle extends Bundle
{
}
namespace AppBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* This bundle will rule the world
*/
class AppBundle extends Bundle
{
}
namespace Symfony\Component\HttpKernel\Bundle;
/**
* An implementation of BundleInterface that adds a few conventions
* for DependencyInjection extensions and Console commands.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Bundle implements BundleInterface
{
}
/**
* Returns the bundle's container extension.
*
* @return ExtensionInterface|null The container extension
*
* @throws \LogicException
*/
public function getContainerExtension()
{
if (null === $this->extension) {
$extension = $this->createContainerExtension();
if (null !== $extension) {
if (!$extension instanceof ExtensionInterface) {
throw new \LogicException(sprintf('Extension %s must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', get_class($extension)));
}
// check naming convention
$basename = preg_replace('/Bundle$/', '', $this->getName());
$expectedAlias = Container::underscore($basename);
if ($expectedAlias != $extension->getAlias()) {
throw new \LogicException(sprintf(
'Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.',
$expectedAlias, $extension->getAlias()
));
}
$this->extension = $extension;
} else {
$this->extension = false;
}
}
if ($this->extension) {
return $this->extension;
}
}
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* My bundle
*/
final class MyBundle extends Bundle
{
/**
* Returns the bundle's container extension.
*
* @return ExtensionInterface|null The container extension
*
* @throws \LogicException
*/
public function getContainerExtension()
{
return new MyExtension();
}
}
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* My bundle
*/
final class MyBundle extends Bundle
{
/**
* Returns the bundle's container extension.
*
* @return ExtensionInterface|null The container extension
*
* @throws \LogicException
*/
public function getContainerExtension()
{
return null;
}
}
http://github.com/mmoreram/BaseBundle
namespace AppBundle;
use Mmoreram\BaseBundle\BaseBundle;
/**
* Class MyBundle.
*/
final class MyBundle extends BaseBundle
{
}
/**
* Finds and registers Commands.
*
* Override this method if your bundle commands do not follow the conventions:
*
* * Commands are in the 'Command' sub-directory
* * Commands extend Symfony\Component\Console\Command\Command
*
* @param Application $application An Application instance
*/
public function registerCommands(Application $application)
{
if (!is_dir($dir = $this->getPath().'/Command')) {
return;
}
if (!class_exists('Symfony\Component\Finder\Finder')) {
throw new \RuntimeException('You need the symfony/finder component to register bundle commands.');
}
$finder = new Finder();
$finder->files()->name('*Command.php')->in($dir);
$prefix = $this->getNamespace().'\\Command';
foreach ($finder as $file) {
$ns = $prefix;
if ($relativePath = $file->getRelativePath()) {
$ns .= '\\'.str_replace('/', '\\', $relativePath);
}
$class = $ns.'\\'.$file->getBasename('.php');
if ($this->container) {
$alias = 'console.command.'.strtolower(str_replace('\\', '_', $class));
if ($this->container->has($alias)) {
continue;
}
}
$r = new \ReflectionClass($class);
if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) {
$application->add($r->newInstance());
}
}
}
/**
* Register Commands.
*
* Disabled as commands are registered as services.
*
* @param Application $application An Application instance
*/
public function registerCommands(Application $application)
{
return;
}
/**
* Register Commands.
*
* Disabled as commands are registered as services.
*
* @param Application $application An Application instance
*/
public function registerCommands(Application $application)
{
return [
new OneCommand(),
new AnotherCommand()
]
}
¿Alguien sabe exactamente qué ocurre cuando... php bin/console?
protected function registerCommands()
{
if ($this->commandsRegistered) {
return;
}
$this->commandsRegistered = true;
$this->kernel->boot();
$container = $this->kernel->getContainer();
foreach ($this->kernel->getBundles() as $bundle) {
if ($bundle instanceof Bundle) {
$bundle->registerCommands($this);
}
}
if ($container->hasParameter('console.command.ids')) {
foreach ($container->getParameter('console.command.ids') as $id) {
$this->add($container->get($id));
}
}
}
/**
* Builds bundle.
*
* @param ContainerBuilder $container Container
*/
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CompilerPass1());
$container->addCompilerPass(new CompilerPass2());
}
/**
* Return a CompilerPass instance array.
*
* @return CompilerPassInterface[]
*/
public function getCompilerPasses()
{
return [
new CompilerPass1(),
new CompilerPass2(),
];
}
Un bundle es un paquete autosuficiente
¿Quién ha inyectado alguna vez el servicio @router?
Adivinad qué pasó...
/**
* Create instance of current bundle, and return dependent bundle namespaces.
*
* @return array Bundle instances
*/
public static function getBundleDependencies(KernelInterface $kernel)
{
return [];
}
/**
* Create instance of current bundle, and return dependent bundle namespaces.
*
* @return array Bundle instances
*/
public static function getBundleDependencies(KernelInterface $kernel)
{
return [
'Another\Bundle',
'Even\Another\Bundle',
];
}
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $config);
/**
* Setting all config elements as DI parameters to inject them
*/
$container->setParameter(
'my.bundle.service_provider',
$config['service_provider']
);
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__ . '/../Resources/config')
);
/**
* Loading DI definitions
*/
$loader->load('classes.yml');
$loader->load('services.yml');
$loader->load('commands.yml');
use Mmoreram\BaseBundle\DependencyInjection\BaseExtension;
/**
* This is the class that loads and manages your bundle configuration
*/
class MyExtension extends BaseExtension
{
}
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* @return string The alias
*
* @api
*/
public function getAlias()
{
return 'app';
}
/**
* Return a new Configuration instance.
*
* If object returned by this method is an instance of
* ConfigurationInterface, extension will use the Configuration to read all
* bundle config definitions.
*
* Also will call getParametrizationValues method to load some config values
* to internal parameters.
*
* @return ConfigurationInterface Configuration file
*/
protected function getConfigurationInstance()
{
return new Configuration();
}
/**
* Get the Config file location.
*
* @return string Config file location
*/
protected function getConfigFilesLocation()
{
return __DIR__ . '/../Resources/config';
}
/**
* Config files to load.
*
* Each array position can be a simple file name if must be loaded always,
* or an array, with the filename in the first position, and a boolean in
* the second one.
*
* As a parameter, this method receives all loaded configuration, to allow
* setting this boolean value from a configuration value.
*
* return array(
* 'file1',
* 'file2',
* ['file3', $config['my_boolean'],
* ...
* );
*
* @param array $config Config definitions
*
* @return array Config files
*/
protected function getConfigFiles(array $config)
{
return [
'services',
'commands',
'controllers',
];
}
/**
* Load Parametrization definition.
*
* return array(
* 'parameter1' => $config['parameter1'],
* 'parameter2' => $config['parameter2'],
* ...
* );
*
* @param array $config Bundles config values
*
* @return array Parametrization values
*/
protected function getParametrizationValues(array $config)
{
return [
'my.bundle.service_provider' => $config['service_provider'],
];
}
¿Cuantos inyectáis parámetros en vuestros servicios?
/**
* Load Parametrization definition.
*
* return array(
* 'parameter1' => $config['parameter1'],
* 'parameter2' => $config['parameter2'],
* ...
* );
*
* @param array $config Bundles config values
*
* @return array Parametrization values
*/
protected function getParametrizationValues(array $config)
{
return [
'my.bundle.service_provider' => $config['service_provider'],
];
}
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class MailTransportPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
// always first check if the primary service is defined
if (!$container->has('app.mailer_transport_chain')) {
return;
}
$definition = $container->findDefinition('app.mailer_transport_chain');
// find all service IDs with the app.mail_transport tag
$taggedServices = $container->findTaggedServiceIds('app.mail_transport');
foreach ($taggedServices as $id => $tags) {
// add the transport service to the ChainTransport service
$definition->addMethodCall('addTransport', array(new Reference($id)));
}
}
}
use Mmoreram\BaseBundle\CompilerPass\TagCompilerPass;
/**
* Class FeederCompilerPass.
*/
final class FeederCompilerPass extends TagCompilerPass
{
}
/**
* Get collector service name.
*
* @return string Collector service name
*/
public function getCollectorServiceName()
{
return 'my.collector.service';
}
/**
* Get collector method name.
*
* @return string Collector method name
*/
public function getCollectorMethodName()
{
return 'addClass';
}
/**
* Get tag name.
*
* @return string Tag name
*/
public function getTagName()
{
return 'my.tag';
}
Bundle
App Symfony
¿Que pasa cuando alguien depende de nosotros?
Bundle
App Symfony
LIbrería externa PHP
Librería PHP
Bundle
App Symfony
LIbrería externa PHP
Bundle externo
Librería PHP
Bundle
App Symfony
En un bundle se pone todo lo necesario para que la librería PHP creada se pueda exponer al framework
La clase Bundle
Los mappings de Doctrine
Las clases para el Dependency Injection (Extension, CompilerPass, Configuration)
Los archivos yml de configuración
En un componente ponemos todo lo que depende de librerías PHP y no es exclusivo de Symfony Framework
Esto incluye clases, managers, entidades, factories, controladores, comandos...
La clase Bundle, aún dependiendo de una librería PHP (HttpKernel), solo es funcional en el Framework