High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
-
Upload
matthiasnoback -
Category
Internet
-
view
2.059 -
download
0
description
Transcript of High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
![Page 1: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/1.jpg)
The Naked BundleMatthias Noback
![Page 2: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/2.jpg)
![Page 3: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/3.jpg)
Assuming you all have aworking project
https://github.com/matthiasnoback/high-quality-bundles-project
![Page 4: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/4.jpg)
Generate a bundleUse app/console generate:bundle
Namespace: Dpc/Bundle/TutorialBundleBundle name: DpcTutorialBundleConfiguration: ymlWhole directory structure: yes
![Page 5: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/5.jpg)
The full directory structure of a bundle:
![Page 6: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/6.jpg)
What's wrong?Too many commentsRouting and a controllerTranslationsTwig templatesA useless test
![Page 7: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/7.jpg)
You are not going to use it all,but it will be committed!
![Page 8: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/8.jpg)
Before we continue, clean up yourbundle
Remove the following files and directories:ControllerResources/docResources/publicResources/translationsResources/viewsTests
Also remove any superfluous comments!
![Page 9: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/9.jpg)
The officialview onbundles
![Page 10: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/10.jpg)
First-class citizens
Documentation » The Quick Tour » The Architecture
![Page 11: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/11.jpg)
I think your code is more important than the framework,which should be considered an implementation detail.
![Page 12: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/12.jpg)
All your code lives in abundle
Documentation » The Book » Creating Pages in Symfony2
![Page 13: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/13.jpg)
I don't think that's a good idea.It contradicts the promise of reuse of "pre-built feature
packages".
![Page 14: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/14.jpg)
Almost everything livesinside a bundle
Documentation » Glossary
![Page 15: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/15.jpg)
Which is not really true, because many things live insidelibraries (e.g. the Symfony components), which is good.
![Page 16: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/16.jpg)
Best practicesDocumentation » Cookbook » Bundles
![Page 17: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/17.jpg)
Controllers
Controllers don't need to extend anything at all.ContainerAware* should be avoided in all cases.
![Page 18: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/18.jpg)
Tests
What's up with the 95%?
![Page 19: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/19.jpg)
Twig
Why Twig? I though Symfony didn't care about this.
Documentation » The Book » Creating and Using Templates
![Page 20: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/20.jpg)
The old view on bundles isnot sufficient anymorePeople are reimplementing things because existing
solutions are too tightly coupled to a framework (or even aspecific version).
Why is it necessary to do all these things again for Symfony,Laravel, Zend, CodeIgniter, CakePHP, etc.?
![Page 21: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/21.jpg)
Last year I started workingon this
![Page 22: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/22.jpg)
Then it became this
![Page 23: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/23.jpg)
About bundles
![Page 24: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/24.jpg)
A bundle is...A thin layer of Framework-specific
configuration to make resources from somelibrary available in a Symfony2 application.
![Page 25: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/25.jpg)
A "Symfony application"meaning:
A project that depends on the Symfony FrameworkBundle.
![Page 26: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/26.jpg)
Resources areRoutes (Symfony Routing Component)Services (Symfony DependencyInjection Component)Templates (Twig)Form types (Symfony Form Component)Mapping metadata (Doctrine ORM, MongoDB ODM, etc.)Translations (Symfony Translation Component)Commands (Symfony Console Component)...?
![Page 27: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/27.jpg)
So: a bundle is mainly configuration to make these resourcesavailable, the rest is elsewhere in a library.
![Page 28: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/28.jpg)
I also wrote
![Page 29: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/29.jpg)
The challengeMake the bundle as clean as possible
![Page 30: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/30.jpg)
Entities
![Page 31: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/31.jpg)
Create an entityUse app/console doctrine:generate:entity
Specs
The entity shortcut name: DpcTutorialBundle:Post.Configuration format: annotationIt has a title (string) field.Run app/console doctrine:schema:create orupdate --force and make sure your entity has acorresponding table in your database.
![Page 32: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/32.jpg)
Let's say you've modelled the Postentity very well
You may want to reuse this in other projects.Yet it's only useful if that project uses Doctrine ORM too!
![Page 33: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/33.jpg)
Why?Annotations couple the Post class to Doctrine ORM.
(Since annotations are classes!)
![Page 34: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/34.jpg)
Also: why are my entities inside abundle?
They are not only useful inside a Symfony project.
![Page 35: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/35.jpg)
Move the entity to anothernamespace
E.g. Dpc\Tutorial\Model\Post.
![Page 36: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/36.jpg)
Create an XML mapping fileE.g. Dpc\Tutorial\Model\Mapping\Post.orm.xml<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="Dpc\Tutorial\Model\Post"> <id name="id" type="integer"> <generator strategy="AUTO"/> </id> <field name="title" type="string"/> </entity></doctrine-mapping>
You can copy the basic XML from/vendor/doctrine/orm/docs/en/reference/xml-
mapping.rst.
![Page 37: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/37.jpg)
In factAlways use XML mapping, it makes a lot of sense, and you
get auto-completion in your IDE!
![Page 38: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/38.jpg)
Remove all ORM things (annotations) from the Post class
![Page 39: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/39.jpg)
If you are going to try the following at home:
Update DoctrineBundleModify composer.json:
{ "require": { ... "doctrine/doctrine-bundle": "~1.2@dev" }}
Run composer update doctrine/doctrine-bundle
![Page 40: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/40.jpg)
Add a compiler pass to your bundleIt will load the XML mapping files
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
class DpcTutorialBundle{ public function build(ContainerBuilder $container) { $container->addCompilerPass($this->buildMappingCompilerPass()); }
private function buildMappingCompilerPass() { return DoctrineOrmMappingsPass::createXmlMappingDriver( array( __DIR__ . '/../../Test/Model/Mapping/' => 'Dpc\Tutorial\Model' ) ); }}
![Page 41: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/41.jpg)
What have we won?Clean model classesThey are reusable in non-Symfony projectsThey are reusable with different persistence libraries
Documentation » The Cookbook » Doctrine » How to provide model classes for several Doctrineimplementations
![Page 42: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/42.jpg)
Controllers
![Page 43: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/43.jpg)
Create a controllerUse app/console generate:controller
Specs
Name: DpcTutorialBundle:PostConfiguration: annotationTemplate: twigThe route contains an id parameter.Action: showActionRoute: /post/{id}/show
![Page 44: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/44.jpg)
Implement the following logicModify the action to retrieve a Post entity from the
database:public function showAction(Post $post){ return array('post' => $post);}
![Page 45: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/45.jpg)
Don't forget to register the route# in the bundle's routing.yml file:DpcTutorialBundle_Controllers: resource: "@DpcTutorialBundle/Controller" type: "annotation"
![Page 46: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/46.jpg)
By the wayConsider using XML for routing too!
For the same reasons
![Page 47: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/47.jpg)
Does all of this really needto be inside the bundle?
![Page 48: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/48.jpg)
Move the controller class to thelibrary
![Page 49: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/49.jpg)
Remove parent Controller classWe are going to inject every dependency by hand instead of
relying on the service container.
![Page 50: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/50.jpg)
Create a service for the controllerservices: dpc_tutorial.post_controller: class: Dpc\Tutorial\Controller\PostController
![Page 51: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/51.jpg)
Remove @Route annotationsInstead: define actual routes in the bundle's routing.yml
file.Use the service id of the controller instead of its class name.
dpc_tutorial.post_controller.show: path: /post/{id}/show defaults: _controller: dpc_tutorial.post_controller:showAction
![Page 52: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/52.jpg)
Remove @Template annotationsInject the templating service instead and use it to render
the template.use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Templating\EngineInterface;
class PostController{ public function __construct(EngineInterface $templating) { $this->templating = $templating; }
public function showAction(Post $post) { return new Response( $this->templating->render( 'DpcTutorialBundle:Post:show.html.twig', array('post' => $post) ) ); }}
![Page 53: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/53.jpg)
services: dpc_tutorial.post_controller: class: Dpc\Tutorial\Controller\PostController arguments: - @templating
![Page 54: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/54.jpg)
What about the
Templates
![Page 55: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/55.jpg)
Move the template to the libraryE.g. from Dpc/Bundle/TutorialBundle/Resources/views/Post/show.html.twig to
Dpc/Tutorial/View/Post/show.html.twig
![Page 56: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/56.jpg)
Change the template reference$this->templating->render( '@DpcTutorial/Post/show.html.twig', array('post' => $post))
![Page 57: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/57.jpg)
Register the new location of thetemplates
# in config.ymltwig: ... paths: "%kernel.root_dir%/../src/Dpc/Tutorial/View": DpcTutorial
Documentation » The Cookbook » Templating » How to use and Register namespaced Twig Paths
![Page 58: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/58.jpg)
Well...We don't want to ask users to modify their config.yml!
![Page 59: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/59.jpg)
Let's prependconfiguration
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
class DpcTutorialExtension extends ConfigurableExtension implements PrependExtensionInterface{ ... public function prepend(ContainerBuilder $container) { $bundles = $container->getParameter('kernel.bundles'); if (!isset($bundles['TwigBundle'])) { return; }
$container->prependExtensionConfig( 'twig', array( 'paths' => array( "%kernel.root_dir%/../src/Dpc/Tutorial/View" => 'DpcTutorial' ) ) ); }}
Documentation » The Cookbook » Bundles » How to simplify configuration of multiple Bundles
![Page 60: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/60.jpg)
One last step!The action's $post argument relies on something called
.param convertersThose convert the id from the route to the actual Post
entity.This is actually Symfony framework-specific behavior
![Page 61: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/61.jpg)
Rewrite the controller to make useof a repository
use Doctrine\Common\Persistence\ObjectRepository;
class PostController{ public function __construct(..., ObjectRepository $postRepository) { ... $this->postRepository = $postRepository; }
public function showAction($id) { $post = $this->postRepository->find($id); if (!($post instanceof Post)) { throw new NotFoundHttpException(); } ... }}
![Page 62: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/62.jpg)
services: dpc_tutorial.post_controller: class: Dpc\Tutorial\Controller\PostController arguments: - @templating - @dpc_tutorial.post_repository
dpc_tutorial.post_repository: class: Doctrine\Common\Persistence\ObjectRepository factory_service: doctrine factory_method: getRepository arguments: - Dpc\Tutorial\Model\Post
![Page 63: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/63.jpg)
What do we have now?
![Page 64: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/64.jpg)
Reusable templates
![Page 65: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/65.jpg)
Reusable controllersThey work with Silex too!
Who would have though that was possible?
![Page 66: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/66.jpg)
Console commands
![Page 67: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/67.jpg)
Create a console commandUse app/console generate:console-command
Make it insert a new post in the database.It takes one argument: the post's title.
![Page 68: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/68.jpg)
Something like thisuse Dpc\Tutorial\Model\Post;use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;use Symfony\Component\Console\Input\InputArgument;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Output\OutputInterface;
class CreatePostCommand extends ContainerAwareCommand{ protected function configure() { $this ->setName('post:create') ->addArgument('title', InputArgument::REQUIRED); }
protected function execute(InputInterface $input, OutputInterface $output) { $manager = $this->getContainer() ->get('doctrine') ->getManagerForClass('Dpc\Tutorial\Model\Post');
$post = new Post(); $post->setTitle($input->getArgument('title')); $manager->persist($post); $manager->flush();
$output->writeln('New post created: '.$post->getTitle()); }}
![Page 69: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/69.jpg)
Why is it inside a bundle?Because it is automatically registered when it's in the
Command directory.
![Page 70: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/70.jpg)
So let's move it out!
![Page 71: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/71.jpg)
Move the command to the library
![Page 72: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/72.jpg)
Create a service for itGive it the tag console.command.
Or else it won't be recognized anymore!
services: dpc_tutorial.create_post_command: class: Dpc\Tutorial\Command\CreatePostCommand tags: - { name: console.command }
![Page 73: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/73.jpg)
What about ContainerAware?It couples our command to the Symfony framework.
Which is not needed at all.
![Page 74: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/74.jpg)
Extend from CommandThen inject dependencies instead of fetching them from the
container.use Doctrine\Common\Persistence\ManagerRegistry;
class CreatePostCommand extends Command{ private $doctrine;
public function __construct(ManagerRegistry $doctrine) { parent::__construct();
$this->doctrine = $doctrine; } ... protected function execute(InputInterface $input, OutputInterface $output) { $manager = $this->doctrine->getManager(); ... }}
![Page 75: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/75.jpg)
services: dpc_tutorial.create_post_command: class: Dpc\Tutorial\Command\CreatePostCommand arguments: - @doctrine tags: - { name: console.command }
![Page 76: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/76.jpg)
What do we have?Explicit dependenciesReusable commands that works in all projects that use theSymfony Console Component (like )A bit less magic (no auto-registering commands)Which means now we can put anything we want in theCommand directory
Cilex
![Page 77: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/77.jpg)
Testing abundle
Or: testing configuration
![Page 78: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/78.jpg)
The Configuration class
![Page 79: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/79.jpg)
I don't get it!
![Page 80: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/80.jpg)
I don't trust myself with it.And when I don't trust myself, I write tests
![Page 81: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/81.jpg)
SymfonyConfigTestOn GitHub: SymfonyConfigTest
{ "require-dev": { "matthiasnoback/symfony-config-test": "~0.1" }}
![Page 82: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/82.jpg)
Prepare a test suite for yourConfiguration class
Create a directory Tests/DependencyInjection insidethe bundle.In that directory create a new class:ConfigurationTest.
![Page 83: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/83.jpg)
Create the test classThe ConfigurationTest should extend fromAbstractConfigurationTestCaseImplement the missing method getConfiguration()namespace Dpc\Bundle\TutorialBundle\Tests\DependencyInjection;
use Dpc\Bundle\TutorialBundle\DependencyInjection\Configuration;use Matthias\SymfonyConfigTest\PhpUnit\AbstractConfigurationTestCase;
class ConfigurationTest extends AbstractConfigurationTestCase{ protected function getConfiguration() { return new Configuration(); }}
![Page 84: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/84.jpg)
Desired structure in config.ymldpc_tutorial: # host should be a required key host: localhost
![Page 85: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/85.jpg)
A required value: hostTest first
/** * @test */public function the_host_key_is_required(){ $this->assertConfigurationIsInvalid( array( array() ), 'host' );}
If we provide no values at all, we expect an exceptioncontaining "host".
![Page 86: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/86.jpg)
See it failbin/phpunit -c app
![Page 87: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/87.jpg)
Make the test pass$rootNode ->children() ->scalarNode('host') ->isRequired() ->end() ->end();
![Page 88: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/88.jpg)
Trial and errorYou're done when the test passes!
![Page 89: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/89.jpg)
Repeated configuration valuesDesired structure in config.yml
dpc_tutorial: servers: a: host: server-a.nobacksoffice.nl port: 2730 b: host: server-b.nobacksoffice.nl port: 2730 ...
host and port are required keys for each serverconfiguration
![Page 90: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/90.jpg)
Test first/** * @test */public function host_is_required_for_each_server(){ $this->assertConfigurationIsInvalid( array( array( 'servers' => array( 'a' => array() ) ) ), 'host' );}
![Page 91: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/91.jpg)
Run the testsbin/phpunit -c app
![Page 92: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/92.jpg)
Write the code$rootNode ->children() ->arrayNode('servers') ->useAttributeAsKey('name') ->prototype('array') ->children() ->scalarNode('host') ->isRequired() ->end()
![Page 93: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/93.jpg)
Run the tests
![Page 94: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/94.jpg)
Test firstRepeat these steps for port
Make sure your test first failsThen you add some codeThen the test should pass
![Page 95: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/95.jpg)
Merging config values$this->assertConfigurationIsInvalid( array( array( ... // e.g. values from config.yml ), array( ... // e.g. values from config_dev.yml ) ), 'host');
![Page 96: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/96.jpg)
Disable mergingTest first
/** * @test */public function server_configurations_are_not_merged(){ $this->assertProcessedConfigurationEquals( array( array( 'servers' => array( 'a' => array('host' => 'host-a', 'port' => 1) ) ), array( 'servers' => array( 'b' => array('host' => 'host-b', 'port' => 2) ) ) ), array( 'servers' => array( 'b' => array('host' => 'host-b', 'port' => 2) ) ) );}
![Page 97: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/97.jpg)
Add some code$rootNode ->children() ->arrayNode('servers') ->useAttributeAsKey('name') // don't reindex the array ->prototype('array') // means: repeatable ->children() ->scalarNode('host')->end() ->scalarNode('port')->end() ->end() ->end() ->end() ->end();
![Page 98: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/98.jpg)
Run the testsbin/phpunit -c app
![Page 99: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/99.jpg)
Disable deep mergingValues from different configuration sources should not be
merged.$rootNode ->children() ->arrayNode('servers') ->performNoDeepMerging() ... ->end() ->end();
![Page 100: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/100.jpg)
Advantages of TDD forConfiguration classes
We gradually approach our goal.We immediately get feedback on what's wrong.We can test different configuration values withoutchanging config.yml manually.We can make sure the user gets very specific errormessages about wrong configuration values.Learn more about all the options by reading the
.offical
documentation of the Config component
![Page 101: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/101.jpg)
Testing Extensionclasses
dpc_tutorial: servers: a: host: localhost port: 2730
Should give us a dpc_tutorial.a_server service withhost and port as constructor arguments.
![Page 102: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/102.jpg)
Create a test class for your extensionDirectory: Tests/DependencyInjectionClass name: [NameOfTheExtension]TestClass should extend AbstractExtensionTestCaseImplement getContainerExtensions(): return aninstance of your extension classnamespace Dpc\Bundle\TutorialBundle\Tests\DependencyInjection;
use Dpc\Bundle\TutorialBundle\DependencyInjection\DpcTutorialExtension;use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
class DpcTutorialExtensionTest extends AbstractExtensionTestCase{ protected function getContainerExtensions() { return array( new DpcTutorialExtension() ); }}
![Page 103: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/103.jpg)
Test first/** * @test */public function it_creates_service_definitions_for_each_server(){ $this->load( array( 'servers' => array( 'a' => array('host' => 'host-a', 'port' => 123), 'b' => array('host' => 'host-b', 'port' => 234) ) ) );
$this->assertContainerBuilderHasServiceDefinitionWithArgument( 'dpc_tutorial.a_server', 0, 'host-a' ); $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'dpc_tutorial.a_server', 1, 123 );
$this->assertContainerBuilderHasServiceDefinitionWithArgument( 'dpc_tutorial.b_server', 0, 'host-b' ); $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'dpc_tutorial.b_server', 1, 234 );}
![Page 104: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/104.jpg)
See it fail
![Page 105: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/105.jpg)
Write the codeuse Symfony\Component\DependencyInjection\Definition;
public function load(array $configs, ContainerBuilder $container){ $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs);
foreach ($config['servers'] as $name => $serverConfig) { $serverDefinition = new Definition(); $serverDefinition->setArguments( array( $serverConfig['host'], $serverConfig['port'], ) );
$container->setDefinition( 'dpc_tutorial.' . $name . '_server', $serverDefinition ); }}
![Page 106: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/106.jpg)
See it pass
![Page 107: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/107.jpg)
Refactor!public function load(array $configs, ContainerBuilder $container){ $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs);
$this->configureServers($container, $config['servers']);}
private function configureServers(ContainerBuilder $container, array $servers){ foreach ($servers as $name => $server) { $this->configureServer($container, $name, $server['host'], $server['port']); }}
private function configureServer(ContainerBuilder $container, $name, $host, $port){ $serverDefinition = new Definition(null, array($host, $port)); $container->setDefinition( 'dpc_tutorial.' . $name . '_server', $serverDefinition );}
![Page 108: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/108.jpg)
Shortcuts versus the Real dealThe base class provides some useful shortcutsTo get the most out of testing your extension:Read all about classes like Definition in the officialdocumentation
![Page 109: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/109.jpg)
Patterns of Dependency Injection
![Page 110: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/110.jpg)
A Bundlecalled Bandle
![Page 111: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/111.jpg)
I thought a bundle is just a class thatimplements BundleInterface...
![Page 112: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/112.jpg)
Why the suffix isnecessary
abstract class Bundle extends ContainerAware implements BundleInterface{ public function getContainerExtension() { ... $basename = preg_replace('/Bundle$/', '', $this->getName());
$class = $this->getNamespace() . '\\DependencyInjection\\' . $basename . 'Extension';
if (class_exists($class)) { $extension = new $class(); ... } ... }} Line 6: '/Bundle$/'
![Page 113: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/113.jpg)
But: no need to guess, youalready know which class
it is, right?
![Page 114: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/114.jpg)
Override thegetContainerExtension() of your
bundle classThen make it return an instance of your extension class.
![Page 115: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/115.jpg)
use Dpc\Bundle\TutorialBundle\DependencyInjection\DpcTutorialExtension;
class DpcTutorialBundle extends Bundle{ public function getContainerExtension() { return new DpcTutorialExtension(); }}
Now the extension doesn't need to be in theDependencyInjection directory anymore!
![Page 116: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/116.jpg)
It still needs to have the Extension suffix though...
![Page 117: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/117.jpg)
Open the Extension class (from theHttpKernel component)Take a look at the getAlias() method.
![Page 118: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/118.jpg)
abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface{ public function getAlias() { $className = get_class($this); if (substr($className, -9) != 'Extension') { throw new BadMethodCallException( 'This extension does not follow the naming convention;' . 'you must overwrite the getAlias() method.' ); } $classBaseName = substr(strrchr($className, '\\'), 1, -9);
return Container::underscore($classBaseName); }}
![Page 119: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/119.jpg)
The alias is used to find out which configuration belongs towhich bundle:
# in config.ymldpc_tutorial: ...
By convention it's the lowercase underscored bundle name.
![Page 120: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/120.jpg)
But what happens when Irename the bundle?
The alias changes too, which means configuration inconfig.yml won't be recognized anymore.
![Page 121: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/121.jpg)
Also:The extension needs to be renamed too, because of the
naming conventions...DpcTutorialBundle, DpcTutorialExtension, dpc_tutorial
NobackTestBundle, NobackTestExtension, noback_test
![Page 122: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/122.jpg)
So: open your extension classOverride the getAlias() method.
Make it return the alias of your extension (a string).E.g. DpcTutorialBundle::getAlias() returns
dpc_tutorial.
![Page 123: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/123.jpg)
class DpcTutorialExtension extends Extension{ public function getAlias() { return 'dpc_tutorial'; }}
![Page 124: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/124.jpg)
But now we have someduplication of information
The alias is also mentioned inside the Configurationclass.
![Page 125: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/125.jpg)
class Configuration implements ConfigurationInterface{ public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('dpc_tutorial'); ...
return $treeBuilder; }}
![Page 126: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/126.jpg)
Modify extension and configurationHow can we make sure that the name of the root node inthe configuration class is the same as the alias returned by
getAlias()?
![Page 127: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/127.jpg)
class Configuration implements ConfigurationInterface{ private $alias;
public function __construct($alias) { $this->alias = $alias; }
public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root($this->alias); ... }}
$configuration = new Configuration($this->getAlias());
![Page 128: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/128.jpg)
This introduces a bugRun app/console config:dump-reference
[extension-alias]
![Page 129: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/129.jpg)
![Page 130: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/130.jpg)
Open the Extension classTake the one from the DependencyInjection
component.
![Page 131: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/131.jpg)
public function getConfiguration(array $config, ContainerBuilder $container){ $reflected = new \ReflectionClass($this); $namespace = $reflected->getNamespaceName(); $class = $namespace.'\\Configuration'; if (class_exists($class)) { $r = new \ReflectionClass($class); $container->addResource(new FileResource($r->getFileName()));
if (!method_exists($class, '__construct')) { $configuration = new $class();
return $configuration; } }}
Our Configuration class has a constructor...
![Page 132: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/132.jpg)
Override getConfiguration() inyour extension
Also: make sure only one instance of Configuration iscreated in the extension class.
![Page 133: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/133.jpg)
class DpcTutorialExtension extends Extension{ public function load(array $configs, ContainerBuilder $container) { $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); ... }
public function getConfiguration(array $config, ContainerBuilder $container) { return new Configuration($this->getAlias()); } ...}
Now we are allowed to rename Configuration or put itsomewhere else entirely!
![Page 134: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/134.jpg)
Some last improvementExtend from ConfigurableExtension.
![Page 135: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/135.jpg)
abstract class ConfigurableExtension extends Extension{ final public function load(array $configs, ContainerBuilder $container) { $this->loadInternal( $this->processConfiguration( $this->getConfiguration($configs, $container), $configs ), $container ); }
abstract protected function loadInternal(array $mergedConfig, ContainerBuilder $container}
![Page 136: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/136.jpg)
It will save you a call to processConfiguration().
![Page 137: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/137.jpg)
class DpcTutorialExtension extends ConfigurableExtension{ public function loadInternal(array $mergedConfig, ContainerBuilder $container) { // $mergedConfig has already been processed
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config' $loader->load('services.xml'); } ...}
![Page 138: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/138.jpg)
We introduced flexibility...By hard-coding the alias
And by skipping all the magic stuff
Now we canChange *Bundle into *Bandle
Change *Extension into *PluginChange Configuration into Complexity
If we want...
![Page 140: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/140.jpg)
€ 15,00
![Page 141: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/141.jpg)
I’m impressed. — Robert C. Martin
leanpub.com/principles-of-php-package-design/c/dpc2014
![Page 142: High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014](https://reader034.fdocuments.net/reader034/viewer/2022050801/53f8d0318d7f7253318b48b4/html5/thumbnails/142.jpg)
Feedbackjoind.in/10849
Twitter@matthiasnoback