diff --git a/book/doctrine.rst b/book/doctrine.rst index 534baad9282..6b03e686698 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -62,16 +62,44 @@ information. By convention, this information is usually configured in an The parameters defined in that file are referenced by the main configuration file when setting up Doctrine: - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - driver: "%database_driver%" - host: "%database_host%" - dbname: "%database_name%" - user: "%database_user%" - password: "%database_password%" + .. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + doctrine: + dbal: + driver: "%database_driver%" + host: "%database_host%" + dbname: "%database_name%" + user: "%database_user%" + password: "%database_password%" + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/config.php + $configuration->loadFromExtension('doctrine', array( + 'dbal' => array( + 'driver' => '%database_driver%', + 'host' => '%database_host%', + 'dbname' => '%database_name%', + 'user' => '%database_user%', + 'password' => '%database_password%', + ), + )); By separating the database information into a separate file, you can easily keep different versions of the file on each server. You can also @@ -909,6 +937,24 @@ To relate the ``Category`` and ``Product`` entities, start by creating a mappedBy: category # don't forget to init the collection in entity __construct() method + .. code-block:: xml + + + + + + + + + + + First, since a ``Category`` object will relate to many ``Product`` objects, a ``products`` array property is added to hold those ``Product`` objects. @@ -966,6 +1012,28 @@ object, you'll want to add a ``$category`` property to the ``Product`` class: name: category_id referencedColumnName: id + .. code-block:: xml + + + + + + + + + + + + Finally, now that you've added a new property to both the ``Category`` and ``Product`` classes, tell Doctrine to generate the missing getter and setter methods for you: @@ -1387,6 +1455,21 @@ and ``nullable``. Take a few examples: length: 150 unique: true + .. code-block:: xml + + + + + .. note:: There are a few more options not listed here. For more details, see diff --git a/cookbook/configuration/apache_router.rst b/cookbook/configuration/apache_router.rst index c2e5d79a4c9..6c33bc52185 100644 --- a/cookbook/configuration/apache_router.rst +++ b/cookbook/configuration/apache_router.rst @@ -13,12 +13,33 @@ Change Router Configuration Parameters To dump Apache routes you must first tweak some configuration parameters to tell Symfony2 to use the ``ApacheUrlMatcher`` instead of the default one: -.. code-block:: yaml +.. configuration-block:: - # app/config/config_prod.yml - parameters: - router.options.matcher.cache_class: ~ # disable router cache - router.options.matcher_class: Symfony\Component\Routing\Matcher\ApacheUrlMatcher + .. code-block:: yaml + + # app/config/config_prod.yml + parameters: + router.options.matcher.cache_class: ~ # disable router cache + router.options.matcher_class: Symfony\Component\Routing\Matcher\ApacheUrlMatcher + + .. code-block:: xml + + + + null + + Symfony\Component\Routing\Matcher\ApacheUrlMatcher + + + + .. code-block:: php + + // app/config/config_prod.php + $container->setParameter('router.options.matcher.cache_class', null); // disable router cache + $container->setParameter( + 'router.options.matcher_class', + 'Symfony\Component\Routing\Matcher\ApacheUrlMatcher' + ); .. tip:: @@ -33,13 +54,28 @@ Generating mod_rewrite rules To test that it's working, let's create a very basic route for demo bundle: -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + hello: + pattern: /hello/{name} + defaults: { _controller: AcmeDemoBundle:Demo:hello } + + .. code-block:: xml + + + + AcmeDemoBundle:Demo:hello + - # app/config/routing.yml - hello: - pattern: /hello/{name} - defaults: { _controller: AcmeDemoBundle:Demo:hello } + .. code-block:: php + // app/config/routing.php + $collection->add('hello', new Route('/hello/{name}', array( + '_controller' => 'AcmeDemoBundle:Demo:hello', + ))); Now generate **url_rewrite** rules: diff --git a/cookbook/configuration/external_parameters.rst b/cookbook/configuration/external_parameters.rst index 041e5ea4fd2..ec4974a6395 100644 --- a/cookbook/configuration/external_parameters.rst +++ b/cookbook/configuration/external_parameters.rst @@ -117,11 +117,19 @@ key, and define the type as ``constant``. This only works for XML configuration. If you're *not* using XML, simply import an XML file to take advantage of this functionality: - .. code-block:: yaml + .. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: parameters.xml } + + .. code-block:: php + + // app/config/config.php + $loader->import('parameters.xml'); - # app/config/config.yml - imports: - - { resource: parameters.xml } Miscellaneous Configuration --------------------------- diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index 804d94761a5..e3a66489ca3 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -101,14 +101,33 @@ may need to modify the paths inside these files:: If you use the AsseticBundle you need to configure this, so it can use the correct ``web`` directory: - .. code-block:: yaml + .. configuration-block:: - # app/config/config.yml + .. code-block:: yaml + + # app/config/config.yml - # ... - assetic: # ... - read_from: "%kernel.root_dir%/../../public_html" + assetic: + # ... + read_from: "%kernel.root_dir%/../../public_html" + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/config.php + + // ... + $container->loadFromExtension('assetic', array( + // ... + 'read_from' => '%kernel.root_dir%/../../public_html', + )); Now you just need to dump the assets again and your application should work: diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 78f1f6c1e98..2dbcb9d3823 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -38,7 +38,7 @@ To get started, configure the database connection parameters: .. code-block:: xml - // app/config/config.xml + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + $container->loadFromExtension('doctrine', array( + 'dbal' => array( + 'default_connection' => 'default', + 'connections' => array( + 'default' => array( + 'driver' => 'pdo_sqlite', + 'memory' => true, + ), + ), + ), + )); + + $container + ->setDefinition( + 'my.listener', + new Definition('Acme\SearchBundle\EventListener\SearchIndexer') + ) + ->addTag('doctrine.event_listener', array('event' => 'postPersist')) + ; + $container + ->setDefinition( + 'my.listener2', + new Definition('Acme\SearchBundle\EventListener\SearchIndexer2') + ) + ->addTag('doctrine.event_listener', array('event' => 'postPersist', 'connection' => 'default')) + ; + $container + ->setDefinition( + 'my.subscriber', + new Definition('Acme\SearchBundle\EventListener\SearchIndexerSubscriber') + ) + ->addTag('doctrine.event_subscriber', array('connection' => 'default')) + ; + Creating the Listener Class --------------------------- @@ -99,7 +137,7 @@ a ``postPersist`` method, which will be called when the event is thrown:: // perhaps you only want to act on some "Product" entity if ($entity instanceof Product) { - // do something with the Product + // ... do something with the Product } } } diff --git a/cookbook/doctrine/file_uploads.rst b/cookbook/doctrine/file_uploads.rst index 9da44db2cd8..6fc7e0b9e14 100644 --- a/cookbook/doctrine/file_uploads.rst +++ b/cookbook/doctrine/file_uploads.rst @@ -22,7 +22,7 @@ will be covered in this cookbook entry. Basic Setup ----------- -First, create a simple Doctrine Entity class to work with:: +First, create a simple ``Doctrine`` Entity class to work with:: // src/Acme/DemoBundle/Entity/Document.php namespace Acme\DemoBundle\Entity; @@ -118,20 +118,68 @@ look like this:: } Next, create this property on your ``Document`` class and add some validation -rules:: +rules: - // src/Acme/DemoBundle/Entity/Document.php +.. configuration-block:: - // ... - class Document - { - /** - * @Assert\File(maxSize="6000000") - */ - public $file; + .. code-block:: yaml + + # src/Acme/DemoBundle/Resources/config/validation.yml + Acme\DemoBundle\Entity\Document: + properties: + file: + - File: + maxSize: 6000000 + + .. code-block:: php-annotations + + // src/Acme/DemoBundle/Entity/Document.php + namespace Acme\DemoBundle\Entity; // ... - } + use Symfony\Component\Validator\Constraints as Assert; + + class Document + { + /** + * @Assert\File(maxSize="6000000") + */ + public $file; + + // ... + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/DemoBundle/Entity/Document.php + namespace Acme\DemoBundle\Entity; + + // ... + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Document + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('file', new Assert\File(array( + 'maxSize' => 6000000, + ))); + } + } .. note:: @@ -141,6 +189,7 @@ rules:: The following controller shows you how to handle the entire process:: + // ... use Acme\DemoBundle\Entity\Document; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; // ... @@ -176,15 +225,27 @@ The following controller shows you how to handle the entire process:: When writing the template, don't forget to set the ``enctype`` attribute: - .. code-block:: html+jinja + .. configuration-block:: + + .. code-block:: html+jinja + +

Upload File

+ +
+ {{ form_widget(form) }} + + +
+ + .. code-block:: html+php -

Upload File

+

Upload File

-
- {{ form_widget(form) }} + enctype($form) ?>> + widget($form) ?> - -
+ + The previous controller will automatically persist the ``Document`` entity with the submitted name, but it will do nothing about the file and the ``path`` diff --git a/cookbook/doctrine/multiple_entity_managers.rst b/cookbook/doctrine/multiple_entity_managers.rst index 89c09465a32..73707e4fc97 100644 --- a/cookbook/doctrine/multiple_entity_managers.rst +++ b/cookbook/doctrine/multiple_entity_managers.rst @@ -56,6 +56,99 @@ The following configuration code shows how you can configure two entity managers mappings: AcmeCustomerBundle: ~ + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('doctrine', array( + 'dbal' => array( + 'default_connection' => 'default', + 'connections' => array( + 'default' => array( + 'driver' => '%database_driver%', + 'host' => '%database_host%', + 'port' => '%database_port%', + 'dbname' => '%database_name%', + 'user' => '%database_user%', + 'password' => '%database_password%', + 'charset' => 'UTF8', + ), + 'customer' => array( + 'driver' => '%database_driver2%', + 'host' => '%database_host2%', + 'port' => '%database_port2%', + 'dbname' => '%database_name2%', + 'user' => '%database_user2%', + 'password' => '%database_password2%', + 'charset' => 'UTF8', + ), + ), + ), + + 'orm' => array( + 'default_entity_manager' => 'default', + 'entity_managers' => array( + 'default' => array( + 'connection' => 'default', + 'mappings' => array( + 'AcmeDemoBundle' => null, + 'AcmeStoreBundle' => null, + ), + ), + 'customer' => array( + 'connection' => 'customer', + 'mappings' => array( + 'AcmeCustomerBundle' => null, + ), + ), + ), + ), + )); + In this case, you've defined two entity managers and called them ``default`` and ``customer``. The ``default`` entity manager manages entities in the ``AcmeDemoBundle`` and ``AcmeStoreBundle``, while the ``customer`` entity diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index 521c8b845f8..4dad1c54d81 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -104,26 +104,45 @@ is "expanded" (i.e. radio buttons or checkboxes, instead of a select field), you want to always render it in a ``ul`` element. In your form theme template (see above link for details), create a ``gender_widget`` block to handle this: -.. code-block:: html+jinja - - {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #} - {% block gender_widget %} - {% spaceless %} - {% if expanded %} -
    - {% for child in form %} -
  • - {{ form_widget(child) }} - {{ form_label(child) }} -
  • - {% endfor %} -
- {% else %} - {# just let the choice widget render the select tag #} - {{ block('choice_widget') }} - {% endif %} - {% endspaceless %} - {% endblock %} +.. configuration-block:: + + .. code-block:: html+jinja + + {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #} + {% block gender_widget %} + {% spaceless %} + {% if expanded %} +
    + {% for child in form %} +
  • + {{ form_widget(child) }} + {{ form_label(child) }} +
  • + {% endfor %} +
+ {% else %} + {# just let the choice widget render the select tag #} + {{ block('choice_widget') }} + {% endif %} + {% endspaceless %} + {% endblock %} + + .. code-block:: html+php + + + +
    block($form, 'widget_container_attributes') ?>> + +
  • + widget($child) ?> + label($child) ?> +
  • + +
+ + + renderBlock('choice_widget') ?> + .. note:: @@ -132,13 +151,35 @@ you want to always render it in a ``ul`` element. In your form theme template Further, the main config file should point to the custom form template so that it's used when rendering all forms. - .. code-block:: yaml + .. configuration-block:: - # app/config/config.yml - twig: - form: - resources: - - 'AcmeDemoBundle:Form:fields.html.twig' + .. code-block:: yaml + + # app/config/config.yml + twig: + form: + resources: + - 'AcmeDemoBundle:Form:fields.html.twig' + + .. code-block:: xml + + + + + AcmeDemoBundle:Form:fields.html.twig + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('twig', array( + 'form' => array( + 'resources' => array( + 'AcmeDemoBundle:Form:fields.html.twig', + ), + ), + )); Using the Field Type -------------------- @@ -194,6 +235,12 @@ example, suppose that you're storing the gender parameters in configuration: + .. code-block:: php + + // app/config/config.php + $container->setParameter('genders.m', 'Male'); + $container->setParameter('genders.f', 'Female'); + To use the parameter, define your custom field type as a service, injecting the ``genders`` parameter value as the first argument to its to-be-created ``__construct`` function: @@ -219,6 +266,21 @@ the ``genders`` parameter value as the first argument to its to-be-created + .. code-block:: php + + // src/Acme/DemoBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; + + $container + ->setDefinition('acme_demo.form.type.gender', new Definition( + 'Acme\DemoBundle\Form\Type\GenderType', + array('%genders%') + )) + ->addTag('form.type', array( + 'alias' => 'gender', + )) + ; + .. tip:: Make sure the services file is being imported. See :ref:`service-container-imports-directive` @@ -231,8 +293,8 @@ method to ``GenderType``, which receives the gender configuration:: // src/Acme/DemoBundle/Form/Type/GenderType.php namespace Acme\DemoBundle\Form\Type; - // ... + // ... class GenderType extends AbstractType { private $genderChoices; diff --git a/cookbook/form/create_form_type_extension.rst b/cookbook/form/create_form_type_extension.rst index 4444b5053f7..95983a33b69 100644 --- a/cookbook/form/create_form_type_extension.rst +++ b/cookbook/form/create_form_type_extension.rst @@ -112,14 +112,19 @@ tag: .. code-block:: xml - + .. code-block:: php $container - ->register('acme_demo_bundle.image_type_extension', 'Acme\DemoBundle\Form\Extension\ImageTypeExtension') + ->register( + 'acme_demo_bundle.image_type_extension', + 'Acme\DemoBundle\Form\Extension\ImageTypeExtension' + ) ->addTag('form.type_extension', array('alias' => 'file')); The ``alias`` key of the tag is the type of field that this extension should @@ -219,8 +224,8 @@ it in the view:: /** * Store the image_path option as a builder attribute * - * @param \Symfony\Component\Form\FormBuilder $builder - * @param array $options + * @param FormBuilder $builder + * @param array $options */ public function buildForm(FormBuilder $builder, array $options) { @@ -232,8 +237,8 @@ it in the view:: /** * Pass the image url to the view * - * @param \Symfony\Component\Form\FormView $view - * @param \Symfony\Component\Form\FormInterface $form + * @param FormView $view + * @param FormInterface $form */ public function buildView(FormView $view, FormInterface $form) { @@ -326,4 +331,4 @@ next to the file field. For example:: } When displaying the form, if the underlying model has already been associated -with an image, you will see it displayed next to the file input. \ No newline at end of file +with an image, you will see it displayed next to the file input. diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index 802497a79b4..893ad451bee 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -65,7 +65,9 @@ for converting to and from the issue number and the Issue object:: * Transforms a string (number) to an object (issue). * * @param string $number + * * @return Issue|null + * * @throws TransformationFailedException if object (issue) is not found. */ public function reverseTransform($number) @@ -287,6 +289,17 @@ it's recognized as a custom field type: + .. code-block:: php + + $container + ->setDefinition('acme_demo.type.issue_selector', array( + new Reference('doctrine.orm.entity_manager'), + )) + ->addTag('form.type', array( + 'alias' => 'issue_selector', + )) + ; + Now, whenever you need to use your special ``issue_selector`` field type, it's quite easy:: @@ -311,4 +324,3 @@ it's quite easy:: return 'task'; } } - diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 7116cf6823c..4e676bb717c 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -39,7 +39,8 @@ from this class will look the exact same regardless if a new Product is being cr or if an existing product is being edited (e.g. a product fetched from the database). Suppose now, that you don't want the user to be able to change the ``name`` value -once the object has been created. To do this, you can rely on Symfony's :doc:`Event Dispatcher ` +once the object has been created. To do this, you can rely on Symfony's +:doc:`Event Dispatcher ` system to analyze the data on the object and modify the form based on the Product object's data. In this entry, you'll learn how to add this level of flexibility to your forms. diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index f54e0d61ef5..c5950637f90 100755 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -179,7 +179,7 @@ In your controller, you'll now initialize a new instance of ``TaskType``:: if ('POST' === $request->getMethod()) { $form->bindRequest($request); if ($form->isValid()) { - // maybe do some form processing, like saving the Task and Tag objects + // ... maybe do some form processing, like saving the Task and Tag objects } } @@ -459,6 +459,24 @@ into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` obje targetEntity: Tag cascade: [persist] + .. code-block:: xml + + + + + + + + + + + + + + A second potential issue deals with the `Owning Side and Inverse Side`_ of Doctrine relationships. In this example, if the "owning" side of the relationship is "Task", then persistence will work fine as the tags are diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index a967dc448e1..e478dc7a202 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -733,6 +733,7 @@ and customize the ``field_errors`` fragment. .. tip:: + See :ref:`cookbook-form-theming-methods` for how to apply this customization. You can also customize the error output for just one specific field type. @@ -786,6 +787,7 @@ class to the ``div`` element around each row: .. tip:: + See :ref:`cookbook-form-theming-methods` for how to apply this customization. Adding a "Required" Asterisk to Field Labels @@ -840,6 +842,7 @@ original template: .. tip:: + See :ref:`cookbook-form-theming-methods` for how to apply this customization. Adding "help" messages @@ -909,6 +912,7 @@ To render a help message below a field, pass in a ``help`` variable: widget($form['title'], array('help' => 'foobar')) ?> .. tip:: + See :ref:`cookbook-form-theming-methods` for how to apply this customization. Using Form Variables diff --git a/cookbook/logging/monolog.rst b/cookbook/logging/monolog.rst index bfc27cda2d7..9c837db0dde 100644 --- a/cookbook/logging/monolog.rst +++ b/cookbook/logging/monolog.rst @@ -59,6 +59,7 @@ allows you to log the messages in several ways easily. .. code-block:: yaml + # app/config/config.yml monolog: handlers: applog: @@ -75,8 +76,10 @@ allows you to log the messages in several ways easily. syslog: type: syslog level: error + .. code-block:: xml + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'applog' => array( + 'type' => 'stream', + 'path' => '/var/log/symfony.log', + 'level' => 'error', + ), + 'main' => array( + 'type' => 'fingers_crossed', + 'action_level' => 'warning', + 'handler' => 'file', + ), + 'file' => array( + 'type' => 'stream', + 'level' => 'debug', + ), + 'syslog' => array( + 'type' => 'syslog', + 'level' => 'error', + ), + ), + )); + The above configuration defines a stack of handlers which will be called in the order where they are defined. @@ -137,6 +166,7 @@ easily. Your formatter must implement .. code-block:: yaml + # app/config/config.yml services: my_formatter: class: Monolog\Formatter\JsonFormatter @@ -149,6 +179,7 @@ easily. Your formatter must implement .. code-block:: xml + + + .. code-block:: php + + // app/config/config.php + $container + ->register('my_formatter', 'Monolog\Formatter\JsonFormatter'); + + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'file' => array( + 'type' => 'stream', + 'level' => 'debug', + 'formatter' => 'my_formatter', + ), + ), + )); + Adding some extra data in the log messages ------------------------------------------ @@ -243,6 +291,59 @@ using a processor. level: debug formatter: monolog.formatter.session_request + .. code-block:: xml + + + + + + [%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%%\n + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container + ->register('monolog.formatter.session_request', 'Monolog\Formatter\LineFormatter') + ->addArgument('[%%datetime%%] [%%extra.token%%] %%channel%%.%%level_name%%: %%message%%\n'); + + $container + ->register('monolog.processor.session_request', 'Acme\MyBundle\SessionRequestProcessor') + ->addArgument(new Reference('session')) + ->addTag('monolog.processor', array('method' => 'processRecord')); + + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'main' => array( + 'type' => 'stream', + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', + 'level' => 'debug', + 'formatter' => 'monolog.formatter.session_request', + ), + ), + )); + .. note:: If you use several handlers, you can also register the processor at the diff --git a/cookbook/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index 330d4a3de60..da5c11945a9 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -61,6 +61,31 @@ it is broken down. /> + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'mail' => array( + 'type' => 'fingers_crossed', + 'action_level' => 'critical', + 'handler' => 'buffered', + ), + 'buffered' => array( + 'type' => 'buffer', + 'handler' => 'swift', + ), + 'swift' => array( + 'type' => 'swift_mailer', + 'from_email' => 'error@example.com', + 'to_email' => 'error@example.com', + 'subject' => 'An Error Occurred!', + 'level' => 'debug', + ), + ), + )); + The ``mail`` handler is a ``fingers_crossed`` handler which means that it is only triggered when the action level, in this case ``critical`` is reached. @@ -154,6 +179,40 @@ get logged on the server as well as the emails being sent: + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'main' => array( + 'type' => 'fingers_crossed', + 'action_level' => 'critical', + 'handler' => 'grouped', + ), + 'grouped' => array( + 'type' => 'group', + 'members' => array('streamed', 'buffered'), + ), + 'streamed' => array( + 'type' => 'stream', + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', + 'level' => 'debug', + ), + 'buffered' => array( + 'type' => 'buffer', + 'handler' => 'swift', + ), + 'swift' => array( + 'type' => 'swift_mailer', + 'from_email' => 'error@example.com', + 'to_email' => 'error@example.com', + 'subject' => 'An Error Occurred!', + 'level' => 'debug', + ), + ), + )); + + This uses the ``group`` handler to send the messages to the two group members, the ``buffered`` and the ``stream`` handlers. The messages will now be both written to the log file and emailed. diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index cc2a8be663f..4cc326af199 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -354,13 +354,13 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` # src/Acme/DemoBundle/Resources/config/services.yml services: - wsse.security.authentication.provider: - class: Acme\DemoBundle\Security\Authentication\Provider\WsseProvider - arguments: ['', %kernel.cache_dir%/security/nonces] + wsse.security.authentication.provider: + class: Acme\DemoBundle\Security\Authentication\Provider\WsseProvider + arguments: ['', %kernel.cache_dir%/security/nonces] - wsse.security.authentication.listener: - class: Acme\DemoBundle\Security\Firewall\WsseListener - arguments: [@security.context, @security.authentication.manager] + wsse.security.authentication.listener: + class: Acme\DemoBundle\Security\Firewall\WsseListener + arguments: [@security.context, @security.authentication.manager] .. code-block:: xml @@ -370,19 +370,19 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` 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"> - - - - %kernel.cache_dir%/security/nonces - - - - - - - + + + + %kernel.cache_dir%/security/nonces + + + + + + + .. code-block:: php @@ -392,17 +392,22 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` use Symfony\Component\DependencyInjection\Reference; $container->setDefinition('wsse.security.authentication.provider', - new Definition( - 'Acme\DemoBundle\Security\Authentication\Provider\WsseProvider', - array('', '%kernel.cache_dir%/security/nonces') - )); + new Definition( + 'Acme\DemoBundle\Security\Authentication\Provider\WsseProvider', array( + '', + '%kernel.cache_dir%/security/nonces', + ) + ) + ); $container->setDefinition('wsse.security.authentication.listener', - new Definition( - 'Acme\DemoBundle\Security\Firewall\WsseListener', array( - new Reference('security.context'), - new Reference('security.authentication.manager')) - )); + new Definition( + 'Acme\DemoBundle\Security\Firewall\WsseListener', array( + new Reference('security.context'), + new Reference('security.authentication.manager'), + ) + ) + ); Now that your services are defined, tell your security context about your factory. Factories must be included in an individual configuration file, @@ -435,6 +440,20 @@ factory service, tagged as ``security.listener.factory``: + .. code-block:: php + + // src/Acme/DemoBundle/Resources/config/security_factories.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + $definition = new Definition('Acme\DemoBundle\DependencyInjection\Security\Factory\WsseFactory', array( + '', + '%kernel.cache_dir%/security/nonces', + )); + $definition->addTag('security.listener.factory'); + + $container->setDefinition('security.authentication.factory.wsse', $definition); + Now, import the factory configuration via the the ``factories`` key in your security configuration: @@ -467,13 +486,35 @@ security configuration: You are finished! You can now define parts of your app as under WSSE protection. -.. code-block:: yaml +.. configuration-block:: - security: - firewalls: - wsse_secured: - pattern: /api/.* - wsse: true + .. code-block:: yaml + + security: + firewalls: + wsse_secured: + pattern: /api/.* + wsse: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'wsse_secured' => array( + 'pattern' => '/api/.*', + 'wsse' => true, + ), + ), + )); + Congratulations! You have written your very own custom security authentication provider! @@ -546,13 +587,38 @@ in order to put it to use. The lifetime of each wsse request is now configurable, and can be set to any desirable value per firewall. -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + security: + firewalls: + wsse_secured: + pattern: /api/.* + wsse: { lifetime: 30 } + + .. code-block:: xml + + + + + + - security: - firewalls: - wsse_secured: - pattern: /api/.* - wsse: { lifetime: 30 } + .. code-block:: php + + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'wsse_secured' => array( + 'pattern' => '/api/.*', + 'wsse' => array( + 'lifetime' => 30, + ), + ), + ), + )); The rest is up to you! Any relevant configuration items can be defined in the factory and consumed or passed to the other classes in the container. diff --git a/cookbook/security/custom_provider.rst b/cookbook/security/custom_provider.rst index fd1c7015105..9a268fa060d 100644 --- a/cookbook/security/custom_provider.rst +++ b/cookbook/security/custom_provider.rst @@ -206,26 +206,66 @@ Now you make the user provider available as a service: Modify ``security.yml`` ----------------------- -In ``/app/config/security.yml`` everything comes together. Add the user provider +Everything comes together in your security configuration. Add the user provider to the list of providers in the "security" section. Choose a name for the user provider (e.g. "webservice") and mention the id of the service you just defined. -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + // app/config/security.yml + security: + providers: + webservice: + id: webservice_user_provider + + .. code-block:: xml - security: - providers: - webservice: - id: webservice_user_provider + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'providers' => array( + 'webservice' => array( + 'id' => 'webservice_user_provider', + ), + ), + )); Symfony also needs to know how to encode passwords that are supplied by website users, e.g. by filling in a login form. You can do this by adding a line to the -"encoders" section in ``/app/config/security.yml``. +"encoders" section in your security configuration: + +.. configuration-block:: + + .. code-block:: yaml -.. code-block:: yaml + # app/config/security.yml + security: + encoders: + Acme\WebserviceUserBundle\Security\User\WebserviceUser: sha512 - security: - encoders: - Acme\WebserviceUserBundle\Security\User\WebserviceUser: sha512 + .. code-block:: xml + + + + sha512 + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'encoders' => array( + 'Acme\WebserviceUserBundle\Security\User\WebserviceUser' => 'sha512', + ), + )); The value here should correspond with however the passwords were originally encoded when creating your users (however those users were created). When @@ -252,15 +292,42 @@ options, the password may be encoded multiple times and encoded to base64. Additionally, the hash, by default, is encoded multiple times and encoded to base64. For specific details, see `MessageDigestPasswordEncoder`_. - To prevent this, configure it in ``security.yml``: - - .. code-block:: yaml - - security: - encoders: - Acme\WebserviceUserBundle\Security\User\WebserviceUser: - algorithm: sha512 - encode_as_base64: false - iterations: 1 + To prevent this, configure it in your configuration file: + + .. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + encoders: + Acme\WebserviceUserBundle\Security\User\WebserviceUser: + algorithm: sha512 + encode_as_base64: false + iterations: 1 + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'encoders' => array( + 'Acme\WebserviceUserBundle\Security\User\WebserviceUser' => array( + 'algorithm' => 'sha512', + 'encode_as_base64' => false, + 'iterations' => 1, + ), + ), + )); .. _MessageDigestPasswordEncoder: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 7567cc47e3d..3671cc52538 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -251,6 +251,64 @@ then be checked against your User entity records in the database: access_control: - { path: ^/admin, roles: ROLE_ADMIN } + .. code-block:: xml + + + + + + ROLE_USER + ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'encoders' => array( + 'Acme\UserBundle\Entity\User' => array( + 'algorithm' => 'sha1', + 'encode_as_base64' => false, + 'iterations' => 1, + ), + ), + 'role_hierarchy' => array( + 'ROLE_ADMIN' => 'ROLE_USER', + 'ROLE_SUPER_ADMIN' => array('ROLE_USER', 'ROLE_ADMIN', 'ROLE_ALLOWED_TO_SWITCH'), + ), + 'providers' => array( + 'administrator' => array( + 'entity' => array( + 'class' => 'AcmeUserBundle:User', + 'property' => 'username', + ), + ), + ), + 'firewalls' => array( + 'admin_area' => array( + 'pattern' => '^/admin', + 'http_basic' => null, + ), + ), + 'access_control' => array( + array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), + ), + )); + The ``encoders`` section associates the ``sha1`` password encoder to the entity class. This means that Symfony will expect the password that's stored in the database to be encoded using this algorithm. For details on how to create @@ -416,6 +474,34 @@ of the ``security.yml`` file. administrators: entity: { class: AcmeUserBundle:User } # ... + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + ..., + 'providers' => array( + 'administrator' => array( + 'entity' => array( + 'class' => 'AcmeUserBundle:User', + ), + ), + ), + ..., + )); By doing this, the security layer will use an instance of ``UserRepository`` and call its ``loadUserByUsername()`` method to fetch a user from the database diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 10242b37ca3..c77f5c9d9eb 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -179,8 +179,26 @@ application configuration file with the following code. # app/config/security.yml security: access_decision_manager: - # Strategy can be: affirmative, unanimous or consensus + # strategy can be: affirmative, unanimous or consensus strategy: unanimous + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/security.xml + $container->loadFromExtension('security', array( + // strategy can be: affirmative, unanimous or consensus + 'access_decision_manager' => array( + 'strategy' => 'unanimous', + ), + )); + That's it! Now, when deciding whether or not a user should have access, the new voter will deny access to any user in the list of blacklisted IPs. diff --git a/cookbook/symfony1.rst b/cookbook/symfony1.rst index 12427c3f0eb..f7295d0807b 100644 --- a/cookbook/symfony1.rst +++ b/cookbook/symfony1.rst @@ -267,11 +267,35 @@ configuration inside a bundle must be included manually. For example, to include a routing resource from a bundle called ``AcmeDemoBundle``, you can do the following: -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + _hello: + resource: "@AcmeDemoBundle/Resources/config/routing.yml" + + .. code-block:: xml + + + + + - # app/config/routing.yml - _hello: - resource: "@AcmeDemoBundle/Resources/config/routing.yml" + + + + .. code-block:: php + + // app/config/routing.php + use Symfony\Component\Routing\RouteCollection; + + $collection = new RouteCollection(); + $collection->addCollection($loader->import("@AcmeHelloBundle/Resources/config/routing.php")); + + return $collection; This will load the routes found in the ``Resources/config/routing.yml`` file of the ``AcmeDemoBundle``. The special ``@AcmeDemoBundle`` is a shortcut syntax @@ -279,11 +303,25 @@ that, internally, resolves to the full path to that bundle. You can use this same strategy to bring in configuration from a bundle: -.. code-block:: yaml +.. configuration-block:: - # app/config/config.yml - imports: - - { resource: "@AcmeDemoBundle/Resources/config/config.yml" } + .. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: "@AcmeDemoBundle/Resources/config/config.yml" } + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/config.php + $this->import('@AcmeDemoBundle/Resources/config/config.php') In Symfony2, configuration is a bit like ``app.yml`` in symfony1, except much more systematic. With ``app.yml``, you could simply create any keys you wanted. @@ -300,10 +338,22 @@ used them in your application: In Symfony2, you can also create arbitrary entries under the ``parameters`` key of your configuration: -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + parameters: + email.from_address: foo.bar@example.com + + .. code-block:: xml + + + foo.bar@example.com + + + .. code-block:: php - parameters: - email.from_address: foo.bar@example.com + $container->setParameter('email.from_address', 'foo.bar@example.com'); You can now access this from a controller, for example:: diff --git a/cookbook/templating/global_variables.rst b/cookbook/templating/global_variables.rst index 93dddeaefb3..2059079ecf9 100644 --- a/cookbook/templating/global_variables.rst +++ b/cookbook/templating/global_variables.rst @@ -7,19 +7,39 @@ How to Inject Variables into all Templates (i.e. Global Variables) Sometimes you want a variable to be accessible to all the templates you use. This is possible inside your ``app/config/config.yml`` file: -.. code-block:: yaml +.. configuration-block:: - # app/config/config.yml - twig: - # ... - globals: - ga_tracking: UA-xxxxx-x + .. code-block:: yaml + + # app/config/config.yml + twig: + # ... + globals: + ga_tracking: UA-xxxxx-x + + .. code-block:: xml + + + + + UA-xxxxx-x + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('twig', array( + // ... + 'globals' => array( + 'ga_tracking' => 'UA-xxxxx-x', + ), + )); Now, the variable ``ga_tracking`` is available in all Twig templates: .. code-block:: html+jinja -

The google tracking code is: {{ ga_tracking }}

+

The google tracking code is: {{ ga_tracking }}

It's that easy! You can also take advantage of the built-in :ref:`book-service-container-parameters` system, which lets you isolate or reuse the value: @@ -30,12 +50,30 @@ system, which lets you isolate or reuse the value: [parameters] ga_tracking: UA-xxxxx-x -.. code-block:: yaml +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + twig: + globals: + ga_tracking: "%ga_tracking%" + + .. code-block:: xml + + + + %ga_tracking% + + + .. code-block:: php - # app/config/config.yml - twig: - globals: - ga_tracking: "%ga_tracking%" + // app/config/config.php + $container->loadFromExtension('twig', array( + 'globals' => array( + 'ga_tracking' => '%ga_tracking%', + ), + )); The same variable is available exactly as before. diff --git a/cookbook/testing/http_authentication.rst b/cookbook/testing/http_authentication.rst index 84249fdf458..0b00422e912 100644 --- a/cookbook/testing/http_authentication.rst +++ b/cookbook/testing/http_authentication.rst @@ -34,3 +34,23 @@ key in your firewall, along with the ``form_login`` key: firewalls: your_firewall_name: http_basic: + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/config_test.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'your_firewall_name' => array( + 'http_basic' => array(), + ), + ), + )); diff --git a/cookbook/web_services/php_soap_extension.rst b/cookbook/web_services/php_soap_extension.rst index 14b5afc8106..d629f907c9d 100644 --- a/cookbook/web_services/php_soap_extension.rst +++ b/cookbook/web_services/php_soap_extension.rst @@ -68,11 +68,19 @@ a ``HelloService`` object properly: - - - + + + + .. code-block:: php + + // app/config/config.php + $container + ->register('hello_service', 'Acme\SoapBundle\Services\HelloService') + ->addArgument(new Reference('mailer')); + + Below is an example of a controller that is capable of handling a SOAP request. If ``indexAction()`` is accessible via the route ``/soap``, then the WSDL document can be retrieved via ``/soap?wsdl``. @@ -125,53 +133,61 @@ An example WSDL is below. .. code-block:: xml - - - - - - - - - - - - - - - - Hello World - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + Hello World + + + + + + + + + + + + + + + + + + + + + + + + +