diff --git a/book/controller.rst b/book/controller.rst index 6a451037e7b..8e0db4b8bc7 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -170,10 +170,16 @@ to the controller: .. code-block:: php // app/config/routing.php + use Symfony\Component\Routing\Route; + use Symfony\Component\Routing\RouteCollection; + + $collection = new RouteCollection(); $collection->add('hello', new Route('/hello/{name}', array( '_controller' => 'AcmeHelloBundle:Hello:index', ))); + return $collection; + Going to ``/hello/ryan`` now executes the ``HelloController::indexAction()`` controller and passes in ``ryan`` for the ``$name`` variable. Creating a "page" means simply creating a controller method and associated route. @@ -257,11 +263,17 @@ example: .. code-block:: php // app/config/routing.php + use Symfony\Component\Routing\Route; + use Symfony\Component\Routing\RouteCollection; + + $collection = new RouteCollection(); $collection->add('hello', new Route('/hello/{firstName}/{lastName}', array( '_controller' => 'AcmeHelloBundle:Hello:index', 'color' => 'green', ))); + return $collection; + The controller for this can take several arguments:: public function indexAction($firstName, $lastName, $color) diff --git a/book/doctrine.rst b/book/doctrine.rst index 84a54b49017..996dcdbee9f 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -85,7 +85,7 @@ information. By convention, this information is usually configured in an xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:doctrine="http://symfony.com/schema/dic/doctrine" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> + http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> + password="%database_password%" /> - .. code-block:: php @@ -175,16 +173,14 @@ for you: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:doctrine="http://symfony.com/schema/dic/doctrine" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> - - - - + http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> + + + .. code-block:: php @@ -319,17 +315,17 @@ in a number of different formats including YAML, XML or directly inside the + 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"> - + - - - + + + @@ -826,13 +822,15 @@ To do this, add the name of the repository class to your mapping definition: + 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"> + + - - + @@ -945,17 +943,18 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: xml + + http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - + mapped-by="category" /> + + http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - - + join-column="category"> + + @@ -1306,6 +1304,8 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: php-annotations + // src/Acme/StoreBundle/Entity/Product.php + /** * @ORM\PrePersist */ @@ -1328,16 +1328,15 @@ the current date, only when the entity is first persisted (i.e. inserted): + 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"> - - - - + + + + diff --git a/book/forms.rst b/book/forms.rst index 0c834d85657..9d5a7ce543f 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -129,7 +129,7 @@ corresponding to the ``task`` and ``dueDate`` properties of the ``Task`` class. You've also assigned each a "type" (e.g. ``text``, ``date``), which, among other things, determines which HTML form tag(s) is rendered for that field. -Finally, you added a submit button with a custom label for submitting the form to +Finally, you added a submit button with a custom label for submitting the form to the server. .. versionadded:: 2.3 @@ -1126,20 +1126,21 @@ easy to use in your application. + - - - + + + + .. code-block:: php // src/Acme/TaskBundle/Resources/config/services.php - use Symfony\Component\DependencyInjection\Definition; - $container ->register( 'acme_demo.form.type.task', @@ -1590,13 +1591,13 @@ file: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:twig="http://symfony.com/schema/dic/twig" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> + http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - - AcmeTaskBundle:Form:fields.html.twig - - + + AcmeTaskBundle:Form:fields.html.twig + + @@ -1676,7 +1677,7 @@ file: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> diff --git a/book/http_cache.rst b/book/http_cache.rst index 2698920121a..dc77604fd63 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -763,6 +763,12 @@ both worlds. In other words, by using both expiration and validation, you can instruct the cache to serve the cached content, while checking back at some interval (the expiration) to verify that the content is still valid. +.. tip:: + + You can also define HTTP caching headers for expiration and validation by using + annotations. See the + :doc:`FrameworkExtraBundle documentation `. + .. index:: pair: Cache; Configuration @@ -866,8 +872,7 @@ First, to use ESI, be sure to enable it in your application configuration: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony - http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -880,7 +885,7 @@ First, to use ESI, be sure to enable it in your application configuration: // app/config/config.php $container->loadFromExtension('framework', array( // ... - 'esi' => array('enabled' => true), + 'esi' => array('enabled' => true), )); Now, suppose you have a page that is relatively static, except for a news @@ -988,8 +993,9 @@ that must be enabled in your configuration: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:doctrine="http://symfony.com/schema/dic/framework" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index c3b72cfdf65..8db213faa9b 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -433,6 +433,7 @@ by adding an entry for ``/contact`` to your routing configuration file: .. code-block:: xml + add('contact', new Route('/contact', array( diff --git a/book/internals.rst b/book/internals.rst index 6c9667125a5..939b0f56839 100644 --- a/book/internals.rst +++ b/book/internals.rst @@ -601,7 +601,7 @@ the configuration for the development environment: xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/webprofiler http://symfony.com/schema/dic/webprofiler/webprofiler-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -611,9 +611,7 @@ the configuration for the development environment: + intercept-redirects="true" /> .. code-block:: php @@ -627,7 +625,6 @@ the configuration for the development environment: $container->loadFromExtension('web_profiler', array( 'toolbar' => true, 'intercept_redirects' => true, - 'verbose' => true, )); When ``only_exceptions`` is set to ``true``, the profiler only collects data @@ -657,18 +654,18 @@ If you enable the web profiler, you also need to mount the profiler routes: + prefix="/_profiler" /> .. code-block:: php - $collection->addCollection( - $loader->import( - "@WebProfilerBundle/Resources/config/routing/profiler.xml" - ), - '/_profiler' - ); + use Symfony\Component\Routing\RouteCollection; + + $profiler = $loader->import('@WebProfilerBundle/Resources/config/routing/profiler.xml'); + $profiler->addPrefix('/_profiler'); + + $collection = new RouteCollection(); + $collection->addCollection($profiler); As the profiler adds some overhead, you might want to enable it only under certain circumstances in the production environment. The ``only_exceptions`` diff --git a/book/page_creation.rst b/book/page_creation.rst index 3d251aacc99..da550fa60e4 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -61,19 +61,20 @@ cache. .. index:: single: Page creation; Example -The "Hello Symfony!" Page -------------------------- +The "Random Number" Page +------------------------ -Start by building a spin-off of the classic "Hello World!" application. When -you're finished, the user will be able to get a personal greeting (e.g. "Hello Symfony") -by going to the following URL: +In this chapter, you'll develop an application that can generate random numbers. +When you're finished, the user will be able to get a random number between ``1`` +and the upper limit set by the URL: .. code-block:: text - http://localhost/app_dev.php/hello/Symfony + http://localhost/app_dev.php/random/100 -Actually, you'll be able to replace ``Symfony`` with any other name to be -greeted. To create the page, follow the simple two-step process. +Actually, you'll be able to replace ``100`` with any other number to generate +numbers up to that upper limit. To create the page, follow the simple two-step +process. .. note:: @@ -97,15 +98,21 @@ A bundle is nothing more than a directory that houses everything related to a specific feature, including PHP classes, configuration, and even stylesheets and JavaScript files (see :ref:`page-creation-bundles`). -To create a bundle called ``AcmeHelloBundle`` (a play bundle that you'll +Depending on the way you installed Symfony, you may already have a bundle called +``AcmeDemoBundle``. Browse the ``src/`` directory of your project and check +if there is a ``DemoBundle/`` directory inside an ``Acme/`` directory. If those +directories already exist, skip the rest of this section and go directly to +create the route. + +To create a bundle called ``AcmeDemoBundle`` (a play bundle that you'll build in this chapter), run the following command and follow the on-screen instructions (use all of the default options): .. code-block:: bash - $ php app/console generate:bundle --namespace=Acme/HelloBundle --format=yml + $ php app/console generate:bundle --namespace=Acme/DemoBundle --format=yml -Behind the scenes, a directory is created for the bundle at ``src/Acme/HelloBundle``. +Behind the scenes, a directory is created for the bundle at ``src/Acme/DemoBundle``. A line is also automatically added to the ``app/AppKernel.php`` file so that the bundle is registered with the kernel:: @@ -114,7 +121,7 @@ the bundle is registered with the kernel:: { $bundles = array( ..., - new Acme\HelloBundle\AcmeHelloBundle(), + new Acme\DemoBundle\AcmeDemoBundle(), ); // ... @@ -132,15 +139,15 @@ located at ``app/config/routing.yml``. Like all configuration in Symfony2, you can also choose to use XML or PHP out of the box to configure routes. If you look at the main routing file, you'll see that Symfony already added -an entry when you generated the ``AcmeHelloBundle``: +an entry when you generated the ``AcmeDemoBundle``: .. configuration-block:: .. code-block:: yaml # app/config/routing.yml - acme_hello: - resource: "@AcmeHelloBundle/Resources/config/routing.yml" + acme_website: + resource: "@AcmeDemoBundle/Resources/config/routing.yml" prefix: / .. code-block:: xml @@ -152,7 +159,8 @@ an entry when you generated the ``AcmeHelloBundle``: xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - @@ -160,21 +168,27 @@ an entry when you generated the ``AcmeHelloBundle``: // app/config/routing.php use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; + + $acmeDemo = $loader->import('@AcmeDemoBundle/Resources/config/routing.php'); + $acmeDemo->addPrefix('/'); $collection = new RouteCollection(); - $collection->addCollection( - $loader->import('@AcmeHelloBundle/Resources/config/routing.php'), - '/' - ); + $collection->addCollection($acmeDemo); return $collection; This entry is pretty basic: it tells Symfony to load routing configuration -from the ``Resources/config/routing.yml`` file that lives inside the ``AcmeHelloBundle``. +from the ``Resources/config/routing.yml`` (``routing.xml`` or ``routing.php`` +in the XML and PHP code example respectively) file that lives inside the ``AcmeDemoBundle``. This means that you place routing configuration directly in ``app/config/routing.yml`` or organize your routes throughout your application, and import them from here. +.. note:: + + You are not limited to load routing configurations that are of the same + format. For example, you could also load a YAML file in an XML configuration + and vice versa. + Now that the ``routing.yml`` file from the bundle is being imported, add the new route that defines the URL of the page that you're about to create: @@ -182,34 +196,34 @@ the new route that defines the URL of the page that you're about to create: .. code-block:: yaml - # src/Acme/HelloBundle/Resources/config/routing.yml - hello: - path: /hello/{name} - defaults: { _controller: AcmeHelloBundle:Hello:index } + # src/Acme/DemoBundle/Resources/config/routing.yml + random: + path: /random/{limit} + defaults: { _controller: AcmeDemoBundle:Random:index } .. code-block:: xml - + - - AcmeHelloBundle:Hello:index + + AcmeDemoBundle:Random:index .. code-block:: php - // src/Acme/HelloBundle/Resources/config/routing.php + // src/Acme/DemoBundle/Resources/config/routing.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $collection = new RouteCollection(); - $collection->add('hello', new Route('/hello/{name}', array( - '_controller' => 'AcmeHelloBundle:Hello:index', + $collection->add('random', new Route('/random/{limit}', array( + '_controller' => 'AcmeDemoBundle:Random:index', ))); return $collection; @@ -217,10 +231,10 @@ the new route that defines the URL of the page that you're about to create: The routing consists of two basic pieces: the ``path``, which is the URL that this route will match, and a ``defaults`` array, which specifies the controller that should be executed. The placeholder syntax in the path -(``{name}``) is a wildcard. It means that ``/hello/Ryan``, ``/hello/Fabien`` -or any other similar URL will match this route. The ``{name}`` placeholder +(``{limit}``) is a wildcard. It means that ``/number/10``, ``/number/327`` +or any other similar URL will match this route. The ``{limit}`` placeholder parameter will also be passed to the controller so that you can use its value -to personally greet the user. +to generate the proper random number. .. note:: @@ -231,20 +245,20 @@ to personally greet the user. Step 2: Create the Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a URL such as ``/hello/Ryan`` is handled by the application, the ``hello`` -route is matched and the ``AcmeHelloBundle:Hello:index`` controller is executed +When a URL such as ``/random/10`` is handled by the application, the ``random`` +route is matched and the ``AcmeDemoBundle:Random:index`` controller is executed by the framework. The second step of the page-creation process is to create that controller. -The controller - ``AcmeHelloBundle:Hello:index`` is the *logical* name of +The controller - ``AcmeDemoBundle:Random:index`` is the *logical* name of the controller, and it maps to the ``indexAction`` method of a PHP class -called ``Acme\HelloBundle\Controller\HelloController``. Start by creating this file -inside your ``AcmeHelloBundle``:: +called ``Acme\DemoBundle\Controller\RandomController``. Start by creating this +file inside your ``AcmeDemoBundle``:: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/Acme/DemoBundle/Controller/RandomController.php + namespace Acme\DemoBundle\Controller; - class HelloController + class RandomController { } @@ -254,19 +268,19 @@ to build and prepare the resource being requested. Except in some advanced cases, the end product of a controller is always the same: a Symfony2 ``Response`` object. -Create the ``indexAction`` method that Symfony will execute when the ``hello`` +Create the ``indexAction`` method that Symfony will execute when the ``random`` route is matched:: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/Acme/DemoBundle/Controller/RandomController.php + namespace Acme\DemoBundle\Controller; use Symfony\Component\HttpFoundation\Response; - class HelloController + class RandomController { - public function indexAction($name) + public function indexAction($limit) { - return new Response('Hello '.$name.'!'); + return new Response('Number: '.rand(1, $limit).''); } } @@ -276,11 +290,11 @@ page in this example). Congratulations! After creating only a route and a controller, you already have a fully-functional page! If you've setup everything correctly, your -application should greet you: +application should generate a random number for you: .. code-block:: text - http://localhost/app_dev.php/hello/Ryan + http://localhost/app_dev.php/random/10 .. _book-page-creation-prod-cache-clear: @@ -291,7 +305,7 @@ application should greet you: .. code-block:: text - http://localhost/app.php/hello/Ryan + http://localhost/app.php/random/10 If you get an error, it's likely because you need to clear your cache by running: @@ -318,24 +332,26 @@ of writing the HTML inside the controller, render a template instead: .. code-block:: php :linenos: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/Acme/DemoBundle/Controller/RandomController.php + namespace Acme\DemoBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - class HelloController extends Controller + class RandomController extends Controller { - public function indexAction($name) + public function indexAction($limit) { + $number = rand(1, $limit); + return $this->render( - 'AcmeHelloBundle:Hello:index.html.twig', - array('name' => $name) + 'AcmeDemoBundle:Random:index.html.twig', + array('number' => $number) ); // render a PHP template instead // return $this->render( - // 'AcmeHelloBundle:Hello:index.html.php', - // array('name' => $name) + // 'AcmeDemoBundle:Random:index.html.php', + // array('number' => $number) // ); } } @@ -358,7 +374,7 @@ By default, Symfony2 supports two different templating languages: classic PHP templates and the succinct but powerful `Twig`_ templates. Don't be alarmed - you're free to choose either or even both in the same project. -The controller renders the ``AcmeHelloBundle:Hello:index.html.twig`` template, +The controller renders the ``AcmeDemoBundle:Random:index.html.twig`` template, which uses the following naming convention: **BundleName**:**ControllerName**:**TemplateName** @@ -368,7 +384,7 @@ location using the following convention. **/path/to/BundleName**/Resources/views/**ControllerName**/**TemplateName** -In this case, ``AcmeHelloBundle`` is the bundle name, ``Hello`` is the +In this case, ``AcmeDemoBundle`` is the bundle name, ``Random`` is the controller, and ``index.html.twig`` the template: .. configuration-block:: @@ -376,19 +392,19 @@ controller, and ``index.html.twig`` the template: .. code-block:: jinja :linenos: - {# src/Acme/HelloBundle/Resources/views/Hello/index.html.twig #} + {# src/Acme/DemoBundle/Resources/views/Random/index.html.twig #} {% extends '::base.html.twig' %} {% block body %} - Hello {{ name }}! + Number: {{ number }} {% endblock %} .. code-block:: html+php - + extend('::base.html.php') ?> - Hello escape($name) ?>! + Number: escape($number) ?> Step through the Twig template line-by-line: @@ -509,16 +525,16 @@ use a Kernel class, ``AppKernel``, to bootstrap the application. .. code-block:: text - http://localhost/app.php/hello/Ryan + http://localhost/app.php/random/10 The front controller, ``app.php``, is executed and the "internal:" URL - ``/hello/Ryan`` is routed internally using the routing configuration. + ``/random/10`` is routed internally using the routing configuration. By using Apache ``mod_rewrite`` rules, you can force the ``app.php`` file to be executed without needing to specify it in the URL: .. code-block:: text - http://localhost/hello/Ryan + http://localhost/random/10 Though front controllers are essential in handling every request, you'll rarely need to modify or even think about them. They'll be mentioned again @@ -570,9 +586,9 @@ You'll learn more about each of these directories in later chapters. .. code-block:: text Class Name: - Acme\HelloBundle\Controller\HelloController + Acme\DemoBundle\Controller\RandomController Path: - src/Acme/HelloBundle/Controller/HelloController.php + src/Acme/DemoBundle/Controller/RandomController.php The Source (``src``) Directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -722,10 +738,10 @@ Bundle Directory Structure The directory structure of a bundle is simple and flexible. By default, the bundle system follows a set of conventions that help to keep code consistent -between all Symfony2 bundles. Take a look at ``AcmeHelloBundle``, as it contains +between all Symfony2 bundles. Take a look at ``AcmeDemoBundle``, as it contains some of the most common elements of a bundle: -* ``Controller/`` contains the controllers of the bundle (e.g. ``HelloController.php``); +* ``Controller/`` contains the controllers of the bundle (e.g. ``RandomController.php``); * ``DependencyInjection/`` holds certain dependency injection extension classes, which may import service configuration, register compiler passes or more @@ -791,9 +807,9 @@ format you prefer: xmlns:framework="http://symfony.com/schema/dic/symfony" xmlns:twig="http://symfony.com/schema/dic/twig" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd - http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd + http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> + @@ -812,16 +828,16 @@ format you prefer: .. code-block:: php + // app/config/config.php $this->import('parameters.yml'); $this->import('security.yml'); $container->loadFromExtension('framework', array( - 'secret' => '%secret%', - 'router' => array( + 'secret' => '%secret%', + 'router' => array( 'resource' => '%kernel.root_dir%/config/routing.php', ), // ... - ), )); // Twig Configuration @@ -907,14 +923,14 @@ the application via the development front controller: .. code-block:: text - http://localhost/app_dev.php/hello/Ryan + http://localhost/app_dev.php/random/10 If you'd like to see how your application will behave in the production environment, call the ``prod`` front controller instead: .. code-block:: text - http://localhost/app.php/hello/Ryan + http://localhost/app.php/random/10 Since the ``prod`` environment is optimized for speed; the configuration, routing and Twig templates are compiled into flat PHP classes and cached. @@ -985,20 +1001,19 @@ the configuration file for the ``dev`` environment. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + + .. code-block:: php diff --git a/book/propel.rst b/book/propel.rst index 6b239374e64..b48b5f3a5a1 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -91,28 +91,31 @@ of your ``AcmeStoreBundle``: .. code-block:: xml - + defaultIdMethod="native"> + - - + + - - + size="100" /> + + +
@@ -305,28 +308,35 @@ Start by adding the ``category`` definition in your ``schema.xml``: .. code-block:: xml - + - - - - - @@ -335,13 +345,15 @@ Start by adding the ``category`` definition in your ``schema.xml``:
- - diff --git a/book/routing.rst b/book/routing.rst index 3607dc16b7c..8e4ef310375 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -169,7 +169,7 @@ file: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -206,12 +206,14 @@ A basic route consists of just two parts: the ``path`` to match and a .. code-block:: yaml + # app/config/routing.yml _welcome: path: / defaults: { _controller: AcmeDemoBundle:Main:homepage } .. code-block:: xml + `); -* ``_locale``: Used to set the locale on the request (:ref:`read more `); +* ``_locale``: Used to set the locale on the request (:ref:`read more `). .. index:: single: Routing; Controllers @@ -1121,7 +1151,8 @@ instead of simply ``/hello/{name}``: xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> - @@ -1130,13 +1161,10 @@ instead of simply ``/hello/{name}``: // app/config/routing.php use Symfony\Component\Routing\RouteCollection; - $collection = new RouteCollection(); - - $acmeHello = $loader->import( - "@AcmeHelloBundle/Resources/config/routing.php" - ); + $acmeHello = $loader->import('@AcmeHelloBundle/Resources/config/routing.php'); $acmeHello->addPrefix('/admin'); + $collection = new RouteCollection(); $collection->addCollection($acmeHello); return $collection; diff --git a/book/security.rst b/book/security.rst index 0572bc13330..97df4d813fa 100644 --- a/book/security.rst +++ b/book/security.rst @@ -123,7 +123,7 @@ authentication (i.e. the old-school username/password box): 'ryan' => array( 'password' => 'ryanpass', 'roles' => 'ROLE_USER', - ), + ), 'admin' => array( 'password' => 'kitten', 'roles' => 'ROLE_ADMIN', @@ -457,7 +457,7 @@ Next, create the controller that will display the login form:: } else { $error = ''; } - + // last username entered by the user $lastUsername = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME); @@ -601,17 +601,26 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: yaml + # app/config/security.yml + + # ... access_control: - { path: ^/, roles: ROLE_ADMIN } .. code-block:: xml + + + .. code-block:: php + // app/config/security.php + + // ... 'access_control' => array( array('path' => '^/', 'role' => 'ROLE_ADMIN'), ), @@ -622,12 +631,18 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: yaml + # app/config/security.yml + + # ... access_control: - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/, roles: ROLE_ADMIN } .. code-block:: xml + + + @@ -635,6 +650,9 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: php + // app/config/security.php + + // ... 'access_control' => array( array('path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), array('path' => '^/', 'role' => 'ROLE_ADMIN'), @@ -648,6 +666,9 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: yaml + # app/config/security.yml + + # ... firewalls: login_firewall: pattern: ^/login$ @@ -658,6 +679,9 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: xml + + + @@ -667,6 +691,9 @@ see :doc:`/cookbook/security/form_login`. .. code-block:: php + // app/config/security.php + + // ... 'firewalls' => array( 'login_firewall' => array( 'pattern' => '^/login$', @@ -744,11 +771,21 @@ You can define as many URL patterns as you need - each is a regular expression. .. code-block:: xml - - - - - + + + + + + + + + + + .. code-block:: php @@ -816,15 +853,30 @@ Take the following ``access_control`` entries as an example: .. code-block:: xml - - - - - - + + + + + + + + + + + + + + .. code-block:: php + // app/config/security.php + $container->loadFromExtension('security', array( + // ... 'access_control' => array( array( 'path' => '^/admin', @@ -846,6 +898,7 @@ Take the following ``access_control`` entries as an example: 'role' => 'ROLE_USER', ), ), + )); For each incoming request, Symfony will decide which ``access_control`` to use based on the URI, the client's IP address, the incoming host name, @@ -949,14 +1002,29 @@ given prefix, ``/esi``, from outside access: .. code-block:: xml - - - - + + + + + + + + + + + + .. code-block:: php + // app/config/security.php + $container->loadFromExtension('security', array( + // ... 'access_control' => array( array( 'path' => '^/esi', @@ -968,6 +1036,7 @@ given prefix, ``/esi``, from outside access: 'role' => 'ROLE_NO_ACCESS' ), ), + )); Here is how it works when the path is ``/esi/something`` coming from the ``10.0.0.1`` IP: @@ -1064,13 +1133,25 @@ the user will be redirected to ``https``: .. code-block:: xml + + + + - + + .. code-block:: php + // app/config/security.php + $container->loadFromExtension('security', array( 'access_control' => array( array( 'path' => '^/cart/checkout', @@ -1078,6 +1159,7 @@ the user will be redirected to ``https``: 'requires_channel' => 'https', ), ), + )); .. _book-security-securing-controller: @@ -1196,15 +1278,23 @@ In fact, you've seen this already in the example in this chapter. .. code-block:: xml - - - - - - - - - + + + + + + + + + + + + + .. code-block:: php @@ -1321,11 +1411,19 @@ class: .. code-block:: xml - - - - - + + + + + + + + + .. code-block:: php @@ -1391,24 +1489,34 @@ do the following: .. code-block:: xml - - - - - - - - - - - + + + + + + + + + + + + + + + .. code-block:: php @@ -1554,22 +1662,30 @@ a new provider that chains the two together: .. code-block:: xml - - - - in_memory - user_db - - - - - - - - - - - + + + + + + + in_memory + user_db + + + + + + + + + + + + .. code-block:: php @@ -1614,6 +1730,7 @@ the first provider is always used: firewalls: secured_area: # ... + pattern: ^/ provider: user_db http_basic: realm: "Secured Demo Area" @@ -1623,13 +1740,21 @@ the first provider is always used: .. code-block:: xml - - - - - - - + + + + + + + + + + + .. code-block:: php @@ -1638,6 +1763,7 @@ the first provider is always used: 'firewalls' => array( 'secured_area' => array( // ... + 'pattern' => '^/', 'provider' => 'user_db', 'http_basic' => array( // ... @@ -1696,10 +1822,18 @@ rules by creating a role hierarchy: .. code-block:: xml - - ROLE_USER - ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH - + + + + + ROLE_USER + ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH + + .. code-block:: php @@ -1944,13 +2078,21 @@ the firewall can handle this automatically for you when you activate the .. code-block:: xml - - + + + + + + + + - - - - + + .. code-block:: php @@ -2050,11 +2192,19 @@ cookie will be ever created by Symfony2): .. code-block:: xml - - - - - + + + + + + + + + .. code-block:: php diff --git a/book/service_container.rst b/book/service_container.rst index e4334c3943f..f03793e54a4 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -375,7 +375,7 @@ configuration. .. code-block:: php // app/config/config.php - $this->import('@AcmeHelloBundle/Resources/config/services.php'); + $loader->import('@AcmeHelloBundle/Resources/config/services.php'); The ``imports`` directive allows your application to include service container configuration resources from any other location (most commonly from bundles). @@ -443,7 +443,7 @@ invokes the service container extension inside the FrameworkBundle: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -994,7 +994,7 @@ it exists and do nothing if it doesn't: )); In YAML, the special ``@?`` syntax tells the service container that the dependency -is optional. Of course, the ``NewsletterManager`` must also be written to +is optional. Of course, the ``NewsletterManager`` must also be rewritten to allow for an optional dependency:: public function __construct(Mailer $mailer = null) @@ -1058,6 +1058,7 @@ Configuring the service container is easy: .. code-block:: yaml + # src/Acme/HelloBundle/Resources/config/services.yml services: newsletter_manager: class: "%newsletter_manager.class%" @@ -1065,6 +1066,7 @@ Configuring the service container is easy: .. code-block:: xml + setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%', array( @@ -1112,6 +1115,7 @@ to be used for a specific purpose. Take the following example: .. code-block:: yaml + # app/config/services.yml services: foo.twig.extension: class: Acme\HelloBundle\Extension\FooExtension @@ -1120,19 +1124,25 @@ to be used for a specific purpose. Take the following example: .. code-block:: xml + - + .. code-block:: php + // app/config/services.php + use Symfony\Component\DependencyInjection\Definition; + $definition = new Definition('Acme\HelloBundle\Extension\FooExtension'); $definition->addTag('twig.extension'); $container->setDefinition('foo.twig.extension', $definition); diff --git a/book/templating.rst b/book/templating.rst index 313f59bd571..01610c936fa 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -715,8 +715,9 @@ tags: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + @@ -751,11 +752,11 @@ in your application configuration: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + - + @@ -832,12 +833,14 @@ configuration: .. code-block:: yaml + # app/config/routing.yml _welcome: path: / defaults: { _controller: AcmeDemoBundle:Welcome:index } .. code-block:: xml + add('_welcome', new Route('/', array( '_controller' => 'AcmeDemoBundle:Welcome:index', @@ -870,19 +877,21 @@ To link to the page, just use the ``path`` Twig function and refer to the route: Home -As expected, this will generate the URL ``/``. Now for a more complicated +As expected, this will generate the URL ``/``. Now, for a more complicated route: .. configuration-block:: .. code-block:: yaml + # app/config/routing.yml article_show: path: /article/{slug} defaults: { _controller: AcmeArticleBundle:Article:show } .. code-block:: xml + add('article_show', new Route('/article/{slug}', array( '_controller' => 'AcmeArticleBundle:Article:show', @@ -1192,11 +1205,12 @@ configuration file: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + - + twig diff --git a/book/testing.rst b/book/testing.rst index d5dcd0c504d..a86230f710a 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -501,8 +501,8 @@ force him with the ``followRedirects()`` method:: $client->followRedirects(); -If you pass ``false`` to the ``followRedirects()`` method, the redirects -will no longer be followed:: +If you pass ``false`` to the ``followRedirects()`` method, the redirects +will no longer be followed:: $client->followRedirects(false); @@ -736,7 +736,7 @@ configuration option: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:swiftmailer="http://symfony.com/schema/dic/swiftmailer" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-1.0.xsd"> + http://symfony.com/schema/dic/swiftmailer http://symfony.com/schema/dic/swiftmailer/swiftmailer-1.0.xsd"> diff --git a/book/translation.rst b/book/translation.rst index ce10b796644..e6f957198bc 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -69,7 +69,7 @@ enable the ``translator`` in your configuration: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -136,6 +136,11 @@ different formats, XLIFF being the recommended format: + .. code-block:: yaml + + # messages.fr.yml + Symfony2 is great: J'aime Symfony2 + .. code-block:: php // messages.fr.php @@ -143,11 +148,6 @@ different formats, XLIFF being the recommended format: 'Symfony2 is great' => 'J\'aime Symfony2', ); - .. code-block:: yaml - - # messages.fr.yml - Symfony2 is great: J'aime Symfony2 - For information on where these files should be located, see :ref:`book-translation-resource-locations`. @@ -448,6 +448,7 @@ by the routing system using the special ``_locale`` parameter: .. code-block:: yaml + # app/config/routing.yml contact: path: /{_locale}/contact defaults: { _controller: AcmeDemoBundle:Contact:index } @@ -456,6 +457,7 @@ by the routing system using the special ``_locale`` parameter: .. code-block:: xml + - - en - + + + + + .. code-block:: php @@ -630,6 +639,11 @@ bundle. + .. code-block:: yaml + + # validators.en.yml + author.name.not_blank: Please enter an author name. + .. code-block:: php // validators.en.php @@ -637,11 +651,6 @@ bundle. 'author.name.not_blank' => 'Please enter an author name.', ); - .. code-block:: yaml - - # validators.en.yml - author.name.not_blank: Please enter an author name. - Translating Database Content ---------------------------- diff --git a/book/validation.rst b/book/validation.rst index 127b666709c..6c58296c5ab 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -272,7 +272,7 @@ annotations if you're using the annotation method to specify your constraints: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -1142,8 +1142,8 @@ provides a sequence of groups to be validated: + http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> + diff --git a/components/config/definition.rst b/components/config/definition.rst index 772f51ceb9f..cead922e819 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -381,6 +381,8 @@ with ``append()``:: This is also useful to help you avoid repeating yourself if you have sections of the config that are repeated in different places. +.. _component-config-normalization: + Normalization ------------- @@ -435,6 +437,8 @@ a second argument:: ->fixXmlConfig('child', 'children') ->children() ->arrayNode('children') + // ... + ->end() ->end() ; diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3e3be050782..80213da7aec 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -140,6 +140,9 @@ output. For example:: It is possible to define your own styles using the class :class:`Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle`:: + use Symfony\Component\Console\Formatter\OutputFormatterStyle; + + // ... $style = new OutputFormatterStyle('red', 'yellow', array('bold', 'blink')); $output->getFormatter()->setStyle('fire', $style); $output->writeln('foo'); diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index 95d7e5b2697..f3fde08777c 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -42,10 +42,20 @@ Here is an example: .. code-block:: xml - + + + + + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + $definition = new Definition('Example\Foo'); $definition->setPublic(false); $container->setDefinition('foo', $definition); @@ -88,15 +98,22 @@ To create a synthetic service, set ``synthetic`` to ``true``: .. code-block:: xml - + + + + + + + .. code-block:: php use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setDefinition('request', new Definition()) + $container + ->setDefinition('request', new Definition()) ->setSynthetic(true); As you see, only the ``synthetic`` option is set. All other options are only used @@ -128,14 +145,23 @@ services. .. code-block:: xml - + + - + + + + + + .. code-block:: php - $definition = new Definition('Example\Foo'); - $container->setDefinition('foo', $definition); + use Symfony\Component\DependencyInjection\Definition; + + $container->setDefinition('foo', new Definition('Example\Foo')); $containerBuilder->setAlias('bar', 'foo'); @@ -173,12 +199,22 @@ the service itself gets loaded. To do so, you can use the ``file`` directive. .. code-block:: xml - - %kernel.root_dir%/src/path/to/file/foo.php - + + + + + + %kernel.root_dir%/src/path/to/file/foo.php + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + $definition = new Definition('Example\Foo\Bar'); $definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php'); $container->setDefinition('foo', $definition); diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index 1868cacc848..aef48199af1 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -203,7 +203,6 @@ The XML version of the config would then look like this: fooValue barValue - .. note:: diff --git a/components/dependency_injection/configurators.rst b/components/dependency_injection/configurators.rst index d13c9118fb6..c8036fc8685 100644 --- a/components/dependency_injection/configurators.rst +++ b/components/dependency_injection/configurators.rst @@ -155,30 +155,40 @@ The service config for the above classes would look something like this: .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 96df6429244..0682a00e624 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -47,18 +47,24 @@ class: .. code-block:: xml - - - NewsletterManager - NewsletterFactory - - - - + + + + + + NewsletterManager + NewsletterFactory + + + + + .. code-block:: php @@ -69,13 +75,11 @@ class: $container->setParameter('newsletter_manager.class', 'NewsletterManager'); $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' - ))->setFactoryClass( - '%newsletter_factory.class%' - )->setFactoryMethod( - 'get' - ); + $definition = new Definition('%newsletter_manager.class%'); + $definition->setFactoryClass('%newsletter_factory.class%'); + $definition->setFactoryMethod('get'); + + $container->setDefinition('newsletter_manager', $definition); When you specify the class to use for the factory (via ``factory_class``) the method will be called statically. If the factory itself should be instantiated @@ -100,20 +104,27 @@ In this case, the method (e.g. get) should be changed to be non-static: .. code-block:: xml - - - NewsletterManager - NewsletterFactory - - - - - - + + + + + + NewsletterManager + NewsletterFactory + + + + + + + + .. code-block:: php @@ -137,7 +148,8 @@ In this case, the method (e.g. get) should be changed to be non-static: .. note:: The factory service is specified by its id name and not a reference to - the service itself. So, you do not need to use the @ syntax. + the service itself. So, you do not need to use the @ syntax for this in + YAML configurations. Passing Arguments to the Factory Method --------------------------------------- @@ -166,22 +178,30 @@ in the previous example takes the ``templating`` service as an argument: .. code-block:: xml - - - NewsletterManager - NewsletterFactory - - - - - - - - + + + + + + NewsletterManager + NewsletterFactory + + + + + + + + + + + .. code-block:: php diff --git a/components/dependency_injection/introduction.rst b/components/dependency_injection/introduction.rst index 0d1448a1cfa..50d5d2d19c5 100644 --- a/components/dependency_injection/introduction.rst +++ b/components/dependency_injection/introduction.rst @@ -244,22 +244,28 @@ config files: .. code-block:: xml - - - sendmail - - - - - %mailer.transport% - - - - - - - - + + + + + + sendmail + + + + + %mailer.transport% + + + + + + + + + .. code-block:: php diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 7fff5442a54..a2f29d69ff4 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -62,10 +62,20 @@ You can mark the service as ``lazy`` by manipulating its definition: .. code-block:: xml - + + + + + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Definition; + $definition = new Definition('Acme\Foo'); $definition->setLazy(true); $container->setDefinition('foo', $definition); diff --git a/components/dependency_injection/parameters.rst b/components/dependency_injection/parameters.rst index e30a186fb76..86cf5a489eb 100644 --- a/components/dependency_injection/parameters.rst +++ b/components/dependency_injection/parameters.rst @@ -52,9 +52,15 @@ You can also use the ``parameters`` section of a config file to set parameters: .. code-block:: xml - - sendmail - + + + + + sendmail + + .. code-block:: php @@ -84,22 +90,28 @@ rather than being tied up and hidden with the service definition: .. code-block:: xml - - sendmail - + + - - - %mailer.transport% - - + + sendmail + + + + + %mailer.transport% + + + .. code-block:: php use Symfony\Component\DependencyInjection\Reference; - // ... $container->setParameter('mailer.transport', 'sendmail'); + $container ->register('mailer', 'Mailer') ->addArgument('%mailer.transport%'); @@ -146,32 +158,34 @@ making the class of a service a parameter: .. code-block:: xml - - sendmail - Mailer - + + + + + sendmail + Mailer + - - - %mailer.transport% - - + + + %mailer.transport% + + + .. code-block:: php use Symfony\Component\DependencyInjection\Reference; - // ... $container->setParameter('mailer.transport', 'sendmail'); $container->setParameter('mailer.class', 'Mailer'); + $container ->register('mailer', '%mailer.class%') ->addArgument('%mailer.transport%'); - $container - ->register('newsletter_manager', 'NewsletterManager') - ->addMethodCall('setMailer', array(new Reference('mailer'))); - .. note:: The percent sign inside a parameter or argument, as part of the string, must @@ -181,11 +195,11 @@ making the class of a service a parameter: .. code-block:: yaml - arguments: ['http://symfony.com/?foo=%%s&bar=%%d'] + arguments: ["http://symfony.com/?foo=%%s&bar=%%d"] .. code-block:: xml - http://symfony.com/?foo=%%s&bar=%%d + http://symfony.com/?foo=%%s&bar=%%d .. code-block:: php @@ -204,7 +218,6 @@ all parameters that are arrays. .. code-block:: yaml - # app/config/config.yml parameters: my_mailer.gateways: - mail1 @@ -220,30 +233,32 @@ all parameters that are arrays. .. code-block:: xml - - - - mail1 - mail2 - mail3 - - - - en - fr + + + + + + mail1 + mail2 + mail3 - - fr - en + + + en + fr + + + fr + en + - - + + .. code-block:: php - // app/config/config.php - use Symfony\Component\DependencyInjection\Definition; - $container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3')); $container->setParameter('my_multilang.language_fallback', array( 'en' => array('en', 'fr'), @@ -263,10 +278,10 @@ key, and define the type as ``constant``. .. code-block:: xml - - + + 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"> GLOBAL_CONSTANT @@ -276,21 +291,18 @@ key, and define the type as ``constant``. .. code-block:: php - $container->setParameter('global.constant.value', GLOBAL_CONSTANT); - $container->setParameter('my_class.constant.value', My_Class::CONSTANT_NAME); + $container->setParameter('global.constant.value', GLOBAL_CONSTANT); + $container->setParameter('my_class.constant.value', My_Class::CONSTANT_NAME); .. note:: - This does not work for YAML configuration. If you're using YAML, you can - import an XML file to take advantage of this functionality: + This does not work for YAML configurations. If you're using YAML, you + can import an XML file to take advantage of this functionality: - .. configuration-block:: - - .. code-block:: yaml + .. code-block:: yaml - # app/config/config.yml - imports: - - { resource: parameters.xml } + imports: + - { resource: parameters.xml } PHP Keywords in XML ------------------- diff --git a/components/dependency_injection/parentservices.rst b/components/dependency_injection/parentservices.rst index 8b3cf6fb7e3..d8d53cffdbd 100644 --- a/components/dependency_injection/parentservices.rst +++ b/components/dependency_injection/parentservices.rst @@ -78,40 +78,46 @@ The service config for these classes would look something like this: .. code-block:: xml - - - NewsletterManager - GreetingCardManager - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + NewsletterManager + GreetingCardManager + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php @@ -211,29 +217,35 @@ a parent for a service. .. code-block:: xml - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + .. code-block:: php @@ -242,7 +254,6 @@ a parent for a service. use Symfony\Component\DependencyInjection\Reference; // ... - $mailManager = new Definition(); $mailManager ->setAbstract(true); @@ -336,38 +347,44 @@ to the ``NewsletterManager`` class, the config would look like this: .. code-block:: xml - + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php diff --git a/components/dependency_injection/tags.rst b/components/dependency_injection/tags.rst index 67a4a730cce..ab523a10a77 100644 --- a/components/dependency_injection/tags.rst +++ b/components/dependency_injection/tags.rst @@ -48,13 +48,19 @@ Then, define the chain as a service: .. code-block:: xml - - TransportChain - + + - - - + + TransportChain + + + + + + .. code-block:: php @@ -89,14 +95,22 @@ For example you may add the following transports as services: .. code-block:: xml - - %mailer_host% - - + + + + + + %mailer_host% + + - - - + + + + + .. code-block:: php @@ -226,14 +240,22 @@ To answer this, change the service declaration: .. code-block:: xml - - %mailer_host% - - - - - - + + + + + + %mailer_host% + + + + + + + + .. code-block:: php diff --git a/components/dependency_injection/types.rst b/components/dependency_injection/types.rst index 5efc223c766..3fb9181fdea 100644 --- a/components/dependency_injection/types.rst +++ b/components/dependency_injection/types.rst @@ -47,21 +47,27 @@ service container configuration: .. code-block:: xml - - - - - - - - + + + + + + + + + + + + + .. code-block:: php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( 'NewsletterManager', @@ -123,23 +129,29 @@ accepts the dependency:: .. code-block:: xml - - - - - - - - - - + + + + + + + + + + + + + + + .. code-block:: php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( 'NewsletterManager' @@ -190,25 +202,31 @@ Another possibility is just setting public fields of the class directly:: .. code-block:: xml - - - - - - - - + + + + + + + + + + + + + .. code-block:: php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( 'NewsletterManager' - ))->setProperty('mailer', new Reference('my_mailer'))); + ))->setProperty('mailer', new Reference('my_mailer')); There are mainly only disadvantages to using property injection, it is similar to setter injection but with these additional important problems: diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index 98fb6c59d0c..61de74a97b4 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -205,6 +205,47 @@ instance of ``Symfony\Component\HttpKernel\Event\FilterResponseEvent``:: // ... } +.. sidebar:: Registering Event Listeners in the Service Container + + When you are using the + :doc:`DependencyInjection component `, + you can use the + :class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterListenersPass` + from the HttpKernel component to tag services as event listeners:: + + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass; + + $containerBuilder = new ContainerBuilder(new ParameterBag()); + $containerBuilder->addCompilerPass(new RegisterListenersPass()); + + // register the event dispatcher service + $containerBuilder->register( + 'event_dispatcher', + 'Symfony\Component\EventDispatcher\EventDispatcher' + ); + + // register your event listener service + $listener = new Definition('AcmeListener'); + $listener->addTag('kernel.event_listener', array( + 'event' => 'foo.action', + 'method' => 'onFooAction', + )); + $containerBuilder->setDefinition('listener_service_id', $listener); + + // register an event subscriber + $subscriber = new Definition('AcmeSubscriber'); + $subscriber->addTag('kernel.event_subscriber'); + $containerBuilder->setDefinition('subscriber_service_id', $subscriber); + + By default, the listeners pass assumes that the event dispatcher's service + id is ``event_dispatcher``, that event listeners are tagged with the + ``kernel.event_listener`` tag and that event subscribers are tagged with + the ``kernel.event_subscriber`` tag. You can change these default values + by passing custom values to the constructor of ``RegisterListenersPass``. + .. _event_dispatcher-closures-as-listeners: .. index:: diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 94762110bcd..5f1f743d631 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -35,7 +35,7 @@ This method has 3 arguments: $language = new ExpressionLanguage(); $language->register('lowercase', function ($str) { - is_string(%1$s) ? strtolower(%1$s) : %1$s; + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); }, function ($arguments, $str) { if (!is_string($str)) { return $str; @@ -69,11 +69,7 @@ Override ``registerFunctions`` to add your own functions:: parent::registerFunctions(); // do not forget to also register core functions $this->register('lowercase', function ($str) { - if (!is_string($str)) { - return $str; - } - - return sprintf('strtolower(%s)', $str); + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); }, function ($arguments, $str) { if (!is_string($str)) { return $str; diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index f392c5272cf..f54aecc8487 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -83,7 +83,7 @@ Example usage:: use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - $pdo = new \PDO('mysql:dbname=testdb;host=127.0.0.1'); + $pdo = new \PDO(...); $storage = new NativeSessionStorage(array(), new PdoSessionHandler($pdo)); $session = new Session($storage); diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index 11175cf1c15..f2c40873af7 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -16,7 +16,7 @@ implementation of :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionIn Make sure your PHP session isn't already started before using the Session class. If you have a legacy session system that starts your session, see - http://symfony.com/doc/current/components/http_foundation/session_php_bridge.html + :doc:`Legacy Sessions `. Quick example:: diff --git a/components/routing/hostname_pattern.rst b/components/routing/hostname_pattern.rst index b97fe30bb04..804ece250ba 100644 --- a/components/routing/hostname_pattern.rst +++ b/components/routing/hostname_pattern.rst @@ -22,12 +22,10 @@ You can also match on the HTTP *host* of the incoming request. .. code-block:: xml - + http://symfony.com/schema/routing/routing-1.0.xsd"> AcmeDemoBundle:Main:mobileHomepage @@ -79,12 +77,10 @@ you can use placeholders in your hostname: .. code-block:: xml - + http://symfony.com/schema/routing/routing-1.0.xsd"> AcmeDemoBundle:Main:mobileHomepage @@ -135,17 +131,14 @@ instance, if you want to match both ``m.example.com`` and .. code-block:: xml - + http://symfony.com/schema/routing/routing-1.0.xsd"> AcmeDemoBundle:Main:mobileHomepage m - m|mobile @@ -198,7 +191,6 @@ instance, if you want to match both ``m.example.com`` and .. code-block:: xml - @@ -250,16 +242,13 @@ You can also set the host option on imported routes: .. code-block:: yaml - # app/config/routing.yml acme_hello: resource: "@AcmeHelloBundle/Resources/config/routing.yml" host: "hello.example.com" .. code-block:: xml - - @@ -269,7 +258,6 @@ You can also set the host option on imported routes: .. code-block:: php - // app/config/routing.php use Symfony\Component\Routing\RouteCollection; $collection = new RouteCollection(); diff --git a/components/templating/helpers/assetshelper.rst b/components/templating/helpers/assetshelper.rst index 8e8dbf857bc..73f8042aaab 100644 --- a/components/templating/helpers/assetshelper.rst +++ b/components/templating/helpers/assetshelper.rst @@ -104,6 +104,8 @@ Asset path generation is handled internally by packages. The component provides You can also use multiple packages:: + use Symfony\Component\Templating\Asset\PathPackage; + // ... $templateEngine->set(new AssetsHelper()); diff --git a/components/templating/introduction.rst b/components/templating/introduction.rst index 4fd9c0f2aaa..1e5b0999b4c 100644 --- a/components/templating/introduction.rst +++ b/components/templating/introduction.rst @@ -103,7 +103,7 @@ In a template: .. note:: - The global variables can be overriden by a local variable in the template + The global variables can be overridden by a local variable in the template with the same name. Output Escaping diff --git a/components/translation/usage.rst b/components/translation/usage.rst index fbb6664c607..6b57f74ff2c 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -127,6 +127,11 @@ recommended format. These files are parsed by one of the loader classes. + .. code-block:: yaml + + Symfony2 is great: J'aime Symfony2 + symfony2.great: J'aime Symfony2 + .. code-block:: php return array( @@ -134,11 +139,6 @@ recommended format. These files are parsed by one of the loader classes. 'symfony2.great' => 'J\'aime Symfony2', ); - .. code-block:: yaml - - Symfony2 is great: J'aime Symfony2 - symfony2.great: J'aime Symfony2 - .. sidebar:: Using Real or Keyword Messages This example illustrates the two different philosophies when creating diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index 8fc99e2e36d..03f866f2e3e 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -39,7 +39,7 @@ Active Core Members * **Fabien Potencier** (:leader:`fabpot`). -* **Mergers**: +* **Mergers** (``@symfony/mergers`` on GitHub): * **Bernhard Schussek** (:merger:`webmozart`) can merge into the Form_, Validator_, Icu_, Intl_, Locale_, OptionsResolver_ and PropertyAccess_ @@ -59,7 +59,7 @@ Active Core Members HttpFoundation_, HttpKernel_, Serializer_, Stopwatch_, DoctrineBridge_, MonologBridge_, and TwigBridge_ components. -* **Deciders**: +* **Deciders** (``@symfony/deciders`` on GitHub): * **Jakub Zalas** (:decider:`jakzal`); * **Jordi Boggiano** (:decider:`seldaek`); diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 8b4535b0d55..ecff6254849 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -148,9 +148,13 @@ Service Naming Conventions ~~~~~~~~~~~~~~~~~~~~~~~~~~ * A service name contains groups, separated by dots; + * The DI alias of the bundle is the first group (e.g. ``fos_user``); + * Use lowercase letters for service and parameter names; + * A group name uses the underscore notation; + * Each service has a corresponding parameter containing the class name, following the ``SERVICE NAME.class`` convention. diff --git a/contributing/documentation/standards.rst b/contributing/documentation/standards.rst index 7cd44203034..7e888fd9796 100644 --- a/contributing/documentation/standards.rst +++ b/contributing/documentation/standards.rst @@ -80,6 +80,7 @@ Configuration examples should show all supported formats using * **Configuration** (including services and routing): YAML, XML, PHP * **Validation**: YAML, Annotations, XML, PHP * **Doctrine Mapping**: Annotations, YAML, XML, PHP +* **Translation**: XML, YAML, PHP Example ~~~~~~~ diff --git a/cookbook/assetic/asset_management.rst b/cookbook/assetic/asset_management.rst index cc80026071e..90311adc4e4 100644 --- a/cookbook/assetic/asset_management.rst +++ b/cookbook/assetic/asset_management.rst @@ -487,7 +487,7 @@ by Symfony (as the asset files are in the ``dev`` environment). This is on purpose - letting Symfony generate these files dynamically in a production environment is just too slow. -.. _cookbook-asetic-dump-prod: +.. _cookbook-assetic-dump-prod: Instead, each time you use your app in the ``prod`` environment (and therefore, each time you deploy), you should run the following task: diff --git a/cookbook/assetic/uglifyjs.rst b/cookbook/assetic/uglifyjs.rst index efdcd07e362..965e8c28e71 100644 --- a/cookbook/assetic/uglifyjs.rst +++ b/cookbook/assetic/uglifyjs.rst @@ -172,7 +172,7 @@ apply this filter when debug mode is off (e.g. ``app.php``): To try this out, switch to your ``prod`` environment (``app.php``). But before you do, don't forget to :ref:`clear your cache ` -and :ref:`dump your assetic assets `. +and :ref:`dump your assetic assets `. .. tip:: diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst new file mode 100644 index 00000000000..9d374d2dc4d --- /dev/null +++ b/cookbook/bundles/configuration.rst @@ -0,0 +1,399 @@ +.. index:: + single: Configuration; Semantic + single: Bundle; Extension configuration + +How to Create Friendly Configuration for a Bundle +================================================= + +If you open your application configuration file (usually ``app/config/config.yml``), +you'll see a number of different configuration "namespaces", such as ``framework``, +``twig`` and ``doctrine``. Each of these configures a specific bundle, allowing +you to configure things at a high level and then let the bundle make all the +low-level, complex changes based on your settings. + +For example, the following tells the FrameworkBundle to enable the form +integration, which involves the definition of quite a few services as well +as integration of other related components: + +.. configuration-block:: + + .. code-block:: yaml + + framework: + form: true + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('framework', array( + 'form' => true, + )); + +.. sidebar:: Using Parameters to Configure your Bundle + + If you don't have plans to share your bundle between projects, it doesn't + make sense to use this more advanced way of configuration. Since you use + the bundle only in one project, you can just change the service + configuration each time. + + If you *do* want to be able to configure something from within + ``config.yml``, you can always create a parameter there and use that + parameter somewhere else. + +Using the Bundle Extension +-------------------------- + +The basic idea is that instead of having the user override individual +parameters, you let the user configure just a few, specifically created, +options. As the bundle developer, you then parse through that configuration and +load correct services and parameters inside an "Extension" class. + +As an example, imagine you are creating a social bundle, which provides +integration with Twitter and such. To be able to reuse your bundle, you have to +make the ``client_id`` and ``client_secret`` variables configurable. Your +bundle configuration would look like: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + acme_social: + twitter: + client_id: 123 + client_secret: $ecret + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('acme_social', array( + 'client_id' => 123, + 'client_secret' => '$ecret', + )); + +.. seealso:: + + Read more about the extension in :doc:`/cookbook/bundles/extension`. + +.. tip:: + + If a bundle provides an Extension class, then you should *not* generally + override any service container parameters from that bundle. The idea + is that if an Extension class is present, every setting that should be + configurable should be present in the configuration made available by + that class. In other words, the extension class defines all the public + configuration settings for which backward compatibility will be maintained. + +.. seealso:: + + For parameter handling within a Dependency Injection class see + :doc:`/cookbook/configuration/using_parameters_in_dic`. + + +Processing the ``$configs`` Array +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First things first, you have to create an extension class as explained in +:doc:`extension`. + +Whenever a user includes the ``acme_social`` key (which is the DI alias) in a +configuration file, the configuration under it is added to an array of +configurations and passed to the ``load()`` method of your extension (Symfony2 +automatically converts XML and YAML to an array). + +For the configuration example in the previous section, the array passed to your +``load()`` method will look like this:: + + array( + array( + 'twitter' => array( + 'client_id' => 123, + 'client_secret' => '$ecret', + ), + ), + ) + +Notice that this is an *array of arrays*, not just a single flat array of the +configuration values. This is intentional, as it allows Symfony to parse +several configuration resources. For example, if ``acme_social`` appears in +another configuration file - say ``config_dev.yml`` - with different values +beneath it, the incoming array might look like this:: + + array( + // values from config.yml + array( + 'twitter' => array( + 'client_id' => 123, + 'client_secret' => '$secret', + ), + ), + // values from config_dev.yml + array( + 'twitter' => array( + 'client_id' => 456, + ), + ), + ) + +The order of the two arrays depends on which one is set first. + +But don't worry! Symfony's Config component will help you merge these values, +provide defaults and give the user validation errors on bad configuration. +Here's how it works. Create a ``Configuration`` class in the +``DependencyInjection`` directory and build a tree that defines the structure +of your bundle's configuration. + +The ``Configuration`` class to handle the sample configuration looks like:: + + // src/Acme/SocialBundle/DependencyInjection/Configuration.php + namespace Acme\SocialBundle\DependencyInjection; + + use Symfony\Component\Config\Definition\Builder\TreeBuilder; + use Symfony\Component\Config\Definition\ConfigurationInterface; + + class Configuration implements ConfigurationInterface + { + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('acme_social'); + + $rootNode + ->children() + ->arrayNode('twitter') + ->children() + ->integerNode('client_id')->end() + ->scalarNode('client_secret')->end() + ->end() + ->end() // twitter + ->end() + ; + + return $treeBuilder; + } + } + +.. seealso:: + + The ``Configuration`` class can be much more complicated than shown here, + supporting "prototype" nodes, advanced validation, XML-specific normalization + and advanced merging. You can read more about this in + :doc:`the Config component documentation `. You + can also see it in action by checking out some of the core Configuration + classes, such as the one from the `FrameworkBundle Configuration`_ or the + `TwigBundle Configuration`_. + +This class can now be used in your ``load()`` method to merge configurations and +force validation (e.g. if an additional option was passed, an exception will be +thrown):: + + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + + $config = $this->processConfiguration($configuration, $configs); + // ... + } + +The ``processConfiguration()`` method uses the configuration tree you've defined +in the ``Configuration`` class to validate, normalize and merge all of the +configuration arrays together. + +.. tip:: + + Instead of calling ``processConfiguration()`` in your extension each time you + provide some configuration options, you might want to use the + :class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\ConfigurableExtension` + to do this automatically for you:: + + // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php + namespace Acme\HelloBundle\DependencyInjection; + + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; + + class AcmeHelloExtension extends ConfigurableExtension + { + // note that this method is called loadInternal and not load + protected function loadInternal(array $mergedConfig, ContainerBuilder $container) + { + // ... + } + } + + This class uses the ``getConfiguration()`` method to get the Configuration + instance, you should override it if your Configuration class is not called + ``Configuration`` or if it is not placed in the same namespace as the + extension. + +.. sidebar:: Processing the Configuration yourself + + Using the Config component is fully optional. The ``load()`` method gets an + array of configuration values. You can simply parse these arrays yourself + (e.g. by overriding configurations and using :phpfunction:`isset` to check + for the existence of a value). Be aware that it'll be very hard to support XML. + + .. code-block:: php + + public function load(array $configs, ContainerBuilder $container) + { + $config = array(); + // let resources override the previous set value + foreach ($configs as $subConfig) { + $config = array_merge($config, $subConfig); + } + + // ... now use the flat $config array + } + +Modifying the Configuration of Another Bundle +--------------------------------------------- + +If you have multiple bundles that depend on each other, it may be useful +to allow one ``Extension`` class to modify the configuration passed to another +bundle's ``Extension`` class, as if the end-developer has actually placed that +configuration in their ``app/config/config.yml`` file. This can be achieved +using a prepend extension. For more details, see +:doc:`/cookbook/bundles/prepend_extension`. + +Dump the Configuration +---------------------- + +The ``config:dump-reference`` command dumps the default configuration of a +bundle in the console using the Yaml format. + +As long as your bundle's configuration is located in the standard location +(``YourBundle\DependencyInjection\Configuration``) and does not require +arguments to be passed to the constructor it will work automatically. If you +have something different, your ``Extension`` class must override the +:method:`Extension::getConfiguration() ` +method and return an instance of your ``Configuration``. + +Supporting XML +-------------- + +Symfony allows people to provide the configuration in three different formats: +Yaml, XML and PHP. Both Yaml and PHP use the same syntax and are supported by +default when using the Config component. Supporting XML requires you to do some +more things. But when sharing your bundle with others, it is recommended that +you follow these steps. + +Make your Config Tree ready for XML +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Config component provides some methods by default to allow it to correctly +process XML configuration. See ":ref:`component-config-normalization`" of the +component documentation. However, you can do some optional things as well, this +will improve the experience of using XML configuration: + +Choosing an XML Namespace +~~~~~~~~~~~~~~~~~~~~~~~~~ + +In XML, the `XML namespace`_ is used to determine which elements belong to the +configuration of a specific bundle. The namespace is returned from the +:method:`Extension::getNamespace() ` +method. By convention, the namespace is a URL (it doesn't have to be a valid +URL nor does it need to exists). By default, the namespace for a bundle is +``http://example.org/dic/schema/DI_ALIAS``, where ``DI_ALIAS`` is the DI alias of +the extension. You might want to change this to a more professional URL:: + + // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php + + // ... + class AcmeHelloExtension extends Extension + { + // ... + + public function getNamespace() + { + return 'http://acme_company.com/schema/dic/hello'; + } + } + +Providing an XML Schema +~~~~~~~~~~~~~~~~~~~~~~~ + +XML has a very useful feature called `XML schema`_. This allows you to +describe all possible elements and attributes and their values in an XML Schema +Definition (an xsd file). This XSD file is used by IDEs for auto completion and +it is used by the Config component to validate the elements. + +In order to use the schema, the XML configuration file must provide an +``xsi:schemaLocation`` attribute pointing to the XSD file for a certain XML +namespace. This location always starts with the XML namespace. This XML +namespace is then replaced with the XSD validation base path returned from +:method:`Extension::getXsdValidationBasePath() ` +method. This namespace is then followed by the rest of the path from the base +path to the file itself. + +By convention, the XSD file lives in the ``Resources/config/schema``, but you +can place it anywhere you like. You should return this path as the base path:: + + // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php + + // ... + class AcmeHelloExtension extends Extension + { + // ... + + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/schema'; + } + } + +Assume the XSD file is called ``hello-1.0.xsd``, the schema location will be +``http://acme_company.com/schema/dic/hello/hello-1.0.xsd``: + +.. code-block:: xml + + + + + + + + + + + + + +.. _`FrameworkBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php +.. _`XML namespace`: http://en.wikipedia.org/wiki/XML_namespace +.. _`XML schema`: http://en.wikipedia.org/wiki/XML_schema diff --git a/cookbook/bundles/extension.rst b/cookbook/bundles/extension.rst index d35580aabca..cc4ce322399 100644 --- a/cookbook/bundles/extension.rst +++ b/cookbook/bundles/extension.rst @@ -2,113 +2,36 @@ single: Configuration; Semantic single: Bundle; Extension configuration -How to Expose a semantic Configuration for a Bundle -=================================================== +How to Load Service Configuration inside a Bundle +================================================= -If you open your application configuration file (usually ``app/config/config.yml``), -you'll see a number of different configuration "namespaces", such as ``framework``, -``twig``, and ``doctrine``. Each of these configures a specific bundle, allowing -you to configure things at a high level and then let the bundle make all the -low-level, complex changes that result. +In Symfony, you'll find yourself using many services. These services can be +registered in the `app/config` directory of your application. But when you +want to decouple the bundle for use in other projects, you want to include the +service configuration in the bundle itself. This article will teach you how to +do that. -For example, the following tells the FrameworkBundle to enable the form -integration, which involves the defining of quite a few services as well -as integration of other related components: - -.. configuration-block:: - - .. code-block:: yaml - - framework: - # ... - form: true - - .. code-block:: xml - - - - - - .. code-block:: php - - $container->loadFromExtension('framework', array( - // ... - 'form' => true, - // ... - )); - -When you create a bundle, you have two choices on how to handle configuration: - -1. **Normal Service Configuration** (*easy*): - - You can specify your services in a configuration file (e.g. ``services.yml``) - that lives in your bundle and then import it from your main application - configuration. This is really easy, quick and totally effective. If you - make use of :ref:`parameters `, then - you still have the flexibility to customize your bundle from your application - configuration. See ":ref:`service-container-imports-directive`" for more - details. - -2. **Exposing Semantic Configuration** (*advanced*): - - This is the way configuration is done with the core bundles (as described - above). The basic idea is that, instead of having the user override individual - parameters, you let the user configure just a few, specifically created - options. As the bundle developer, you then parse through that configuration - and load services inside an "Extension" class. With this method, you won't - need to import any configuration resources from your main application - configuration: the Extension class can handle all of this. - -The second option - which you'll learn about in this article - is much more -flexible, but also requires more time to setup. If you're wondering which -method you should use, it's probably a good idea to start with method #1, -and then change to #2 later if you need to. If you plan to distribute your -bundle, the second option is recommended. - -The second method has several specific advantages: - -* Much more powerful than simply defining parameters: a specific option value - might trigger the creation of many service definitions; - -* Ability to have configuration hierarchy; - -* Smart merging when several configuration files (e.g. ``config_dev.yml`` - and ``config.yml``) override each other's configuration; - -* Configuration validation (if you use a :ref:`Configuration Class `); - -* IDE auto-completion when you create an XSD and developers use XML. - -.. sidebar:: Overriding bundle parameters - - If a Bundle provides an Extension class, then you should generally *not* - override any service container parameters from that bundle. The idea - is that if an Extension class is present, every setting that should be - configurable should be present in the configuration made available by - that class. In other words the extension class defines all the publicly - supported configuration settings for which backward compatibility will - be maintained. - -.. seealso:: +Creating an Extension Class +--------------------------- - For parameter handling within a Dependency Injection class see - :doc:`/cookbook/configuration/using_parameters_in_dic`. +In order to load service configuration, you have to create a Dependency +Injection Extension for your bundle. This class has some conventions in order +to be detected automatically. But you'll later see how you can change it to +your own preferences. By default, the Extension has to comply with the +following conventions: -.. index:: - single: Bundle; Extension - single: DependencyInjection; Extension +* It has to live in the ``DependencyInjection`` namespace of the bundle; -Creating an Extension Class ---------------------------- +* The name is equal to the bundle name with the ``Bundle`` suffix replaced by + ``Extension`` (e.g. the Extension class of ``AcmeHelloBundle`` would be + called ``AcmeHelloExtension``). -If you do choose to expose a semantic configuration for your bundle, you'll -first need to create a new "Extension" class, which will handle the process. -This class should live in the ``DependencyInjection`` directory of your bundle -and its name should be constructed by replacing the ``Bundle`` suffix of the -Bundle class name with ``Extension``. For example, the Extension class of -``AcmeHelloBundle`` would be called ``AcmeHelloExtension``:: +The Extension class should implement the +:class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface`, +but usually you would simply extend the +:class:`Symfony\\Component\\DependencyInjection\\Extension\\Extension` class:: - // Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php + // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php namespace Acme\HelloBundle\DependencyInjection; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -118,496 +41,80 @@ Bundle class name with ``Extension``. For example, the Extension class of { public function load(array $configs, ContainerBuilder $container) { - // ... where all of the heavy logic is done - } - - public function getXsdValidationBasePath() - { - return __DIR__.'/../Resources/config/'; - } - - public function getNamespace() - { - return 'http://www.example.com/symfony/schema/'; + // ... you'll load the files here later } } -.. note:: - - The ``getXsdValidationBasePath`` and ``getNamespace`` methods are only - required if the bundle provides optional XSD's for the configuration. - -The presence of the previous class means that you can now define an ``acme_hello`` -configuration namespace in any configuration file. The namespace ``acme_hello`` -is constructed from the extension's class name by removing the word ``Extension`` -and then lowercasing and underscoring the rest of the name. In other words, -``AcmeHelloExtension`` becomes ``acme_hello``. - -You can begin specifying configuration under this namespace immediately: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - acme_hello: ~ - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('acme_hello', array()); - -.. tip:: - - If you follow the naming conventions laid out above, then the ``load()`` - method of your extension code is always called as long as your bundle - is registered in the Kernel. In other words, even if the user does not - provide any configuration (i.e. the ``acme_hello`` entry doesn't even - appear), the ``load()`` method will be called and passed an empty ``$configs`` - array. You can still provide some sensible defaults for your bundle if - you want. - -Registering the Extension Class -------------------------------- - -An Extension class will automatically be registered by Symfony2 when -following these simple conventions: - -* The extension must be stored in the ``DependencyInjection`` sub-namespace; - -* The extension must be named after the bundle name and suffixed with - ``Extension`` (``AcmeHelloExtension`` for ``AcmeHelloBundle``); - -* The extension *should* provide an XSD schema (but will be registered automatically - regardless). - Manually Registering an Extension Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When not following the conventions, you will have to manually register your -extension. To manually register an extension class override the -:method:`Bundle::build() ` -method in your bundle:: +extension. To do this, you should override the +:method:`Bundle::getContainerExtension() ` +method to return the instance of the extension:: // ... use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass; class AcmeHelloBundle extends Bundle { - public function build(ContainerBuilder $container) + public function getContainerExtension() { - parent::build($container); - - // register extensions that do not follow the conventions manually - $container->registerExtension(new UnconventionalExtensionClass()); - } - } - -In this case, the extension class must also implement a ``getAlias()`` method -and return a unique alias named after the bundle (e.g. ``acme_hello``). This -is required because the class name doesn't follow the conventions by ending -in ``Extension``. - -Additionally, the ``load()`` method of your extension will *only* be called -if the user specifies the ``acme_hello`` alias in at least one configuration -file. Once again, this is because the Extension class doesn't follow the -conventions set out above, so nothing happens automatically. - -Parsing the ``$configs`` Array ------------------------------- - -Whenever a user includes the ``acme_hello`` namespace in a configuration file, -the configuration under it is added to an array of configurations and -passed to the ``load()`` method of your extension (Symfony2 automatically -converts XML and YAML to an array). - -Take the following configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - acme_hello: - foo: fooValue - bar: barValue - - .. code-block:: xml - - - - - - - - barValue - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('acme_hello', array( - 'foo' => 'fooValue', - 'bar' => 'barValue', - )); - -The array passed to your ``load()`` method will look like this:: - - array( - array( - 'foo' => 'fooValue', - 'bar' => 'barValue', - ), - ) - -Notice that this is an *array of arrays*, not just a single flat array of the -configuration values. This is intentional. For example, if ``acme_hello`` -appears in another configuration file - say ``config_dev.yml`` - with different -values beneath it, then the incoming array might look like this:: - - array( - array( - 'foo' => 'fooValue', - 'bar' => 'barValue', - ), - array( - 'foo' => 'fooDevValue', - 'baz' => 'newConfigEntry', - ), - ) - -The order of the two arrays depends on which one is set first. - -It's your job, then, to decide how these configurations should be merged -together. You might, for example, have later values override previous values -or somehow merge them together. - -Later, in the :ref:`Configuration Class ` -section, you'll learn of a truly robust way to handle this. But for now, -you might just merge them manually:: - - public function load(array $configs, ContainerBuilder $container) - { - $config = array(); - foreach ($configs as $subConfig) { - $config = array_merge($config, $subConfig); + return new UnconventionalExtensionClass(); } - - // ... now use the flat $config array } -.. caution:: - - Make sure the above merging technique makes sense for your bundle. This - is just an example, and you should be careful to not use it blindly. +Since the new Extension class name doesn't follow the naming conventions, you +should also override +:method:`Extension::getAlias() ` +to return the correct DI alias. The DI alias is the name used to refer to the +bundle in the container (e.g. in the ``app/config/config.yml`` file). By +default, this is done by removing the ``Extension`` prefix and converting the +class name to underscores (e.g. ``AcmeHelloExtension``'s DI alias is +``acme_hello``). Using the ``load()`` Method --------------------------- -Within ``load()``, the ``$container`` variable refers to a container that only -knows about this namespace configuration (i.e. it doesn't contain service -information loaded from other bundles). The goal of the ``load()`` method -is to manipulate the container, adding and configuring any methods or services -needed by your bundle. +In the ``load()`` method, all services and parameters related to this extension +will be loaded. This method doesn't get the actual container instance, but a +copy. This container only has the parameters from the actual container. After +loading the services and parameters, the copy will be merged into the actual +container, to ensure all services and parameters are also added to the actual +container. -Loading external Configuration Resources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In the ``load()`` method, you can use PHP code to register service definitions, +but it is more common if you put the these definitions in a configuration file +(using the Yaml, XML or PHP format). Luckily, you can use the file loaders in +the extension! -One common thing to do is to load an external configuration file that may -contain the bulk of the services needed by your bundle. For example, suppose -you have a ``services.xml`` file that holds much of your bundle's service -configuration:: +For instance, assume you have a file called ``services.xml`` in the +``Resources/config`` directory of your bundle, your load method looks like:: use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\Config\FileLocator; + // ... public function load(array $configs, ContainerBuilder $container) { - // ... prepare your $config variable - - $loader = new XmlFileLoader( - $container, - new FileLocator(__DIR__.'/../Resources/config') - ); - $loader->load('services.xml'); - } - -You might even do this conditionally, based on one of the configuration values. -For example, suppose you only want to load a set of services if an ``enabled`` -option is passed and set to true:: - - public function load(array $configs, ContainerBuilder $container) - { - // ... prepare your $config variable - - $loader = new XmlFileLoader( - $container, - new FileLocator(__DIR__.'/../Resources/config') - ); - - if (isset($config['enabled']) && $config['enabled']) { - $loader->load('services.xml'); - } - } - -Configuring Services and Setting Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once you've loaded some service configuration, you may need to modify the -configuration based on some of the input values. For example, suppose you -have a service whose first argument is some string "type" that it will use -internally. You'd like this to be easily configured by the bundle user, so -in your service configuration file (e.g. ``services.xml``), you define this -service and use a blank parameter - ``acme_hello.my_service_type`` - as -its first argument: - -.. code-block:: xml - - - - - - - - - - - %acme_hello.my_service_type% - - - - -But why would you define an empty parameter and then pass it to your service? -The answer is that you'll set this parameter in your extension class, based -on the incoming configuration values. Suppose, for example, that you want -to allow the user to define this *type* option under a key called ``my_type``. -Add the following to the ``load()`` method to do this:: - - public function load(array $configs, ContainerBuilder $container) - { - // ... prepare your $config variable - $loader = new XmlFileLoader( $container, new FileLocator(__DIR__.'/../Resources/config') ); $loader->load('services.xml'); - - if (!isset($config['my_type'])) { - throw new \InvalidArgumentException( - 'The "my_type" option must be set' - ); - } - - $container->setParameter( - 'acme_hello.my_service_type', - $config['my_type'] - ); } -Now, the user can effectively configure the service by specifying the ``my_type`` -configuration value: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - acme_hello: - my_type: foo - # ... - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('acme_hello', array( - 'my_type' => 'foo', - ..., - )); - -Global Parameters -~~~~~~~~~~~~~~~~~ - -When you're configuring the container, be aware that you have the following -global parameters available to use: - -* ``kernel.name`` -* ``kernel.environment`` -* ``kernel.debug`` -* ``kernel.root_dir`` -* ``kernel.cache_dir`` -* ``kernel.logs_dir`` -* ``kernel.bundles`` -* ``kernel.charset`` - -.. caution:: - - All parameter and service names starting with a ``_`` are reserved for the - framework, and new ones must not be defined by bundles. - -.. _cookbook-bundles-extension-config-class: - -Validation and Merging with a Configuration Class -------------------------------------------------- - -So far, you've done the merging of your configuration arrays by hand and -are checking for the presence of config values manually using the ``isset()`` -PHP function. An optional *Configuration* system is also available which -can help with merging, validation, default values, and format normalization. +Other available loaders are the ``YamlFileLoader``, ``PhpFileLoader`` and +``IniFileLoader``. .. note:: - Format normalization refers to the fact that certain formats - largely XML - - result in slightly different configuration arrays and that these arrays - need to be "normalized" to match everything else. - -To take advantage of this system, you'll create a ``Configuration`` class -and build a tree that defines your configuration in that class:: - - // src/Acme/HelloBundle/DependencyInjection/Configuration.php - namespace Acme\HelloBundle\DependencyInjection; - - use Symfony\Component\Config\Definition\Builder\TreeBuilder; - use Symfony\Component\Config\Definition\ConfigurationInterface; - - class Configuration implements ConfigurationInterface - { - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('acme_hello'); - - $rootNode - ->children() - ->scalarNode('my_type')->defaultValue('bar')->end() - ->end(); - - return $treeBuilder; - } - } - -This is a *very* simple example, but you can now use this class in your ``load()`` -method to merge your configuration and force validation. If any options other -than ``my_type`` are passed, the user will be notified with an exception -that an unsupported option was passed:: - - public function load(array $configs, ContainerBuilder $container) - { - $configuration = new Configuration(); - - $config = $this->processConfiguration($configuration, $configs); - - // ... - } - -The ``processConfiguration()`` method uses the configuration tree you've defined -in the ``Configuration`` class to validate, normalize and merge all of the -configuration arrays together. - -The ``Configuration`` class can be much more complicated than shown here, -supporting array nodes, "prototype" nodes, advanced validation, XML-specific -normalization and advanced merging. You can read more about this in -:doc:`the Config component documentation `. -You can also see it in action by checking out some of the core Configuration classes, -such as the one from the `FrameworkBundle Configuration`_ or the `TwigBundle Configuration`_. - -Modifying the Configuration of another Bundle -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have multiple bundles that depend on each other, it may be useful -to allow one ``Extension`` class to modify the configuration passed to another -bundle's ``Extension`` class, as if the end-developer has actually placed that -configuration in their ``app/config/config.yml`` file. - -For more details, see :doc:`/cookbook/bundles/prepend_extension`. - -Default Configuration Dump -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``config:dump-reference`` command allows a bundle's default configuration to -be output to the console in YAML. - -As long as your bundle's configuration is located in the standard location -(``YourBundle\DependencyInjection\Configuration``) and does not have a -``__construct()`` it will work automatically. If you have something -different, your ``Extension`` class must override the -:method:`Extension::getConfiguration() ` -method and return an instance of your -``Configuration``. - -Comments and examples can be added to your configuration nodes using the -``->info()`` and ``->example()`` methods:: - - // src/Acme/HelloBundle/DependencyExtension/Configuration.php - namespace Acme\HelloBundle\DependencyInjection; - - use Symfony\Component\Config\Definition\Builder\TreeBuilder; - use Symfony\Component\Config\Definition\ConfigurationInterface; - - class Configuration implements ConfigurationInterface - { - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('acme_hello'); - - $rootNode - ->children() - ->scalarNode('my_type') - ->defaultValue('bar') - ->info('what my_type configures') - ->example('example setting') - ->end() - ->end() - ; + The ``IniFileLoader`` can only be used to load parameters and it can only + load them as strings. - return $treeBuilder; - } - } - -This text appears as YAML comments in the output of the ``config:dump-reference`` -command. - -.. index:: - pair: Convention; Configuration +Using Configuration to Change the Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _`FrameworkBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php -.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php +The Extension is also the class that handles the configuration for that +particular bundle (e.g. the configuration in ``app/config/config.yml``). To +read more about it, see the ":doc:`/cookbook/bundles/configuration`" article. diff --git a/cookbook/bundles/index.rst b/cookbook/bundles/index.rst index df0cf217b9d..87641de5e23 100644 --- a/cookbook/bundles/index.rst +++ b/cookbook/bundles/index.rst @@ -10,4 +10,5 @@ Bundles override remove extension + configuration prepend_extension diff --git a/cookbook/configuration/configuration_organization.rst b/cookbook/configuration/configuration_organization.rst new file mode 100644 index 00000000000..52c5c0f0d4a --- /dev/null +++ b/cookbook/configuration/configuration_organization.rst @@ -0,0 +1,363 @@ +.. index:: + single: Configuration + +How to Organize Configuration Files +=================================== + +The default Symfony2 Standard Edition defines three +:doc:`execution environments ` called +``dev``, ``prod`` and ``test``. An environment simply represents a way to +execute the same codebase with different configurations. + +In order to select the configuration file to load for each environment, Symfony +executes the ``registerContainerConfiguration()`` method of the ``AppKernel`` +class:: + + // app/AppKernel.php + use Symfony\Component\HttpKernel\Kernel; + use Symfony\Component\Config\Loader\LoaderInterface; + + class AppKernel extends Kernel + { + // ... + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); + } + } + +This method loads the ``app/config/config_dev.yml`` file for the ``dev`` +environment and so on. In turn, this file loads the common configuration file +located at ``app/config/config.yml``. Therefore, the configuration files of the +default Symfony Standard Edition follow this structure: + +.. code-block:: text + + / + ├─ app/ + │ └─ config/ + │ ├─ config.yml + │ ├─ config_dev.yml + │ ├─ config_prod.yml + │ ├─ config_test.yml + │ ├─ parameters.yml + │ ├─ parameters.yml.dist + │ ├─ routing.yml + │ ├─ routing_dev.yml + │ └─ security.yml + ├─ src/ + ├─ vendor/ + └─ web/ + +This default structure was chosen for its simplicity — one file per environment. +But as any other Symfony feature, you can customize it to better suit your needs. +The following sections explain different ways to organize your configuration +files. In order to simplify the examples, only the ``dev`` and ``prod`` +environments are taken into account. + +Different Directories per Environment +------------------------------------- + +Instead of suffixing the files with ``_dev`` and ``_prod``, this technique +groups all the related configuration files under a directory with the same +name as the environment: + +.. code-block:: text + + / + ├─ app/ + │ └─ config/ + │ ├─ common/ + │ │ ├─ config.yml + │ │ ├─ parameters.yml + │ │ ├─ routing.yml + │ │ └─ security.yml + │ ├─ dev/ + │ │ ├─ config.yml + │ │ ├─ parameters.yml + │ │ ├─ routing.yml + │ │ └─ security.yml + │ └─ prod/ + │ ├─ config.yml + │ ├─ parameters.yml + │ ├─ routing.yml + │ └─ security.yml + ├─ src/ + ├─ vendor/ + └─ web/ + +To make this work, change the code of the +:method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerContainerConfiguration` +method:: + + // app/AppKernel.php + use Symfony\Component\HttpKernel\Kernel; + use Symfony\Component\Config\Loader\LoaderInterface; + + class AppKernel extends Kernel + { + // ... + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.'/config/'.$this->getEnvironment().'/config.yml'); + } + } + +Then, make sure that each ``config.yml`` file loads the rest of the configuration +files, including the common files. For instance, this would be the imports +needed for the ``app/config/dev/config.yml`` file: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/dev/config.yml + imports: + - { resource: '../common/config.yml' } + - { resource: 'parameters.yml' } + - { resource: 'security.yml' } + + # ... + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/dev/config.php + $loader->import('../common/config.php'); + $loader->import('parameters.php'); + $loader->import('security.php'); + + // ... + +Semantic Configuration Files +---------------------------- + +A different organization strategy may be needed for complex applications with +large configuration files. For instance, you could create one file per bundle +and several files to define all application services: + +.. code-block:: text + + / + ├─ app/ + │ └─ config/ + │ ├─ bundles/ + │ │ ├─ bundle1.yml + │ │ ├─ bundle2.yml + │ │ ├─ ... + │ │ └─ bundleN.yml + │ ├─ environments/ + │ │ ├─ common.yml + │ │ ├─ dev.yml + │ │ └─ prod.yml + │ ├─ routing/ + │ │ ├─ common.yml + │ │ ├─ dev.yml + │ │ └─ prod.yml + │ └─ services/ + │ ├─ frontend.yml + │ ├─ backend.yml + │ ├─ ... + │ └─ security.yml + ├─ src/ + ├─ vendor/ + └─ web/ + +Again, change the code of the ``registerContainerConfiguration()`` method to +make Symfony aware of the new file organization:: + + // app/AppKernel.php + use Symfony\Component\HttpKernel\Kernel; + use Symfony\Component\Config\Loader\LoaderInterface; + + class AppKernel extends Kernel + { + // ... + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.'/config/environments/'.$this->getEnvironment().'.yml'); + } + } + +Following the same technique explained in the previous section, make sure to +import the appropriate configuration files from each main file (``common.yml``, +``dev.yml`` and ``prod.yml``). + +Advanced Techniques +------------------- + +Symfony loads configuration files using the +:doc:`Config component `, which provides some +advanced features. + +Mix and Match Configuration Formats +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Configuration files can import files defined with any other built-in configuration +format (``.yml``, ``.xml``, ``.php``, ``.ini``): + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: 'parameters.yml' } + - { resource: 'services.xml' } + - { resource: 'security.yml' } + - { resource: 'legacy.php' } + + # ... + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $loader->import('parameters.yml'); + $loader->import('services.xml'); + $loader->import('security.yml'); + $loader->import('legacy.php'); + + // ... + +.. caution:: + + The ``IniFileLoader`` parses the file contents using the + :phpfunction:`parse_ini_file` function. Therefore, you can only set + parameters to string values. Use one of the other loaders if you want + to use other data types (e.g. boolean, integer, etc.). + +If you use any other configuration format, you have to define your own loader +class extending it from :class:`Symfony\\Component\\DependencyInjection\\Loader\\FileLoader`. +When the configuration values are dynamic, you can use the PHP configuration +file to execute your own logic. In addition, you can define your own services +to load configurations from databases or web services. + +Global Configuration Files +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some system administrators may prefer to store sensitive parameters in files +outside the project directory. Imagine that the database credentials for your +website are stored in the ``/etc/sites/mysite.com/parameters.yml`` file. Loading +this file is as simple as indicating the full file path when importing it from +any other configuration file: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: 'parameters.yml' } + - { resource: '/etc/sites/mysite.com/parameters.yml' } + + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $loader->import('parameters.yml'); + $loader->import('/etc/sites/mysite.com/parameters.yml'); + + // ... + +Most of the time, local developers won't have the same files that exist on the +production servers. For that reason, the Config component provides the +``ignore_errors`` option to silently discard errors when the loaded file +doesn't exist: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: 'parameters.yml' } + - { resource: '/etc/sites/mysite.com/parameters.yml', ignore_errors: true } + + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $loader->import('parameters.yml'); + $loader->import('/etc/sites/mysite.com/parameters.yml', null, true); + + // ... + +As you've seen, there are lots of ways to organize your configuration files. You +can choose one of these or even create your own custom way of organizing the +files. Don't feel limited by the Standard Edition that comes with Symfony. For even +more customization, see ":doc:`/cookbook/configuration/override_dir_structure`". diff --git a/cookbook/configuration/index.rst b/cookbook/configuration/index.rst index 69250747457..35b87ef0f2e 100644 --- a/cookbook/configuration/index.rst +++ b/cookbook/configuration/index.rst @@ -12,3 +12,4 @@ Configuration pdo_session_storage apache_router web_server_configuration + configuration_organization diff --git a/cookbook/console/logging.rst b/cookbook/console/logging.rst index ee8ec0d9e08..133021ccf7a 100644 --- a/cookbook/console/logging.rst +++ b/cookbook/console/logging.rst @@ -205,11 +205,11 @@ First configure a listener for console terminate events in the service container xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Acme\DemoBundle\EventListener\ConsoleExceptionListener + Acme\DemoBundle\EventListener\ConsoleExceptionListener - + @@ -223,11 +223,11 @@ First configure a listener for console terminate events in the service container use Symfony\Component\DependencyInjection\Reference; $container->setParameter( - 'console_terminate_listener.class', + 'console_exception_listener.class', 'Acme\DemoBundle\EventListener\ConsoleExceptionListener' ); $definitionConsoleExceptionListener = new Definition( - '%console_terminate_listener.class%', + '%console_exception_listener.class%', array(new Reference('logger')) ); $definitionConsoleExceptionListener->addTag( @@ -247,7 +247,7 @@ Then implement the actual listener:: use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Psr\Log\LoggerInterface; - class ConsoleTerminateListener + class ConsoleExceptionListener { private $logger; diff --git a/cookbook/deployment/tools.rst b/cookbook/deployment/tools.rst index 4e0d147f7e0..58cc3bd6cfc 100644 --- a/cookbook/deployment/tools.rst +++ b/cookbook/deployment/tools.rst @@ -71,14 +71,23 @@ Common Post-Deployment Tasks After deploying your actual source code, there are a number of common things you'll need to do: -A) Configure your ``app/config/parameters.yml`` File +A) Check Requirements +~~~~~~~~~~~~~~~~~~~~~ + +Check if your server meets the requirements by running: + +.. code-block:: bash + + $ php app/check.php + +B) Configure your ``app/config/parameters.yml`` File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This file should be customized on each system. The method you use to deploy your source code should *not* deploy this file. Instead, you should set it up manually (or via some build process) on your server(s). -B) Update your Vendors +C) Update your Vendors ~~~~~~~~~~~~~~~~~~~~~~ Your vendors can be updated before transferring your source code (i.e. @@ -97,7 +106,7 @@ as you normally do: ensures that development packages are not installed in the production environment. -C) Clear your Symfony Cache +D) Clear your Symfony Cache ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Make sure you clear (and warm-up) your Symfony cache: @@ -106,7 +115,7 @@ Make sure you clear (and warm-up) your Symfony cache: $ php app/console cache:clear --env=prod --no-debug -D) Dump your Assetic Assets +E) Dump your Assetic Assets ~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're using Assetic, you'll also want to dump your assets: @@ -115,7 +124,7 @@ If you're using Assetic, you'll also want to dump your assets: $ php app/console assetic:dump --env=prod --no-debug -E) Other Things! +F) Other Things! ~~~~~~~~~~~~~~~~ There may be lots of other things that you need to do, depending on your diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index e47ed2d8fca..2f208c23c99 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -61,7 +61,8 @@ To get started, configure the database connection parameters: ), )); -For full DBAL configuration options, see :ref:`reference-dbal-configuration`. +For full DBAL configuration options, or to learn how to configure multiple +connections, see :ref:`reference-dbal-configuration`. You can then access the Doctrine DBAL connection by accessing the ``database_connection`` service:: @@ -141,11 +142,8 @@ mapping type: # app/config/config.yml doctrine: dbal: - connections: - default: - # other connections parameters - mapping_types: - enum: string + mapping_types: + enum: string .. code-block:: xml @@ -158,11 +156,7 @@ mapping type: - - - - string - + string @@ -172,14 +166,9 @@ mapping type: // app/config/config.php $container->loadFromExtension('doctrine', array( 'dbal' => array( - 'connections' => array( - 'default' => array( - // other connection parameers - 'mapping_types' => array( - 'enum' => 'string', - ), - ), - ), + mapping_types' => array( + 'enum' => 'string', + ), ), )); diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 908523244a2..6871be7cdab 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -413,6 +413,10 @@ Now, each time a user clicks the ``Add a tag`` link, a new sub form will appear on the page. When the form is submitted, any new tag forms will be converted into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` object. +.. seealso:: + + You can find a working example in this `JSFiddle`_. + To make handling these new tags easier, add an "adder" and a "remover" method for the tags in the ``Task`` class:: @@ -726,3 +730,4 @@ the relationship between the removed ``Tag`` and ``Task`` object. each Tag object itself. .. _`Owning Side and Inverse Side`: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html +.. _`JSFiddle`: http://jsfiddle.net/847Kf/4/ diff --git a/cookbook/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index 3dc89f4e254..0446c11598e 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -28,6 +28,8 @@ it is broken down. type: swift_mailer from_email: error@example.com to_email: error@example.com + # or list of recipients + # to_email: [developer_1@example.com, developer_2@example.com, ...] subject: An Error Occurred! level: debug diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 442d891a4b1..8eed3af02fd 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -14,6 +14,7 @@ * :doc:`/cookbook/bundles/override` * :doc:`/cookbook/bundles/remove` * :doc:`/cookbook/bundles/extension` + * :doc:`/cookbook/bundles/configuration` * :doc:`/cookbook/bundles/prepend_extension` * :doc:`/cookbook/cache/index` @@ -30,6 +31,7 @@ * :doc:`/cookbook/configuration/pdo_session_storage` * :doc:`/cookbook/configuration/apache_router` * :doc:`/cookbook/configuration/web_server_configuration` + * :doc:`/cookbook/configuration/configuration_organization` * :doc:`/cookbook/console/index` diff --git a/cookbook/routing/redirect_trailing_slash.rst b/cookbook/routing/redirect_trailing_slash.rst index 55d4138d4c1..65a09c9467c 100644 --- a/cookbook/routing/redirect_trailing_slash.rst +++ b/cookbook/routing/redirect_trailing_slash.rst @@ -44,16 +44,15 @@ system, as explained below: defaults: { _controller: AcmeDemoBundle:Redirecting:removeTrailingSlash } requirements: url: .*/$ - _method: GET + methods: [GET] .. code-block:: xml - + AcmeDemoBundle:Redirecting:removeTrailingSlash .*/$ - GET @@ -72,8 +71,11 @@ system, as explained below: ), array( 'url' => '.*/$', - '_method' => 'GET', - ) + ), + array(), + '', + array(), + array('GET') ) ); diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst index 0add181f1c0..0feefca9a4b 100644 --- a/cookbook/security/api_key_authentication.rst +++ b/cookbook/security/api_key_authentication.rst @@ -45,13 +45,19 @@ value and then a User object is created:: public function createToken(Request $request, $providerKey) { - if (!$request->query->has('apikey')) { + // look for an apikey query parameter + $apiKey = $request->query->get('apikey'); + + // or if you want to use an "apikey" header, then do something like this: + // $apiKey = $request->headers->get('apikey'); + + if (!$apiKey) { throw new BadCredentialsException('No API key found'); } return new PreAuthenticatedToken( 'anon.', - $request->query->get('apikey'), + $apiKey, $providerKey ); } diff --git a/reference/constraints/Valid.rst b/reference/constraints/Valid.rst index 1ba69a9262d..669ae84b32f 100644 --- a/reference/constraints/Valid.rst +++ b/reference/constraints/Valid.rst @@ -26,7 +26,7 @@ an ``Address`` instance in the ``$address`` property. .. code-block:: php // src/Acme/HelloBundle/Entity/Address.php - namespace Amce\HelloBundle\Entity; + namespace Acme\HelloBundle\Entity; class Address { diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index abc5edc553c..7f4ab96061f 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -183,7 +183,7 @@ Field Variables | choices | ``array`` | A nested array containing the ``ChoiceView`` objects of | | | | the remaining choices. | +------------------------+--------------+-------------------------------------------------------------------+ -| separator | ``string`` | The seperator to use between choice groups. | +| separator | ``string`` | The separator to use between choice groups. | +------------------------+--------------+-------------------------------------------------------------------+ | empty_value | ``mixed`` | The empty value if not already in the list, otherwise | | | | ``null``. |