From 86ab47aaff52878deef6d395d86293434a9f6ca1 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 19 May 2017 10:26:22 -0400 Subject: [PATCH 1/2] more type changes --- _build/redirection_map | 2 + bundles/best_practices.rst | 9 +- bundles/extension.rst | 6 + console/command_in_controller.rst | 4 +- console/commands_as_services.rst | 167 ++-------- console/logging.rst | 3 + controller/service.rst | 342 ++++---------------- event_dispatcher.rst | 2 + logging/monolog_console.rst | 125 ++----- reference/configuration/framework.rst | 2 + reference/dic_tags.rst | 110 +++---- service_container.rst | 4 + service_container/configurators.rst | 90 +++--- service_container/debug.rst | 19 +- service_container/definitions.rst | 3 + service_container/expression_language.rst | 44 +-- service_container/factories.rst | 68 ++-- service_container/import.rst | 116 ++----- service_container/injection_types.rst | 7 +- service_container/lazy_services.rst | 11 +- service_container/optional_dependencies.rst | 38 +-- service_container/parameters.rst | 18 +- service_container/parent_services.rst | 89 +++-- service_container/request.rst | 48 +-- service_container/service_decoration.rst | 40 +-- service_container/shared.rst | 18 +- service_container/synthetic_services.rst | 5 +- service_container/tags.rst | 74 ++--- service_container/third_party.rst | 104 ------ session/locale_sticky_session.rst | 175 ++++------ session/proxy_examples.rst | 64 ++-- session/sessions_directory.rst | 115 +------ templating.rst | 5 - templating/embedding_controllers.rst | 12 +- templating/overriding.rst | 46 +-- templating/templating_service.rst | 74 ----- templating/twig_extension.rst | 100 +----- testing/database.rst | 12 +- validation/custom_constraint.rst | 53 +-- validation/raw_values.rst | 5 +- validation/translations.rst | 12 +- 41 files changed, 633 insertions(+), 1608 deletions(-) delete mode 100644 service_container/third_party.rst delete mode 100644 templating/templating_service.rst diff --git a/_build/redirection_map b/_build/redirection_map index bfdeeb4e42d..3ce14efbb69 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -336,3 +336,5 @@ /components/dependency_injection/autowiring /service_container/autowiring /event_dispatcher/class_extension /event_dispatcher /security/target_path /security +/service_container/third_party /service_container +/templating/templating_service /templates diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index 6985e358aaa..b4961b09793 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -384,7 +384,14 @@ If the bundle defines services, they must be prefixed with the bundle alias. For example, AcmeBlogBundle services must be prefixed with ``acme_blog``. In addition, services not meant to be used by the application directly, should -be :ref:`defined as private `. +be :ref:`defined as private `. For public services, +:ref:`aliases should be created ` from the interface/class +to the service id. For example, in MonlogBundle, an alias is created from +``Psr\Log\LoggerInterface`` to ``logger`` so that the ``LoggerInterface`` type-hint +can be used for autowiring. + +Services should not use autowiring or autoconfiguration. Instead, all services should +be defined explicitly. .. seealso:: diff --git a/bundles/extension.rst b/bundles/extension.rst index 7012bd1beed..da6381ac9cb 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -129,6 +129,12 @@ read more about it, see the ":doc:`/bundles/configuration`" article. Adding Classes to Compile ------------------------- +.. note:: + + The ``addClassesToCompile()`` method was deprecated in Symfony 3.3, and will + be removed in Symfony 4.0. If you want to use this method and be compatible + with Symfony 4.0, check to see if the method exists before calling it. + Symfony creates a big ``classes.php`` file in the cache directory to aggregate the contents of the PHP classes that are used in every request. This reduces the I/O operations and increases the application performance. diff --git a/console/command_in_controller.rst b/console/command_in_controller.rst index ee610478606..b995037660b 100644 --- a/console/command_in_controller.rst +++ b/console/command_in_controller.rst @@ -32,12 +32,12 @@ Run this command from inside your controller via:: use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\KernelInterface; class SpoolController extends Controller { - public function sendSpoolAction($messages = 10) + public function sendSpoolAction($messages = 10, KernelInterface $kernel) { - $kernel = $this->get('kernel'); $application = new Application($kernel); $application->setAutoExit(false); diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 012dbab5d45..d781eca5ca2 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -4,173 +4,64 @@ How to Define Commands as Services ================================== -By default, Symfony will take a look in the ``Command`` directory of each -bundle and automatically register your commands. If a command extends the -:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, -Symfony will even inject the container. -While making life easier, this has some limitations: +If you're using the :ref:`default services.yml configuration `, +your command classes are already registered as services. Great! This is the recommended +setup, but it's not required. Symfony also looks in the ``Command`` directory of +each bundle and automatically registers those classes as commands. -* Your command must live in the ``Command`` directory; -* There's no way to conditionally register your command based on the environment - or availability of some dependencies; -* You can't access the container in the ``configure()`` method (because - ``setContainer()`` hasn't been called yet); -* You can't use the same class to create many commands (i.e. each with - different configuration). +.. note:: -To solve these problems, you can register your command as a service and tag it -with ``console.command``: + You can also manually register your command as a service by configure the service + and :doc:`tagging it ` with ``console.command``. -.. configuration-block:: +In either case, if your class extends :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, +you can access public services via ``$this->getContainer()->get('SERVICE_ID')``. - .. code-block:: yaml +But if your class is registered as a service, you can instead access services by +using normal :ref:`dependency injection `. - # app/config/config.yml - services: - AppBundle\Command\MyCommand: [console.command] +For example, suppose you want to log something from within your command:: - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - use AppBundle\Command\MyCommand; - - $container->register(MyCommand::class) - ->addTag('console.command') - ; - -Using Dependencies and Parameters to Set Default Values for Options -------------------------------------------------------------------- - -Imagine you want to provide a default value for the ``name`` option. You could -pass one of the following as the 5th argument of ``addOption()``: - -* a hardcoded string; -* a container parameter (e.g. something from ``parameters.yml``); -* a value computed by a service (e.g. a repository). - -By extending ``ContainerAwareCommand``, only the first is possible, because you -can't access the container inside the ``configure()`` method. Instead, inject -any parameter or service you need into the constructor. For example, suppose you -store the default value in some ``%command.default_name%`` parameter:: - - // src/AppBundle/Command/GreetCommand.php namespace AppBundle\Command; + use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; - class GreetCommand extends Command + class SunshineCommand extends Command { - protected $defaultName; + private $logger; - public function __construct($defaultName) + public function __construct(LoggerInterface $logger) { - $this->defaultName = $defaultName; + $this->logger = $logger; + // you *must* call the parent constructor parent::__construct(); } protected function configure() { - // try to avoid work here (e.g. database query) - // this method is *always* called - see warning below - $defaultName = $this->defaultName; - $this - ->setName('demo:greet') - ->setDescription('Greet someone') - ->addOption( - 'name', - '-n', - InputOption::VALUE_REQUIRED, - 'Who do you want to greet?', - $defaultName - ) - ; + ->setName('app:sunshine') + ->setDescription('Hello PhpStorm'); } protected function execute(InputInterface $input, OutputInterface $output) { - $name = $input->getOption('name'); - - $output->writeln($name); + $this->logger->info('Waking up the sun'); + // ... } } -Now, just update the arguments of your service configuration like normal to -inject the ``command.default_name`` parameter: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - parameters: - command.default_name: Javier - - services: - AppBundle\Command\MyCommand: - arguments: ["%command.default_name%"] - tags: [console.command] - - .. code-block:: xml - - - - - - - Javier - - - - - %command.default_name% - - - - - - - .. code-block:: php - - // app/config/config.php - use AppBundle\Command\MyCommand; - - $container->setParameter('command.default_name', 'Javier'); - - $container - ->register(MyCommand::class) - ->setArguments(array('%command.default_name%')) - ->addTag('console.command') - ; - -Great, you now have a dynamic default value! +If you're using the :ref:`default services.yml configuration `, +the command class will automatically be registered as a service and passed the ``$logger`` +argument (thanks to autowiring). In other words, *just* by creating this class, everything +works! You can call the ``app:sunshine`` command and start logging. .. caution:: - Be careful not to actually do any work in ``configure`` (e.g. make database - queries), as your code will be run, even if you're using the console to - execute a different command. + You *do* have access to services in ``configure()``. However, try to avoid doing + any work (e.g. making database queries), as that code will be run, even if you're + using the console to execute a different command. diff --git a/console/logging.rst b/console/logging.rst index f2119bcb914..000165112b0 100644 --- a/console/logging.rst +++ b/console/logging.rst @@ -10,3 +10,6 @@ listener for the console. Starting from Symfony 3.3, the Console component provides automatic error and exception logging. + +You can of course also access and use the :doc:`logger ` service to +log messages. diff --git a/controller/service.rst b/controller/service.rst index ec19c44044e..dd49d0cbfae 100644 --- a/controller/service.rst +++ b/controller/service.rst @@ -4,109 +4,23 @@ How to Define Controllers as Services ===================================== -.. caution:: +In Symfony, a controller does *not* need to be registered as a service. But if you're +using the :ref:`default services.yml configuration `, +your controllers *are* already registered as services. This means you can use dependency +injection line any other normal service. - Defining controllers as services is **not officially recommended** by Symfony. - They are used by some developers for very specific use cases, such as - DDD (*domain-driven design*) and Hexagonal Architecture applications. +Referencing your Service from Routing +------------------------------------- -In the :doc:`/controller` guide, you've learned how easily a controller can be -used when it extends the base -:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class. While -this works fine, controllers can also be specified as services. Even if you don't -specify your controllers as services, you might see them being used in some -open-source Symfony bundles, so it may be useful to understand both approaches. +Registering your controller as a service is great, but you also need to make sure +that your routing references the service properly, so that Symfony knows to use it. -These are the main **advantages** of defining controllers as services: +If the service id is the fully-qualified class name (FQCN) of your controller, you're +done! You can use the normal ``AppBundle:Hello:index`` syntax in your routing and +it will find your service. -* The entire controller and any service passed to it can be modified via the - service container configuration. This is useful when developing reusable bundles; -* Your controllers are more "sandboxed". By looking at the constructor arguments, - it's easy to see what types of things this controller may or may not do; -* Since dependencies must be injected manually, it's more obvious when your - controller is becoming too big (i.e. if you have many constructor arguments). - -These are the main **drawbacks** of defining controllers as services: - -* It takes more work to create the controllers because they don't have - automatic access to the services or to the base controller shortcuts; -* The constructor of the controllers can rapidly become too complex because you - must inject every single dependency needed by them; -* The code of the controllers is more verbose because you can't use the shortcuts - of the base controller and you must replace them with some lines of code. - -The recommendation from the :doc:`best practices ` -is also valid for controllers defined as services: avoid putting your business -logic into the controllers. Instead, inject services that do the bulk of the work. - -Defining the Controller as a Service ------------------------------------- - -A controller can be defined as a service in the same way as any other class. -For example, if you have the following simple controller:: - - // src/AppBundle/Controller/HelloController.php - namespace AppBundle\Controller; - - use Symfony\Component\HttpFoundation\Response; - - class HelloController - { - public function indexAction($name) - { - return new Response('Hello '.$name.'!'); - } - } - -Then you can define it as a service as follows: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - AppBundle\Controller\HelloController: ~ - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\Controller\HelloController; - - $container->register(HelloController::class); - -Referring to the Service ------------------------- - -If the service id is the fully-qualified class name (FQCN) of your controller, -you can keep using the usual notation. For example, to forward to the -``indexAction()`` method of the above ``AppBundle\Controller\HelloController`` -service:: - - $this->forward('AppBundle:Hello:index', array('name' => $name)); - -Otherwise, use the single colon (``:``) notation. For example, to forward to the -``indexAction()`` method of a service with the id ``app.hello_controller``:: - - $this->forward('app.hello_controller:indexAction', array('name' => $name)); - -You can also route to the service by using the same notation when defining -the route ``_controller`` value: +But, if your service has a different id, you can use a special ``SERVICEID:METHOD`` +syntax: .. configuration-block:: @@ -117,6 +31,19 @@ the route ``_controller`` value: path: /hello defaults: { _controller: app.hello_controller:indexAction } + .. code-block:: php-annotations + + # src/AppBundle/Controller/HelloController.php + // ... + + /** + * @Route(service="app.hello_controller") + */ + class HelloController + { + // ... + } + .. code-block:: xml @@ -144,59 +71,26 @@ the route ``_controller`` value: You cannot drop the ``Action`` part of the method name when using the single colon notation. -.. tip:: - - You can also use annotations to configure routing using a controller - defined as a service. Make sure you specify the service ID in the - ``@Route`` annotation if your service ID is not your controller - fully-qualified class name (FQCN). See the - `FrameworkExtraBundle documentation`_ for details. - .. _controller-service-invoke: -.. tip:: +Invokable Controllers +--------------------- - If your controller implements the ``__invoke()`` method, you can simply - refer to the service id (``AppBundle\Controller\HelloController`` or - ``app.hello_controller`` for example). +If your controller implements the ``__invoke()`` method - popular with the +Action-Domain-Response (ADR) pattern, you can simply refer to the service id +(``AppBundle\Controller\HelloController`` or ``app.hello_controller`` for example). Alternatives to base Controller Methods --------------------------------------- -When using a controller defined as a service, it will most likely not extend -the base ``Controller`` class. Instead of relying on its shortcut methods, -you'll interact directly with the services that you need. Fortunately, this is -usually pretty easy and the base `Controller class source code`_ is a great -source on how to perform many common tasks. +When using a controller defined as a service, you can still extend any of the +:ref:`normal base controller ` classes and +use their shortcuts. But, you don't need to! You can choose to extend *nothing*, +and use dependency injection to access difference services. -For example, if you want to render a template instead of creating the ``Response`` -object directly, then your code would look like this if you were extending -Symfony's base controller:: - - // src/AppBundle/Controller/HelloController.php - namespace AppBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - - class HelloController extends Controller - { - public function indexAction($name) - { - return $this->render( - 'AppBundle:Hello:index.html.twig', - array('name' => $name) - ); - } - } - -If you look at the source code for the ``render()`` function in Symfony's -`base Controller class`_, you'll see that this method actually uses the -``templating`` service:: - - public function render($view, array $parameters = array(), Response $response = null) - { - return $this->container->get('templating')->renderResponse($view, $parameters, $response); - } +The base `Controller class source code`_ is a great way to see how to performance +simple tasks. For example, ``$this->render()`` is usually used to render a Twig +template and return a Response. But, you can also do this directly: In a controller that's defined as a service, you can instead inject the ``templating`` service and use it directly:: @@ -204,167 +98,41 @@ service and use it directly:: // src/AppBundle/Controller/HelloController.php namespace AppBundle\Controller; - use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; use Symfony\Component\HttpFoundation\Response; class HelloController { - private $templating; + private $twig; - public function __construct(EngineInterface $templating) + public function __construct(\Twig_Environment $twig) { - $this->templating = $templating; + $this->twig = $twig; } public function indexAction($name) { - return $this->templating->renderResponse( - 'AppBundle:Hello:index.html.twig', + $content = $this->twig->renderResponse( + 'hellp/index.html.twig', array('name' => $name) ); + + return new Response($content); } } -The service definition also needs modifying to specify the constructor -argument: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - AppBundle\Controller\HelloController: - arguments: ['@templating'] - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\Controller\HelloController; - use Symfony\Component\DependencyInjection\Reference; - - $container->register('app.hello_controller', HelloController::class) - ->addArgument(new Reference('templating')); - -Rather than fetching the ``templating`` service from the container, you can -inject *only* the exact service(s) that you need directly into the controller. - -.. note:: - - This does not mean that you cannot extend these controllers from your own - base controller. The move away from the standard base controller is because - its helper methods rely on having the container available which is not - the case for controllers that are defined as services. It may be a good - idea to extract common code into a service that's injected rather than - place that code into a base controller that you extend. Both approaches - are valid, exactly how you want to organize your reusable code is up to - you. +You can also use a special :ref:`action-based dependency injection ` +to receive services as arguments to your controller action methods. Base Controller Methods and Their Service Replacements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This list explains how to replace the convenience methods of the base -controller: - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createForm` (service: ``form.factory``) - .. code-block:: php - - $formFactory->create($type, $data, $options); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createFormBuilder` (service: ``form.factory``) - .. code-block:: php - - $formFactory->createBuilder('form', $data, $options); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createNotFoundException` - .. code-block:: php - - new NotFoundHttpException($message, $previous); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward` (service: ``http_kernel``) - .. code-block:: php - - use Symfony\Component\HttpKernel\HttpKernelInterface; - // ... - - $request = ...; - $attributes = array_merge($path, array('_controller' => $controller)); - $subRequest = $request->duplicate($query, null, $attributes); - $httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::generateUrl` (service: ``router``) - .. code-block:: php - - $router->generate($route, $params, $referenceType); - - .. note:: - - The ``$referenceType`` argument must be one of the constants defined - in the :class:`Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface`. - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::getDoctrine` (service: ``doctrine``) - *Simply inject doctrine instead of fetching it from the container.* - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::getUser` (service: ``security.token_storage``) - .. code-block:: php - - $user = null; - $token = $tokenStorage->getToken(); - if (null !== $token && is_object($token->getUser())) { - $user = $token->getUser(); - } - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isGranted` (service: ``security.authorization_checker``) - .. code-block:: php - - $authChecker->isGranted($attributes, $object); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::redirect` - .. code-block:: php - - use Symfony\Component\HttpFoundation\RedirectResponse; - - return new RedirectResponse($url, $status); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::render` (service: ``templating``) - .. code-block:: php - - $templating->renderResponse($view, $parameters, $response); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::renderView` (service: ``templating``) - .. code-block:: php - - $templating->render($view, $parameters); - -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::stream` (service: ``templating``) - .. code-block:: php - - use Symfony\Component\HttpFoundation\StreamedResponse; - - $templating = $this->templating; - $callback = function () use ($templating, $view, $parameters) { - $templating->stream($view, $parameters); - }; +The best way to see how to replace base ``Controller`` convenience methods is to +look at the `ControllerTrait`_ that holds its logic. - return new StreamedResponse($callback); +If you want to know what type-hints to use for each service, see the +``getSubscribedEvents`` in `AbstractController`_. -.. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php -.. _`base Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php -.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#controller-as-service +.. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +.. _`base Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +.. _`ControllerTrait`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +.. _`AbstractController`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 3161c99b6d1..a12f7e00125 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -124,6 +124,8 @@ using a special "tag": of the internal Symfony listeners usually range from ``-255`` to ``255`` but your own listeners can use any positive or negative integer. +.. _events-subscriber: + Creating an Event Subscriber ---------------------------- diff --git a/logging/monolog_console.rst b/logging/monolog_console.rst index 0d97e3119fb..88ff07c0a04 100644 --- a/logging/monolog_console.rst +++ b/logging/monolog_console.rst @@ -9,11 +9,6 @@ It is possible to use the console to print messages for certain :class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance that is passed when a command gets executed. -.. seealso:: - Alternatively, you can use the - :doc:`standalone PSR-3 logger ` provided with - the console component. - When a lot of logging has to happen, it's cumbersome to print information depending on the verbosity settings (``-v``, ``-vv``, ``-vvv``) because the calls need to be wrapped in conditions. The code quickly gets verbose or dirty. @@ -58,65 +53,25 @@ the console. If they are displayed, they are timestamped and colored appropriate Additionally, error logs are written to the error output (php://stderr). There is no need to conditionally handle the verbosity settings anymore. -The Monolog console handler is enabled in the Monolog configuration. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - monolog: - handlers: - console: - type: console - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('monolog', array( - 'handlers' => array( - 'console' => array( - 'type' => 'console', - ), - ), - )); - -With the ``verbosity_levels`` option you can adapt the mapping between -verbosity and log level. In the given example it will also show notices in -normal verbosity mode (instead of warnings only). Additionally, it will only -use messages logged with the custom ``my_channel`` channel and it changes the -display style via a custom formatter (see the -:doc:`MonologBundle reference ` for more -information): +The Monolog console handler is enabled by default in the Symfony Framework. For +example, in ``config_dev.yml``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # app/config/config_dev.yml monolog: handlers: + # ... console: type: console - verbosity_levels: - VERBOSITY_NORMAL: NOTICE - channels: my_channel - formatter: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter + process_psr_3_messages: false + channels: ['!event', '!doctrine', '!console'] + + # optionally configure the mapping between verbosity levels and log levels + # verbosity_levels: + # VERBOSITY_NORMAL: NOTICE .. code-block:: xml @@ -129,9 +84,14 @@ information): http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Symfony\Bridge\Monolog\Formatter\ConsoleFormatter + + + + + !event + !doctrine + !console + @@ -139,56 +99,19 @@ information): .. code-block:: php // app/config/config.php - use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter; - $container->loadFromExtension('monolog', array( 'handlers' => array( 'console' => array( - 'type' => 'console', - 'verbosity_levels' => array( - 'VERBOSITY_NORMAL' => 'NOTICE', - ), - 'channels' => 'my_channel', - 'formatter' => ConsoleFormatter::class, + 'type' => 'console', + 'process_psr_3_messages' => false, + 'channels' => array('!event', '!doctrine', '!console'), ), ), )); -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - Symfony\Bridge\Monolog\Formatter\ConsoleFormatter: - arguments: - - "[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n" - - .. code-block:: xml - - - - - - - - [%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n - - - - - .. code-block:: php - - // app/config/services.php - use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter; - - $container - ->autowire(ConsoleFormatter::class) - ->addArgument('[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n') - ; +Now, log messages will be shown on the console based on the log levels and verbosity. +By default (normal verbosity level), warnings and higher will be shown. But in +:doc:`full verbostiy mode `, all messages will be shown. .. _ConsoleHandler: https://github.com/symfony/MonologBridge/blob/master/Handler/ConsoleHandler.php .. _MonologBridge: https://github.com/symfony/MonologBridge diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 0c183b8b510..5d5887c1450 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -318,6 +318,8 @@ setting should be present in your ``test`` environment (usually via For more information, see :doc:`/testing`. +.. _config-framework-default_locale: + default_locale ~~~~~~~~~~~~~~ diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 7e883eb157c..d6ecf67aa3d 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -91,7 +91,8 @@ And then register it as a tagged service: .. code-block:: yaml services: - AppBundle\Assetic\CustomWorker: [assetic.factory_worker] + AppBundle\Assetic\CustomWorker: + tags: [assetic.factory_worker] .. code-block:: xml @@ -457,10 +458,11 @@ service class:: { // clear your cache } - } -Then register this class and tag it with ``kernel.cache_clearer``: +If you're using the :ref:`default services.yml configuration `, +your service will be automatically tagged with ``kernel.cache_clearer``. But, you +can also register it manually: .. configuration-block:: @@ -489,7 +491,8 @@ Then register this class and tag it with ``kernel.cache_clearer``: use AppBundle\Cache\MyClearer; - $container->register(MyClearer::class) + $container + ->register(MyClearer::class) ->addTag('kernel.cache_clearer') ; @@ -538,8 +541,9 @@ application without calling this cache warmer. In Symfony, optional warmers are always executed by default (you can change this by using the ``--no-optional-warmers`` option when executing the command). -To register your warmer with Symfony, give it the ``kernel.cache_warmer`` -tag: +If you're using the :ref:`default services.yml configuration `, +your service will be automatically tagged with ``kernel.cache_warmer``. But, you +can also register it manually: .. configuration-block:: @@ -569,7 +573,8 @@ tag: use AppBundle\Cache\MyCustomWarmer; - $container->register(MyCustomWarmer::class) + $container + ->register(MyCustomWarmer::class) ->addTag('kernel.cache_warmer', array('priority' => 0)) ; @@ -625,48 +630,8 @@ kernel.event_subscriber **Purpose**: To subscribe to a set of different events/hooks in Symfony -To enable a custom subscriber, add it as a regular service in one of your -configuration and tag it with ``kernel.event_subscriber``: - -.. configuration-block:: - - .. code-block:: yaml - - services: - AppBundle\EventListener\CustomSubscriber: [kernel.event_subscriber] - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - use AppBundle\EventListener\CustomSubscriber; - - $container->register(CustomSubscriber::class) - ->addTag('kernel.event_subscriber') - ; - -.. note:: - - Your service must implement the :class:`Symfony\\Component\\EventDispatcher\\EventSubscriberInterface` - interface. - -.. note:: - - If your service is created by a factory, you **MUST** correctly set - the ``class`` parameter for this tag to work correctly. +This is an alternative way to create an event listener, and is the recommended +way (instead of using ``kernel.event_listener``). See :ref:`events-subscriber`. kernel.fragment_renderer ------------------------ @@ -752,7 +717,8 @@ You can add a processor globally: .. code-block:: yaml services: - Monolog\Processor\IntrospectionProcessor: [monolog.processor] + Monolog\Processor\IntrospectionProcessor: + tags: [monolog.processor] .. code-block:: xml @@ -773,7 +739,8 @@ You can add a processor globally: use Monolog\Processor\IntrospectionProcessor; - $container->register(IntrospectionProcessor::class) + $container + ->register(IntrospectionProcessor::class) ->addTag('monolog.processor') ; @@ -813,7 +780,8 @@ attribute: use Monolog\Processor\IntrospectionProcessor; - $container->register(IntrospectionProcessor::class) + $container + ->register(IntrospectionProcessor::class) ->addTag('monolog.processor', array('handler' => 'firephp')) ; @@ -849,7 +817,8 @@ You can also add a processor for a specific logging channel by using the use Monolog\Processor\IntrospectionProcessor; - $container->register(IntrospectionProcessor::class) + $container + ->register(IntrospectionProcessor::class) ->addTag('monolog.processor', array('channel' => 'security')) ; @@ -871,7 +840,8 @@ of your configuration and tag it with ``routing.loader``: .. code-block:: yaml services: - AppBundle\Routing\CustomLoader: [routing.loader] + AppBundle\Routing\CustomLoader: + tags: [routing.loader] .. code-block:: xml @@ -1030,8 +1000,7 @@ templates): use AppBundle\Templating\AppHelper; - $container - ->register(AppHelper::class) + $container->register(AppHelper::class) ->addTag('templating.helper', array('alias' => 'alias_name')) ; @@ -1080,7 +1049,8 @@ Now, register your loader as a service and tag it with ``translation.loader``: use AppBundle\Translation\MyCustomLoader; - $container->register(MyCustomLoader::class) + $container + ->register(MyCustomLoader::class) ->addTag('translation.loader', array('alias' => 'bin')) ; @@ -1245,14 +1215,17 @@ twig.extension **Purpose**: To register a custom Twig Extension To enable a Twig extension, add it as a regular service in one of your -configuration and tag it with ``twig.extension``: +configuration and tag it with ``twig.extension``. If you're using the +:ref:`default services.yml configuration `, +the service is auto-registered and auto-tagged. But, you can also register it manually: .. configuration-block:: .. code-block:: yaml services: - AppBundle\Twig\AppExtension: [twig.extension] + AppBundle\Twig\AppExtension: + tags: [twig.extension] .. code-block:: xml @@ -1273,7 +1246,8 @@ configuration and tag it with ``twig.extension``: use AppBundle\Twig\AppExtension; - $container->register(AppExtension::class) + $container + ->register(AppExtension::class) ->addTag('twig.extension') ; @@ -1292,8 +1266,7 @@ also have to be added as regular services: .. code-block:: yaml services: - twig.extension.intl: - class: Twig_Extensions_Extension_Intl + Twig_Extensions_Extension_Intl: tags: [twig.extension] .. code-block:: xml @@ -1305,7 +1278,7 @@ also have to be added as regular services: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -1314,7 +1287,7 @@ also have to be added as regular services: .. code-block:: php $container - ->register('twig.extension.intl', 'Twig_Extensions_Extension_Intl') + ->register('Twig_Extensions_Extension_Intl') ->addTag('twig.extension') ; @@ -1326,7 +1299,11 @@ twig.loader By default, Symfony uses only one `Twig Loader`_ - :class:`Symfony\\Bundle\\TwigBundle\\Loader\\FilesystemLoader`. If you need to load Twig templates from another resource, you can create a service for -the new loader and tag it with ``twig.loader``: +the new loader and tag it with ``twig.loader``. + +If you use the :ref:`default services.yml configuration `, +the service will be automatically tagged thanks to autoconfiguration. But, you can +also register it manually: .. configuration-block:: @@ -1356,7 +1333,8 @@ the new loader and tag it with ``twig.loader``: use AppBundle\Twig\CustomLoader; - $container->register(CustomLoader::class) + $container + ->register(CustomLoader::class) ->addTag('twig.loader', array('priority' => 0)) ; diff --git a/service_container.rst b/service_container.rst index 246cb1a3581..2ceaca349f1 100644 --- a/service_container.rst +++ b/service_container.rst @@ -492,6 +492,10 @@ Thanks to this, the container will pass ``manager@example.com`` as the third arg to ``__construct`` when creating the ``SiteUpdateManager`` service. The other arguments will still be autowired. +But, isn't this fragile? Fortunately, no! If you rename the ``$adminEmail`` argument +to something else - e.g. ``$mainEmail`` - you will get a clear exception when you +reload the next page (even if that page doesn't use this service). + .. _service-container-parameters: Service Parameters diff --git a/service_container/configurators.rst b/service_container/configurators.rst index e3adc54534b..d783e8b7686 100644 --- a/service_container/configurators.rst +++ b/service_container/configurators.rst @@ -21,10 +21,12 @@ of emails to users. Emails are passed through different formatters that could be enabled or not depending on some dynamic application settings. You start defining a ``NewsletterManager`` class like this:: + // src/AppBundle/Mail/NewsletterManager.php + namespace AppBundle\Mail; + class NewsletterManager implements EmailFormatterAwareInterface { - protected $mailer; - protected $enabledFormatters; + private $enabledFormatters; public function setEnabledFormatters(array $enabledFormatters) { @@ -36,10 +38,12 @@ You start defining a ``NewsletterManager`` class like this:: and also a ``GreetingCardManager`` class:: + // src/AppBundle/Mail/GreetingCardManager.php + namespace AppBundle\Mail; + class GreetingCardManager implements EmailFormatterAwareInterface { - protected $mailer; - protected $enabledFormatters; + private $enabledFormatters; public function setEnabledFormatters(array $enabledFormatters) { @@ -54,9 +58,11 @@ on application settings. To do this, you also have an ``EmailFormatterManager`` class which is responsible for loading and validating formatters enabled in the application:: + // src/AppBundle/Mail/EmailFormatterManager.php + namespace AppBundle\Mail; + class EmailFormatterManager { - // ... public function getEnabledFormatters() @@ -74,6 +80,9 @@ If your goal is to avoid having to couple ``NewsletterManager`` and ``GreetingCardManager`` with ``EmailFormatterManager``, then you might want to create a configurator class to configure these instances:: + // src/AppBundle/Mail/EmailConfigurator.php + namespace AppBundle\Mail; + class EmailConfigurator { private $formatterManager; @@ -107,7 +116,10 @@ how to load them, keeping the single responsibility principle. Using the Configurator ---------------------- -You can configure the service configurator using the ``configurator`` option: +You can configure the service configurator using the ``configurator`` option. If +you're using the :ref:`default services.yml configuration `, +all the classes are already loaded as services. All you need to do is specify the +``configurator``: .. configuration-block:: @@ -115,24 +127,19 @@ You can configure the service configurator using the ``configurator`` option: # app/config/services.yml services: - app.email_formatter_manager: - class: EmailFormatterManager - # ... + # ... - app.email_configurator: - class: AppBundle\Mail\EmailConfigurator - arguments: ['@app.email_formatter_manager'] + # Registers all 4 classes as services, including AppBundle\Mail\EmailConfigurator + AppBundle\: + resource: '../../src/AppBundle/*' # ... - app.newsletter_manager: - class: AppBundle\Mail\NewsletterManager - arguments: ['@mailer'] - configurator: 'app.email_configurator:configure' + # override the services to set the configurator + AppBundle\Mail\NewsletterManager: + configurator: 'AppBundle\Mail\EmailConfigurator:configure' - app.greeting_card_manager: - class: AppBundle\Mail\GreetingCardManager - arguments: ['@mailer'] - configurator: 'app.email_configurator:configure' + AppBundle\Mail\GreetingCardManager: + configurator: 'AppBundle\Mail\EmailConfigurator:configure' .. code-block:: xml @@ -144,25 +151,14 @@ You can configure the service configurator using the ``configurator`` option: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - + - - - + + - - - - - - - - - - + + @@ -177,16 +173,14 @@ You can configure the service configurator using the ``configurator`` option: use Symfony\Component\DependencyInjection\Reference; // ... - $container->register('app.email_formatter_manager', EmailFormatterManager::class); - $container->register('app.email_configurator', EmailConfigurator::class); + $container->autowire(EmailFormatterManager::class); + $container->autowire(EmailConfigurator::class); - $container->register('app.newsletter_manager', NewsletterManager::class) - ->addArgument(new Reference('mailer')) - ->setConfigurator(array(new Reference('app.email_configurator'), 'configure')); + $container->autowire(NewsletterManager::class) + ->setConfigurator(array(new Reference(EmailConfigurator::class), 'configure')); - $container->register('app.greeting_card_manager', GreetingCardManager::class) - ->addArgument(new Reference('mailer')) - ->setConfigurator(array(new Reference('app.email_configurator'), 'configure')); + $container->autowire(GreetingCardManager::class) + ->setConfigurator(array(new Reference(EmailConfigurator::class), 'configure')); .. versionadded:: 3.2 @@ -200,10 +194,10 @@ You can configure the service configurator using the ``configurator`` option: app.newsletter_manager: # new syntax - configurator: 'app.email_configurator:configure' + configurator: 'AppBundle\Mail\EmailConfigurator:configure' # old syntax - configurator: ['@app.email_configurator', configure] + configurator: ['@AppBundle\Mail\EmailConfigurator', configure] -That's it! When requesting the ``app.newsletter_manager`` or -``app.greeting_card_manager`` service, the created instance will first be +That's it! When requesting the ``AppBundle\Mail\NewsletterManager`` or +``AppBundle\Mail\GreetingCardManager`` service, the created instance will first be passed to the ``EmailConfigurator::configure()`` method. diff --git a/service_container/debug.rst b/service_container/debug.rst index 5815b5cc94b..2ae3b167d4d 100644 --- a/service_container/debug.rst +++ b/service_container/debug.rst @@ -18,22 +18,27 @@ By default, only public services are shown, but you can also view private servic $ php bin/console debug:container --show-private -.. note:: +To see a list of all of the available types that can be used for autowiring, run: - If a private service is only used as an argument to just *one* other service, - it won't be displayed by the ``debug:container`` command, even when using - the ``--show-private`` option. See :ref:`Inline Private Services ` - for more details. +.. code-block:: terminal + + $ php bin/console debug:container --types + +.. versionadded:: 3.3 + The ``--types`` option was introduced in Symfony 3.3. + +Detailed Info about a Single Service +------------------------------------ You can get more detailed information about a particular service by specifying its id: .. code-block:: terminal - $ php bin/console debug:container app.mailer + $ php bin/console debug:container 'AppBundle\Service\Mailer' # to show the service arguments: - $ php bin/console debug:container app.mailer --show-arguments + $ php bin/console debug:container 'AppBundle\Service\Mailer' --show-arguments .. versionadded:: 3.3 The ``--show-arguments`` option was introduced in Symfony 3.3. diff --git a/service_container/definitions.rst b/service_container/definitions.rst index 6f214843edd..8a159eeb3a0 100644 --- a/service_container/definitions.rst +++ b/service_container/definitions.rst @@ -38,6 +38,9 @@ There are some helpful methods for working with the service definitions:: // shortcut for the previous method $container->register('app.number_generator', \AppBundle\NumberGenerator::class); + // or create a service whose id matches its class + $container->register(\AppBundle\NumberGenerator::class); + Working with a Definition ------------------------- diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index 9889485cd56..f61fb910653 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -10,15 +10,12 @@ How to Inject Values Based on Complex Expressions The service container also supports an "expression" that allows you to inject very specific values into a service. -For example, suppose you have a third service (not shown here), called ``mailer_configuration``, -which has a ``getMailerMethod()`` method on it, which will return a string -like ``sendmail`` based on some configuration. Remember that the first argument -to the ``my_mailer`` service is the simple string ``sendmail``: +For example, suppose you have a service (not shown here), called ``AppBundle\Mail\MailerConfiguration``, +which has a ``getMailerMethod()`` method on it. This returns a string - like ``sendmail`` +based on some configuration. -.. include:: /_includes/service_container/_my_mailer.rst.inc - -But instead of hardcoding this, how could we get this value from the ``getMailerMethod()`` -of the new ``mailer_configuration`` service? One way is to use an expression: +Suppose that you want to pass the result of this method as a constructor argument +to another service: ``AppBundle\Mailer``. One way to do this is with an expression: .. configuration-block:: @@ -26,9 +23,12 @@ of the new ``mailer_configuration`` service? One way is to use an expression: # app/config/config.yml services: - my_mailer: - class: AppBundle\Mailer - arguments: ["@=service('mailer_configuration').getMailerMethod()"] + # ... + + AppBundle\Mail\MailerConfiguration: ~ + + AppBundle\Mailer: + arguments: ["@=service('AppBundle\Mail\MailerConfiguration').getMailerMethod()"] .. code-block:: xml @@ -40,8 +40,12 @@ of the new ``mailer_configuration`` service? One way is to use an expression: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - service('mailer_configuration').getMailerMethod() + + + + + + service('AppBundle\Mail\MailerConfiguration').getMailerMethod() @@ -49,11 +53,14 @@ of the new ``mailer_configuration`` service? One way is to use an expression: .. code-block:: php // app/config/config.php + use AppBundle\Mail\MailerConfiguration; use AppBundle\Mailer; use Symfony\Component\ExpressionLanguage\Expression; + + $container->autowire(AppBundle\Mail\MailerConfiguration::class); - $container->register('my_mailer', Mailer::class) - ->addArgument(new Expression('service("mailer_configuration").getMailerMethod()')); + $container->autowire(Mailer::class) + ->addArgument(new Expression('service("AppBundle\Mail\MailerConfiguration").getMailerMethod()')); To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. @@ -72,8 +79,7 @@ via a ``container`` variable. Here's another example: .. code-block:: yaml services: - my_mailer: - class: AppBundle\Mailer + AppBundle\Mailer: arguments: ["@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'"] .. code-block:: xml @@ -85,7 +91,7 @@ via a ``container`` variable. Here's another example: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + container.hasParameter('some_param') ? parameter('some_param') : 'default_value' @@ -96,7 +102,7 @@ via a ``container`` variable. Here's another example: use AppBundle\Mailer; use Symfony\Component\ExpressionLanguage\Expression; - $container->register('my_mailer', Mailer::class) + $container->autowire(Mailer::class) ->addArgument(new Expression( "container.hasParameter('some_param') ? parameter('some_param') : 'default_value'" )); diff --git a/service_container/factories.rst b/service_container/factories.rst index 1d735e7e752..bd765afb2b6 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -36,10 +36,10 @@ configure the service container to use the .. code-block:: yaml # app/config/services.yml - services: - app.newsletter_manager: - class: AppBundle\Email\NewsletterManager + # ... + + AppBundle\Email\NewsletterManager: # call the static method factory: ['AppBundle\Email\NewsletterManagerStaticFactory', createNewsletterManager] @@ -54,7 +54,7 @@ configure the service container to use the http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -66,17 +66,18 @@ configure the service container to use the // app/config/services.php use AppBundle\Email\NewsletterManager; + use AppBundle\NumberGenerator; use AppBundle\Email\NewsletterManagerStaticFactory; // ... - $container->register('app.newsletter_manager', \AppBundle\NumberGenerator::class) + $container->register(NumberGenerator::class) // call the static method ->setFactory(array(NewsletterManagerStaticFactory::class, 'createNewsletterManager')); .. note:: - When using a factory to create services, the value chosen for the ``class`` - option has no effect on the resulting service. The actual class name + When using a factory to create services, the value chosen for class + has no effect on the resulting service. The actual class name only depends on the object that is returned by the factory. However, the configured class name may be used by compiler passes and therefore should be set to a sensible value. @@ -95,13 +96,13 @@ Configuration of the service container then looks like this: # app/config/services.yml services: - app.newsletter_manager_factory: - class: AppBundle\Email\NewsletterManagerFactory + # ... - app.newsletter_manager: - class: AppBundle\Email\NewsletterManager + AppBundle\Email\NewsletterManagerFactory: ~ + + AppBundle\Email\NewsletterManager: # call a method on the specified factory service - factory: 'app.newsletter_manager_factory:createNewsletterManager' + factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' .. code-block:: xml @@ -114,13 +115,11 @@ Configuration of the service container then looks like this: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - + - @@ -135,12 +134,12 @@ Configuration of the service container then looks like this: use AppBundle\Email\NewsletterManagerFactory; // ... - $container->register('app.newsletter_manager_factory', NewsletterManagerFactory::class); + $container->register(NewsletterManagerFactory::class); - $container->register('app.newsletter_manager', NewsletterManager::class) + $container->register(NewsletterManager::class) // call a method on the specified factory service ->setFactory(array( - new Reference('app.newsletter_manager_factory'), + new Reference(NewsletterManagerFactory::class), 'createNewsletterManager', )); @@ -155,18 +154,23 @@ Configuration of the service container then looks like this: app.newsletter_manager: # new syntax - factory: 'app.newsletter_manager_factory:createNewsletterManager' + factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' # old syntax - factory: ['@app.newsletter_manager_factory', createNewsletterManager] + factory: ['@AppBundle\Email\NewsletterManagerFactory', createNewsletterManager] .. _factories-passing-arguments-factory-method: Passing Arguments to the Factory Method --------------------------------------- -If you need to pass arguments to the factory method, you can use the ``arguments`` -options inside the service container. For example, suppose the ``createNewsletterManager()`` -method in the previous example takes the ``templating`` service as an argument: +.. tip:: + + Arguments to your factory method are :ref:`autowired ` if + that's enabled for your service. + +If you need to pass arguments to the factory method you can use the ``arguments`` +options. For example, suppose the ``createNewsletterManager()`` method in the previous +example takes the ``templating`` service as an argument: .. configuration-block:: @@ -177,9 +181,8 @@ method in the previous example takes the ``templating`` service as an argument: services: # ... - app.newsletter_manager: - class: AppBundle\Email\NewsletterManager - factory: 'newsletter_manager_factory:createNewsletterManager' + AppBundle\Email\NewsletterManager: + factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' arguments: ['@templating'] .. code-block:: xml @@ -195,8 +198,8 @@ method in the previous example takes the ``templating`` service as an argument: - - + + @@ -207,12 +210,13 @@ method in the previous example takes the ``templating`` service as an argument: // app/config/services.php use AppBundle\Email\NewsletterManager; + use AppBundle\Email\NewsletterManagerFactory; use Symfony\Component\DependencyInjection\Reference; // ... - $container->register('app.newsletter_manager', NewsletterManager::class) + $container->register(NewsletterManager::class) ->addArgument(new Reference('templating')) ->setFactory(array( - new Reference('app.newsletter_manager_factory'), + new Reference(NewsletterManagerFactory::class), 'createNewsletterManager', )); diff --git a/service_container/import.rst b/service_container/import.rst index bc9da428f28..9ae5b1bd790 100644 --- a/service_container/import.rst +++ b/service_container/import.rst @@ -8,9 +8,8 @@ How to Import Configuration Files/Resources .. tip:: In this section, service configuration files are referred to as *resources*. - This is to highlight the fact that, while most configuration resources - will be files (e.g. YAML, XML, PHP), Symfony is so flexible that configuration - could be loaded from anywhere (e.g. a database or even via an external + While most configuration resources are files (e.g. YAML, XML, PHP), Symfony is + able to load configuration from anywhere (e.g. a database or even via an external web service). The service container is built using a single configuration resource @@ -33,11 +32,9 @@ methods. Importing Configuration with ``imports`` ---------------------------------------- -So far, you've placed your ``app.mailer`` service container definition directly -in the services configuration file (e.g. ``app/config/services.yml``). If your -application ends up having many services, this file becomes huge and hard to -maintain. To avoid this, you can split your service configuration into multiple -service files: +By default, service configuration lives in ``app/config/services.yml``. But if that +file becomes large, you're free to organize into multiple files. For suppose you +decided to move some configuration to a new file: .. configuration-block:: @@ -45,12 +42,10 @@ service files: # app/config/services/mailer.yml parameters: - app.mailer.transport: sendmail + # ... some parameters services: - app.mailer: - class: AppBundle\Mailer - arguments: ['%app.mailer.transport%'] + # ... some services .. code-block:: xml @@ -62,31 +57,22 @@ service files: http://symfony.com/schema/dic/services/services-1.0.xsd"> - sendmail + - - %app.mailer.transport% - + .. code-block:: php // app/config/services/mailer.php - use AppBundle\Mailer; - $container->setParameter('app.mailer.transport', 'sendmail'); - - $container->register('app.mailer', Mailer::class) - ->addArgument('%app.mailer.transport%'); - -The definition itself hasn't changed, only its location. To make the service -container load the definitions in this resource file, use the ``imports`` key -in any already loaded resource (e.g. ``app/config/services.yml`` or -``app/config/config.yml``): + // ... some parameters + // ... some services +To import this file, use the ``imports`` key from a file that *is* loaded: .. configuration-block:: .. code-block:: yaml @@ -128,72 +114,18 @@ Importing Configuration via Container Extensions ------------------------------------------------ Third-party bundle container configuration, including Symfony core services, -are usually loaded using another method that's more flexible and easy to -configure in your application. - -Internally, each bundle defines its services like you've seen so far. However, -these files aren't imported using the ``import`` directive. These bundles use a -*dependency injection extension* to load the files. The extension also allows -bundles to provide configuration to dynamically load some services. - -Take the FrameworkBundle - the core Symfony Framework bundle - as an -example. The presence of the following code in your application configuration -invokes the service container extension inside the FrameworkBundle: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - secret: xxxxxxxxxx - form: true - # ... - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - 'secret' => 'xxxxxxxxxx', - 'form' => array(), - - // ... - )); - -When the resources are parsed, the container looks for an extension that -can handle the ``framework`` directive. The extension in question, which lives -in the FrameworkBundle, is invoked and the service configuration for the -FrameworkBundle is loaded. - -The settings under the ``framework`` directive (e.g. ``form: true``) indicate -that the extension should load all services related to the Form component. If -form was disabled, these services wouldn't be loaded and Form integration would -not be available. +are usually loaded using another method: a container extension. -When installing or configuring a bundle, see the bundle's documentation for -how the services for the bundle should be installed and configured. The options -available for the core bundles can be found inside the :doc:`Reference Guide `. +Internally, each bundle defines its services in files like you've seen so far. +However, these files aren't imported using the ``import`` directive. Instead, bundles +use a *dependency injection extension* to load the files automatically. As soon +as you enable a bundle, its extension is called, which is able to load service +configuration files. -.. seealso:: +In fact, each configuration block in ``config.yml`` - e.g. ``framework`` or ``twig``- +is passed to the extension for that bundle - e.g. ``FrameworkBundle`` or ``TwigBundle`` - +and used to configure those services further. - If you want to use dependency injection extensions in your own shared - bundles and provide user friendly configuration, take a look at the - :doc:`/bundles/extension` article. +If you want to use dependency injection extensions in your own shared +bundles and provide user friendly configuration, take a look at the +:doc:`/bundles/extension` article. diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index ab6b2a64bf4..d2afc93b044 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -19,12 +19,12 @@ The most common way to inject dependencies is via a class's constructor. To do this you need to add an argument to the constructor signature to accept the dependency:: - namespace AppBundle\Mail\NewsletterManager; + namespace AppBundle\Mail; // ... class NewsletterManager { - protected $mailer; + private $mailer; public function __construct(MailerInterface $mailer) { @@ -44,8 +44,7 @@ service container configuration: services: # ... - app.newsletter_manager: - class: NewsletterManager + AppBundle\Mail\NewsletterManager: arguments: ['@mailer'] .. code-block:: xml diff --git a/service_container/lazy_services.rst b/service_container/lazy_services.rst index 830dcea691c..2b0ab1ee960 100644 --- a/service_container/lazy_services.rst +++ b/service_container/lazy_services.rst @@ -4,6 +4,10 @@ Lazy Services ============= +.. seealso:: + + Another way to inject services lazily is via a :doc:`service locator `. + Why Lazy Services? ------------------ @@ -48,8 +52,7 @@ You can mark the service as ``lazy`` by manipulating its definition: .. code-block:: yaml services: - app.twig_extension: - class: AppBundle\Twig\AppExtension + AppBundle\Twig\AppExtension: lazy: true .. code-block:: xml @@ -61,7 +64,7 @@ You can mark the service as ``lazy`` by manipulating its definition: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -69,7 +72,7 @@ You can mark the service as ``lazy`` by manipulating its definition: use AppBundle\Twig\AppExtension; - $container->register('app.twig_extension', AppExtension::class) + $container->register(AppExtension::class) ->setLazy(true); Once you inject the service into another service, a virtual `proxy`_ with the diff --git a/service_container/optional_dependencies.rst b/service_container/optional_dependencies.rst index b387bc92dcc..3148bd30c47 100644 --- a/service_container/optional_dependencies.rst +++ b/service_container/optional_dependencies.rst @@ -2,10 +2,8 @@ How to Make Service Arguments/References Optional ================================================= Sometimes, one of your services may have an optional dependency, meaning -that the dependency is not required for your service to work properly. In -the example above, the ``app.mailer`` service *must* exist, otherwise an exception -will be thrown. By modifying the ``app.newsletter_manager`` service definition, -you can make this reference optional, there are two strategies for doing this. +that the dependency is not required for your service to work properly. You can +configure the container to not throw an error in this case. Setting Missing Dependencies to null ------------------------------------ @@ -25,12 +23,10 @@ if the service does not exist: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - + + @@ -42,11 +38,11 @@ if the service does not exist: use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; - $container->register('app.mailer', ...); + // ... - $container->register('app.newsletter_manager', NewsletterManager::class) + $container->register(NewsletterManager::class) ->addArgument(new Reference( - 'app.mailer', + 'logger', ContainerInterface::NULL_ON_INVALID_REFERENCE )); @@ -73,7 +69,7 @@ call if the service exists and remove the method call if it does not: app.newsletter_manager: class: AppBundle\Newsletter\NewsletterManager calls: - - [setMailer, ['@?app.mailer']] + - [setLogger, ['@?logger']] .. code-block:: xml @@ -89,9 +85,9 @@ call if the service exists and remove the method call if it does not: - - - + + + @@ -104,13 +100,11 @@ call if the service exists and remove the method call if it does not: use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; - $container->register('app.mailer', ...); - $container - ->register('app.newsletter_manager', NewsletterManager::class) - ->addMethodCall('setMailer', array( + ->register(NewsletterManager::class) + ->addMethodCall('setLogger', array( new Reference( - 'app.mailer', + 'logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE ), )) @@ -124,9 +118,9 @@ call if the service exists and remove the method call if it does not: In YAML, the special ``@?`` syntax tells the service container that the dependency is optional. Of course, the ``NewsletterManager`` must also be rewritten by -adding a ``setMailer()`` method:: +adding a ``setLogger()`` method:: - public function setMailer(Mailer $mailer) + public function setLogger(LoggerInterface $logger) { // ... } diff --git a/service_container/parameters.rst b/service_container/parameters.rst index 4c9e2d649ba..0884e34712d 100644 --- a/service_container/parameters.rst +++ b/service_container/parameters.rst @@ -54,8 +54,7 @@ and hidden with the service definition: mailer.transport: sendmail services: - app.mailer: - class: AppBundle\Mailer + AppBundle\Service\Mailer: arguments: ['%mailer.transport%'] .. code-block:: xml @@ -71,7 +70,7 @@ and hidden with the service definition: - + %mailer.transport% @@ -79,12 +78,12 @@ and hidden with the service definition: .. code-block:: php - use AppBundle\Mailer; + use AppBundle\Service\Mailer; use Symfony\Component\DependencyInjection\Reference; $container->setParameter('mailer.transport', 'sendmail'); - $container->register('app.mailer', Mailer::class) + $container->register(Mailer::class) ->addArgument('%mailer.transport%'); .. caution:: @@ -168,8 +167,8 @@ accessor methods for parameters:: .. note:: - You can only set a parameter before the container is compiled. To learn - more about compiling the container see + You can only set a parameter before the container is compiled: not at run-time. + To learn more about compiling the container see :doc:`/components/dependency_injection/compilation`. .. _component-di-parameters-array: @@ -233,6 +232,11 @@ for all parameters that are arrays. 'fr' => array('fr', 'en'), )); +Environment Variables and Dynamic Values +---------------------------------------- + +See :doc:`/configuration/external_parameters`. + .. _component-di-parameters-constants: Constants as Parameters diff --git a/service_container/parent_services.rst b/service_container/parent_services.rst index 6390d52661d..381ca6a6233 100644 --- a/service_container/parent_services.rst +++ b/service_container/parent_services.rst @@ -18,7 +18,7 @@ you may have multiple repository classes which need the protected $entityManager; protected $logger; - public function __construct(ObjectManager $manager) + public function __construct(EntityManagerInterface $manager) { $this->entityManager = $manager; } @@ -40,21 +40,18 @@ duplicated service definitions: .. code-block:: yaml services: - app.base_doctrine_repository: - # as no class is configured, the parent service MUST be abstract + AppBundle\Repository\BaseDoctrineRepository: abstract: true arguments: ['@doctrine.entity_manager'] calls: - [setLogger, ['@logger']] - app.user_repository: - class: AppBundle\Repository\DoctrineUserRepository - # extend the app.base_doctrine_repository service - parent: app.base_doctrine_repository + AppBundle\Repository\DoctrineUserRepository: + # extend the AppBundle\Repository\BaseDoctrineRepository service + parent: AppBundle\Repository\BaseDoctrineRepository - app.post_repository: - class: AppBundle\Repository\DoctrinePostRepository - parent: app.base_doctrine_repository + AppBundle\Repository\DoctrinePostRepository: + parent: AppBundle\Repository\BaseDoctrineRepository # ... @@ -67,8 +64,7 @@ duplicated service definitions: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - + @@ -76,15 +72,13 @@ duplicated service definitions: - - + - @@ -95,36 +89,40 @@ duplicated service definitions: use AppBundle\Repository\DoctrineUserRepository; use AppBundle\Repository\DoctrinePostRepository; + use AppBundle\Repository\BaseDoctrineRepository; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Reference; - // as no class is configured, the parent service MUST be abstract - $container->register('app.base_doctrine_repository') + $container->register(BaseDoctrineRepository::class) + ->setAbstract(true) ->addArgument(new Reference('doctrine.entity_manager')) ->addMethodCall('setLogger', array(new Reference('logger'))) ; - // extend the app.base_doctrine_repository service - $definition = new ChildDefinition('app.base_doctrine_repository'); + // extend the AppBundle\Repository\BaseDoctrineRepository service + $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrineUserRepository::class); - $container->setDefinition('app.user_repository', $definition); + $container->setDefinition(DoctrineUserRepository::class, $definition); - $definition = new ChildDefinition('app.base_doctrine_repository'); + $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrinePostRepository::class); - - $container->setDefinition('app.post_repository', $definition); + $container->setDefinition(DoctrinePostRepository::class, $definition); // ... In this context, having a ``parent`` service implies that the arguments and method calls of the parent service should be used for the child services. Specifically, the ``EntityManager`` will be injected and ``setLogger()`` will -be called when ``app.user_repository`` is instantiated. +be called when ``AppBundle\Repository\DoctrineUserRepository`` is instantiated. + +All attributes on the parent service are shared with the child **except** for +``shared``, ``abstract`` and ``tags``. These are *not* inherited from the parent. -.. caution:: +.. note:: - The ``shared``, ``abstract`` and ``tags`` attributes are *not* inherited from - parent services. + If you have a ``_defaults`` section in your file, all child services are required + to explicitly override those values to avoid ambiguity. You will see a clear + error message about this. .. tip:: @@ -147,9 +145,8 @@ in the child class: services: # ... - app.user_repository: - class: AppBundle\Repository\DoctrineUserRepository - parent: app.base_doctrine_repository + AppBundle\Repository\DoctrineUserRepository: + parent: AppBundle\Repository\BaseDoctrineRepository # overrides the public setting of the parent service public: false @@ -158,9 +155,8 @@ in the child class: # argument list arguments: ['@app.username_checker'] - app.post_repository: - class: AppBundle\Repository\DoctrinePostRepository - parent: app.base_doctrine_repository + AppBundle\Repository\DoctrinePostRepository: + parent: AppBundle\Repository\BaseDoctrineRepository # overrides the first argument (using the special index_N key) arguments: @@ -178,9 +174,8 @@ in the child class: - @@ -204,20 +198,21 @@ in the child class: use AppBundle\Repository\DoctrineUserRepository; use AppBundle\Repository\DoctrinePostRepository; + use AppBundle\Repository\BaseDoctrineRepository; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Reference; // ... - $definition = new ChildDefinition('app.base_doctrine_repository'); + $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrineUserRepository::class); // overrides the public setting of the parent service $definition->setPublic(false); // appends the '@app.username_checker' argument to the parent argument list $definition->addArgument(new Reference('app.username_checker')); - $container->setDefinition('app.user_repository', $definition); + $container->setDefinition(DoctrineUserRepository::class, $definition); - $definition = new ChildDefinition('app.base_doctrine_repository'); + $definition = new ChildDefinition(BaseDoctrineRepository::class); $definition->setClass(DoctrinePostRepository::class); // overrides the first argument $definition->replaceArgument(0, new Reference('doctrine.custom_entity_manager')); - $container->setDefinition('app.post_repository', $definition); + $container->setDefinition(DoctrinePostRepository::class, $definition); diff --git a/service_container/request.rst b/service_container/request.rst index c02c7c76b48..5b4f4af738c 100644 --- a/service_container/request.rst +++ b/service_container/request.rst @@ -33,50 +33,12 @@ method:: // ... } -Now, just inject the ``request_stack``, which behaves like any normal service: - -.. configuration-block:: - - .. code-block:: yaml - - # src/AppBundle/Resources/config/services.yml - services: - newsletter_manager: - class: AppBundle\Newsletter\NewsletterManager - arguments: ["@request_stack"] - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // src/AppBundle/Resources/config/services.php - use AppBundle\Newsletter\NewsletterManager; - use Symfony\Component\DependencyInjection\Reference; - - // ... - $container->register('newsletter_manager', NewsletterManager::class) - ->addArgument(new Reference('request_stack')); +Now, just inject the ``request_stack``, which behaves like any normal service. +If you're using the :ref:`default services.yml configuration `, +this will happen automatically via autowiring. .. tip:: - If you define a controller as a service then you can get the ``Request`` - object without injecting the container by having it passed in as an - argument of your action method. See :ref:`controller-request-argument` for + In a controller you can get the ``Request`` object by having it passed in as an + argument to your action method. See :ref:`controller-request-argument` for details. diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst index 7c9e713276d..91eea9f7902 100644 --- a/service_container/service_decoration.rst +++ b/service_container/service_decoration.rst @@ -48,22 +48,27 @@ the original service is lost: $container->register('app.mailer', DecoratingMailer::class); Most of the time, that's exactly what you want to do. But sometimes, -you might want to decorate the old one instead. In this case, the -old service should be kept around to be able to reference it in the -new one. This configuration replaces ``app.mailer`` with a new one, but keeps -a reference of the old one as ``app.decorating_mailer.inner``: +you might want to decorate the old service instead: keeping the old service so +that you can reference it: .. configuration-block:: .. code-block:: yaml services: - # ... + app.mailer: + class: AppBundle\Mailer app.decorating_mailer: class: AppBundle\DecoratingMailer + # overrides the app.mailer service + # but that service is still available as app.decorating_mailer.inner decorates: app.mailer + + # pass the old service as an argument arguments: ['@app.decorating_mailer.inner'] + + # private, because usually you do not need to fetch app.decorating_mailer directly public: false .. code-block:: xml @@ -74,7 +79,7 @@ a reference of the old one as ``app.decorating_mailer.inner``: xsd:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + register('app.mailer', Mailer::class); + $container->register('app.decorating_mailer', DecoratingMailer::class) ->setDecoratedService('app.mailer') ->addArgument(new Reference('app.decorating_mailer.inner')) ->setPublic(false) ; -Here is what's going on here: the ``decorates`` option tells the container that -the ``app.decorating_mailer`` service replaces the ``app.mailer`` service. By -convention, the old ``app.mailer`` service is renamed to -``app.decorating_mailer.inner``, so you can inject it into your new service. +The ``decorates`` option tells the container that the ``app.decorating_mailer`` service +replaces the ``app.mailer`` service. The old ``app.mailer`` service is renamed to +``app.decorating_mailer.inner`` so you can inject it into your new service. .. tip:: - Most of the time, the decorator should be declared private, as you will not - need to retrieve it as ``app.decorating_mailer`` from the container. - - The visibility of the decorated ``app.mailer`` service (which is an alias + The visibility (public) of the decorated ``app.mailer`` service (which is an alias for the new service) will still be the same as the original ``app.mailer`` visibility. @@ -117,11 +120,8 @@ convention, the old ``app.mailer`` service is renamed to The generated inner id is based on the id of the decorator service (``app.decorating_mailer`` here), not of the decorated service (``app.mailer`` - here). This is mandatory to allow several decorators on the same service - (they need to have different generated inner ids). - - You can change the inner service name if you want to using the - ``decoration_inner_name`` option: + here). You can control the inner service name via the ``decoration_inner_name`` + option: .. configuration-block:: diff --git a/service_container/shared.rst b/service_container/shared.rst index aec4943f188..5256b6ba00f 100644 --- a/service_container/shared.rst +++ b/service_container/shared.rst @@ -6,7 +6,7 @@ How to Define Non Shared Services In the service container, all services are shared by default. This means that each time you retrieve the service, you'll get the *same* instance. This is -often the behavior you want, but in some cases, you might want to always get a +usually the behavior you want, but in some cases, you might want to always get a *new* instance. In order to always get a new instance, set the ``shared`` setting to ``false`` @@ -18,8 +18,7 @@ in your service definition: # app/config/services.yml services: - app.some_not_shared_service: - class: ... + AppBundle\SomeNonSharedService: shared: false # ... @@ -27,18 +26,17 @@ in your service definition: - + .. code-block:: php // app/config/services.php use Symfony\Component\DependencyInjection\Definition; + use AppBundle\SomeNonSharedService; - $definition = new Definition('...'); - $definition->setShared(false); + $container->register(SomeNonSharedService::class) + ->setShared(false); - $container->setDefinition('app.some_not_shared_service', $definition); - -Now, whenever you call ``$container->get('app.some_not_shared_service')`` or -inject this service, you'll receive a new instance. +Now, whenever you request an the ``AppBundle\SomeNonSharedService`` from the container, +you will be passed a new instance. diff --git a/service_container/synthetic_services.rst b/service_container/synthetic_services.rst index 9788cb59bea..ee550947af1 100644 --- a/service_container/synthetic_services.rst +++ b/service_container/synthetic_services.rst @@ -25,9 +25,8 @@ from within the ``Kernel`` class:: } Services that are set at runtime are called *synthetic services*. This service -has to be configured in the container, so the container knows the service does -exist during compilation (otherwise, services depending on this ``kernel`` -service will get a "service does not exist" error). +has to be configured so the container knows the service exists during compilation +(otherwise, services depending on ``kernel`` will get a "service does not exist" error). In order to do so, mark the service as synthetic in your service definition configuration: diff --git a/service_container/tags.rst b/service_container/tags.rst index 5255e1f57c5..37368f23137 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -59,6 +59,17 @@ out :doc:`/reference/dic_tags`. Each of these has a different effect on your service and many tags require additional arguments (beyond just the ``name`` parameter). +**For most users, this is all you need to know**. If you want to go further and +learn how to create your own custom tags, keep reading. + +Autoconfiguring Tags +-------------------- + +Starting in Symfony 3.3, if you enable :ref:`autoconfigure `, +then some tags are automatically applied for you. That's true for the ``twig.extension`` +tag: the container sees that your class extends ``Twig_Extension`` (or more accurately, +that it implements ``Twig_ExtensionInterface``) and adds the tag for you. + Creating custom Tags -------------------- @@ -100,8 +111,7 @@ Then, define the chain as a service: .. code-block:: yaml services: - app.mailer_transport_chain: - class: AppBundle\Mail\TransportChain + AppBundle\Mail\TransportChain: ~ .. code-block:: xml @@ -112,9 +122,7 @@ Then, define the chain as a service: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -122,7 +130,7 @@ Then, define the chain as a service: use AppBundle\Mail\TransportChain; - $container->register('app.mailer_transport_chain', TransportChain::class); + $container->autowire(TransportChain::class); Define Services with a Custom Tag ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -136,13 +144,11 @@ For example, you may add the following transports as services: .. code-block:: yaml services: - app.smtp_transport: - class: \Swift_SmtpTransport + Swift_SmtpTransport: arguments: ['%mailer_host%'] tags: [app.mail_transport] - app.sendmail_transport: - class: \Swift_SendmailTransport + Swift_SendmailTransport: tags: [app.mail_transport] .. code-block:: xml @@ -154,13 +160,13 @@ For example, you may add the following transports as services: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + %mailer_host% - + @@ -168,11 +174,11 @@ For example, you may add the following transports as services: .. code-block:: php - $container->register('app.smtp_transport', '\Swift_SmtpTransport') + $container->register(\Swift_SmtpTransport::class) ->addArgument('%mailer_host%') ->addTag('app.mail_transport'); - $container->register('app.sendmail_transport', '\Swift_SendmailTransport') + $container->register(\Swift_SendmailTransport::class) ->addTag('app.mail_transport'); Notice that each service was given a tag named ``app.mail_transport``. This is @@ -193,17 +199,18 @@ container for any services with the ``app.mail_transport`` tag:: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; + use AppBundle\Mail\TransportChain; class MailTransportPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { // always first check if the primary service is defined - if (!$container->has('app.mailer_transport_chain')) { + if (!$container->has(TransportChain::class)) { return; } - $definition = $container->findDefinition('app.mailer_transport_chain'); + $definition = $container->findDefinition(TransportChain::class); // find all service IDs with the app.mail_transport tag $taggedServices = $container->findTaggedServiceIds('app.mail_transport'); @@ -285,16 +292,14 @@ To answer this, change the service declaration: .. code-block:: yaml services: - app.smtp_transport: - class: \Swift_SmtpTransport + Swift_SmtpTransport: arguments: ['%mailer_host%'] tags: - - { name: app.mail_transport, alias: foo } + - { name: app.mail_transport, alias: smtp } - app.sendmail_transport: - class: \Swift_SendmailTransport + Swift_SendmailTransport: tags: - - { name: app.mail_transport, alias: bar } + - { name: app.mail_transport, alias: sendmail } .. code-block:: xml @@ -305,25 +310,25 @@ To answer this, change the service declaration: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + %mailer_host% - + - - + + .. code-block:: php - $container->register('app.smtp_transport', '\Swift_SmtpTransport') + $container->register(\Swift_SmtpTransport::class) ->addArgument('%mailer_host%') ->addTag('app.mail_transport', array('alias' => 'foo')); - $container->register('app.sendmail_transport', '\Swift_SendmailTransport') + $container->register(\Swift_SendmailTransport::class) ->addTag('app.mail_transport', array('alias' => 'bar')); .. tip:: @@ -337,12 +342,12 @@ To answer this, change the service declaration: services: # Compact syntax - app.sendmail_transport: + Swift_SendmailTransport: class: \Swift_SendmailTransport tags: [app.mail_transport] # Verbose syntax - app.sendmail_transport: + Swift_SendmailTransport: class: \Swift_SendmailTransport tags: - { name: app.mail_transport } @@ -362,14 +367,11 @@ use this, update the compiler:: { public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('app.mailer_transport_chain')) { - return; - } - - $definition = $container->getDefinition('app.mailer_transport_chain'); - $taggedServices = $container->findTaggedServiceIds('app.mail_transport'); + // ... foreach ($taggedServices as $id => $tags) { + + // a service could have the same tag twice foreach ($tags as $attributes) { $definition->addMethodCall('addTransport', array( new Reference($id), diff --git a/service_container/third_party.rst b/service_container/third_party.rst deleted file mode 100644 index 636c16f1861..00000000000 --- a/service_container/third_party.rst +++ /dev/null @@ -1,104 +0,0 @@ -.. index:: - single: DependencyInjection; Third-Party Bundles - single: Service Container; Third-Party Bundles - -How to Work with Services Provided by Third-Party Bundles -========================================================= - -Since Symfony and all third-party bundles configure and retrieve their services -via the container, you can easily access them or even use them in your own -services. To keep things simple, Symfony by default does not require that -controllers must be defined as services. Furthermore, Symfony injects the entire -service container into your controller. For example, to handle the storage of -information on a user's session, Symfony provides a ``session`` service, -which you can access inside a standard controller as follows:: - - public function indexAction($bar) - { - $session = $this->get('session'); - $session->set('foo', $bar); - - // ... - } - -In Symfony, you'll constantly use services provided by the Symfony core or -other third-party bundles to perform tasks such as rendering templates (``templating``), -sending emails (``mailer``), or accessing information on the request through the request stack (``request_stack``). - -You can take this a step further by using these services inside services that -you've created for your application. Beginning by modifying the ``NewsletterManager`` -to use the real Symfony ``mailer`` service (instead of the pretend ``app.mailer``). -Also pass the templating engine service to the ``NewsletterManager`` -so that it can generate the email content via a template:: - - // src/AppBundle/Newsletter/NewsletterManager.php - namespace AppBundle\Newsletter; - - use Symfony\Component\Templating\EngineInterface; - - class NewsletterManager - { - protected $mailer; - - protected $templating; - - public function __construct( - \Swift_Mailer $mailer, - EngineInterface $templating - ) { - $this->mailer = $mailer; - $this->templating = $templating; - } - - // ... - } - -Configuring the service container is easy: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - app.newsletter_manager: - class: AppBundle\Newsletter\NewsletterManager - arguments: ['@mailer', '@templating'] - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\Newsletter\NewsletterManager; - - $container->register('app.newsletter_manager', NewsletterManager::class) - ->setArguments(array( - new Reference('mailer'), - new Reference('templating'), - )); - -The ``app.newsletter_manager`` service now has access to the core ``mailer`` -and ``templating`` services. This is a common way to create services specific -to your application that leverage the power of different services within -the framework. - -.. tip:: - - Be sure that the ``swiftmailer`` entry appears in your application - configuration. As was mentioned in :ref:`service-container-extension-configuration`, - the ``swiftmailer`` key invokes the service extension from the - SwiftmailerBundle, which registers the ``mailer`` service. diff --git a/session/locale_sticky_session.rst b/session/locale_sticky_session.rst index b7e94cc5255..4e89eb2ffc2 100644 --- a/session/locale_sticky_session.rst +++ b/session/locale_sticky_session.rst @@ -5,27 +5,26 @@ Making the Locale "Sticky" during a User's Session ================================================== Symfony stores the locale setting in the Request, which means that this setting -is not available in subsequent requests. In this article, you'll learn how to -store the locale in the session, so that it'll be the same for every subsequent -request. +is not automtically saved ("sticky") across requests. But, you *can* store the locale +in the session, so that it's used on subsequent requests. -Creating a LocaleListener -------------------------- +.. _creating-a-LocaleSubscriber: -To simulate that the locale is stored in a session, you need to create and -register a :doc:`new event listener `. -The listener will look something like this. Typically, ``_locale`` is used -as a routing parameter to signify the locale, though it doesn't really matter -how you determine the desired locale from the request:: +Creating a LocaleSubscriber +--------------------------- - // src/AppBundle/EventListener/LocaleListener.php - namespace AppBundle\EventListener; +Create and a :ref:`new event subscriber `. Typically, ``_locale`` +is used as a routing parameter to signify the locale, though you can determine the +correct locale however you want:: + + // src/AppBundle/EventSubscriber/LocaleSubscriber.php + namespace AppBundle\EventSubscriber; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; - class LocaleListener implements EventSubscriberInterface + class LocaleSubscriber implements EventSubscriberInterface { private $defaultLocale; @@ -59,47 +58,58 @@ how you determine the desired locale from the request:: } } -Then register the listener: +If you're using the :ref:`default services.yml configuration `, +you're done! Symfony will automatically know about the event subscriber and call +the ``onKernelRequest`` method on each request. + +To see it working, either set the ``_locale`` key on the session manually (e.g. +via some "Change Locale" route & controller), or create a route with a the :ref:`_locale default `. + +.. sidebar:: Explicitly Configure the Subscriber -.. configuration-block:: + You can also explicitly configure it, in order to pass in the :ref:`default_locale `: - .. code-block:: yaml + .. configuration-block:: - services: - app.locale_listener: - class: AppBundle\EventListener\LocaleListener - arguments: ['%kernel.default_locale%'] - tags: [kernel.event_subscriber] + .. code-block:: yaml - .. code-block:: xml + services: + # ... - - + AppBundle\EventSubscriber\LocaleSubscriber: + arguments: ['%kernel.default_locale%'] + # redundant if you're using autoconfigure + tags: [kernel.event_subscriber] - - - %kernel.default_locale% + .. code-block:: xml - - - - + + - .. code-block:: php + + + %kernel.default_locale% - use AppBundle\EventListener\LocaleListener; + + + + - $container->register('app.locale_listener', LocaleListener::class) - ->addArgument('%kernel.default_locale%') - ->addTag('kernel.event_subscriber'); + .. code-block:: php + + use AppBundle\EventSubscriber\LocaleSubscriber; + + $container->register(LocaleSubscriber::class) + ->addArgument('%kernel.default_locale%') + ->addTag('kernel.event_subscriber'); That's it! Now celebrate by changing the user's locale and seeing that it's -sticky throughout the request. Remember, to get the user's locale, always -use the :method:`Request::getLocale ` +sticky throughout the request. + +Remember, to get the user's locale, always use the :method:`Request::getLocale ` method:: // from a controller... @@ -114,39 +124,37 @@ Setting the Locale Based on the User's Preferences -------------------------------------------------- You might want to improve this technique even further and define the locale based on -the user entity of the logged in user. However, since the ``LocaleListener`` is called +the user entity of the logged in user. However, since the ``LocaleSubscriber`` is called before the ``FirewallListener``, which is responsible for handling authentication and setting the user token on the ``TokenStorage``, you have no access to the user which is logged in. -Suppose you have defined a ``locale`` property on your ``User`` entity and -you want to use this as the locale for the given user. To accomplish this, +Suppose you have a ``locale`` property on your ``User`` entity and +want to use this as the locale for the given user. To accomplish this, you can hook into the login process and update the user's session with this locale value before they are redirected to their first page. -To do this, you need an event listener for the ``security.interactive_login`` +To do this, you need an event subscriber on the ``security.interactive_login`` event: .. code-block:: php - // src/AppBundle/EventListener/UserLocaleListener.php - namespace AppBundle\EventListener; + // src/AppBundle/EventSubscriber/UserLocaleSubscriber.php + namespace AppBundle\EventSubscriber; - use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Stores the locale of the user in the session after the - * login. This can be used by the LocaleListener afterwards. + * login. This can be used by the LocaleSubscriber afterwards. */ - class UserLocaleListener + class UserLocaleSubscriber implements EventSubscriberInterface { - /** - * @var Session - */ private $session; - public function __construct(Session $session) + public function __construct(SessionInterface $session) { $this->session = $session; } @@ -164,58 +172,13 @@ event: } } -Then register the listener: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - app.user_locale_listener: - class: AppBundle\EventListener\UserLocaleListener - arguments: ['@session'] - tags: - - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin } - - .. code-block:: xml - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\EventListener\UserLocaleListener; - use Symfony\Component\DependencyInjection\Reference; - - $container - ->register('app.user_locale_listener', UserLocaleListener::class) - ->addArgument(new Reference('session')) - ->addTag( - 'kernel.event_listener', - array('event' => 'security.interactive_login', 'method' => 'onInteractiveLogin') - ); +If you're using the :ref:`default services.yml configuration `, +you're done! Symfony will automatically know about the event subscriber will pass +your the ``session`` service. Now, when you login, the user's locale will be set +into the session. .. caution:: In order to update the language immediately after a user has changed - their language preferences, you need to update the session after an update - to the ``User`` entity. + their language preferences, you also need to update the session when you change + the ``User`` entity. diff --git a/session/proxy_examples.rst b/session/proxy_examples.rst index d38cfd741ba..9c8f3b219a0 100644 --- a/session/proxy_examples.rst +++ b/session/proxy_examples.rst @@ -10,40 +10,12 @@ a custom save handler just by defining a class that extends the :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy` class. -Then, define a new service related to the custom session handler: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - app.session_handler: - class: AppBundle\Session\CustomSessionHandler - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - use AppBundle\Session\CustomSessionHandler; - - $container->register('app.session_handler', CustomSessionHandler::class); +Then, define the class as a :ref:`service `. +If you're using the :ref:`default services.yml configuration `, +that happens automatically. Finally, use the ``framework.session.handler_id`` configuration option to tell -Symfony to use your own session handler instead of the default one: +Symfony to use your session handler instead of the default one: .. configuration-block:: @@ -53,7 +25,7 @@ Symfony to use your own session handler instead of the default one: framework: session: # ... - handler_id: app.session_handler + handler_id: AppBundle\Session\CustomSessionHandler .. code-block:: xml @@ -65,18 +37,19 @@ Symfony to use your own session handler instead of the default one: http://symfony.com/schema/dic/services/services-1.0.xsd"> - + .. code-block:: php // app/config/config.php + use AppBundle\Session\CustomSessionHandler; $container->loadFromExtension('framework', array( // ... 'session' => array( // ... - 'handler_id' => 'app.session_handler', + 'handler_id' => CustomSessionHandler::class, ), )); @@ -133,24 +106,37 @@ can intercept the session before it is written:: use AppBundle\Entity\User; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; class ReadOnlySessionProxy extends SessionHandlerProxy { - private $user; + private $tokenStorage; - public function __construct(\SessionHandlerInterface $handler, User $user) + public function __construct(\SessionHandlerInterface $handler, TokenStorageInterface $tokenStorage) { - $this->user = $user; + $this->tokenStorage = $tokenStorage; parent::__construct($handler); } public function write($id, $data) { - if ($this->user->isGuest()) { + if ($this->getUser() && $this->getUser()->isGuest()) { return; } return parent::write($id, $data); } + + private function getUser() + { + if (!$token = $tokenStorage->getToken()) { + return; + } + + $user = $token->getUser(); + if (is_object($user)) { + return $user; + } + } } diff --git a/session/sessions_directory.rst b/session/sessions_directory.rst index ed41dafc046..c1b91f5472f 100644 --- a/session/sessions_directory.rst +++ b/session/sessions_directory.rst @@ -4,102 +4,8 @@ Configuring the Directory where Session Files are Saved ======================================================= -By default, the Symfony Standard Edition uses the global ``php.ini`` values -for ``session.save_handler`` and ``session.save_path`` to determine where -to store session data. This is because of the following configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - session: - # handler_id set to null will use default session handler from php.ini - handler_id: ~ - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - 'session' => array( - // handler_id set to null will use default session handler from php.ini - 'handler_id' => null, - ), - )); - -With this configuration, changing *where* your session metadata is stored -is entirely up to your ``php.ini`` configuration. - -However, if you have the following configuration, Symfony will store the session -data in files in the cache directory ``%kernel.cache_dir%/sessions``. This -means that when you clear the cache, any current sessions will also be deleted: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - session: ~ - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - 'session' => array(), - )); - -Using a different directory to save session data is one method to ensure -that your current sessions aren't lost when you clear Symfony's cache. - -.. tip:: - - Using a different session save handler is an excellent (yet more complex) - method of session management available within Symfony. See - :doc:`/components/http_foundation/session_configuration` for a - discussion of session save handlers. There are also articles - about storing sessions in a :doc:`relational database ` - or a :doc:`NoSQL database `. - -To change the directory in which Symfony saves session data, you only need -change the framework configuration. In this example, you will change the -session directory to ``app/sessions``: +By default, Symfony stores session metadata on the filesystem. If you want to control +this path, update the ``framework.session.save_path`` configuration key: .. configuration-block:: @@ -109,7 +15,7 @@ session directory to ``app/sessions``: framework: session: handler_id: session.handler.native_file - save_path: '%kernel.project_dir%/app/sessions' + save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%' .. code-block:: xml @@ -124,9 +30,7 @@ session directory to ``app/sessions``: http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + @@ -136,6 +40,15 @@ session directory to ``app/sessions``: $container->loadFromExtension('framework', array( 'session' => array( 'handler_id' => 'session.handler.native_file', - 'save_path' => '%kernel.project_dir%/app/sessions', + 'save_path' => '%kernel.project_dir%/var/sessions/%kernel.environment%' ), )); + +Storing Sessions Elsewhere (e.g. database) +------------------------------------------ + +Of course, you can store your session data anywhere by using the ``handler_id`` option. +See :doc:`/components/http_foundation/session_configuration` for a discussion of +session save handlers. There are also articles about storing sessions in a +:doc:`relational database ` +or a :doc:`NoSQL database `. diff --git a/templating.rst b/templating.rst index 495a06e17dc..1a24325a043 100644 --- a/templating.rst +++ b/templating.rst @@ -445,11 +445,6 @@ to be rendered as HTML (``index.html.twig``), XML (``index.xml.twig``), or any other format. For more information, read the :doc:`/templating/formats` section. -.. note:: - - The available "engines" can be configured and even new engines added. - See :ref:`Templating Configuration ` for more details. - .. index:: single: Templating; Tags and helpers single: Templating; Helpers diff --git a/templating/embedding_controllers.rst b/templating/embedding_controllers.rst index 45f223180b3..228ae4053ee 100644 --- a/templating/embedding_controllers.rst +++ b/templating/embedding_controllers.rst @@ -9,6 +9,12 @@ you have a sidebar in your layout that contains the three most recent articles. Retrieving the three articles may include querying the database or performing other heavy logic that can't be done from within a template. +.. note:: + + Rendering embedded controllers is "heavier" than including a template or calling + a custom Twig function. Unless you're planning on :doc:`caching the fragment `, + avoid embedding many controllers. + The solution is to simply embed the result of an entire controller from your template. First, create a controller that renders a certain number of recent articles:: @@ -92,8 +98,4 @@ string syntax for controllers (i.e. **bundle**:**controller**:**action**): ) ?> -Whenever you find that you need a variable or a piece of information that -you don't have access to in a template, consider rendering a controller. -Controllers are fast to execute and promote good code organization and reuse. -Of course, like all controllers, they should ideally be "skinny", meaning -that as much code as possible lives in reusable :doc:`services `. +The result of an embedded controler can also be :doc:`cached ` diff --git a/templating/overriding.rst b/templating/overriding.rst index 925e225b8b3..a018b630020 100644 --- a/templating/overriding.rst +++ b/templating/overriding.rst @@ -9,30 +9,12 @@ bundles (see `KnpBundles.com`_) for a large number of different features. Once you use a third-party bundle, you'll likely need to override and customize one or more of its templates. -Suppose you've installed the imaginary open-source AcmeBlogBundle in your +Suppose you've installed an imaginary open-source AcmeBlogBundle in your project. And while you're really happy with everything, you want to override -the blog "list" page to customize the markup specifically for your application. -By digging into the ``Blog`` controller of the AcmeBlogBundle, you find the -following:: +the template for a blog list page. Inside the bundle, the template you want to +override lives at ``Resources/views/Blog/index.html.twig``. - public function indexAction() - { - // some logic to retrieve the blogs - $blogs = ...; - - $this->render( - 'AcmeBlogBundle:Blog:index.html.twig', - array('blogs' => $blogs) - ); - } - -When the ``AcmeBlogBundle:Blog:index.html.twig`` is rendered, Symfony actually -looks in two different locations for the template: - -#. ``app/Resources/AcmeBlogBundle/views/Blog/index.html.twig`` -#. ``src/Acme/BlogBundle/Resources/views/Blog/index.html.twig`` - -To override the bundle template, just copy the ``index.html.twig`` template +To override the bundle template, just copy ``index.html.twig`` template from the bundle to ``app/Resources/AcmeBlogBundle/views/Blog/index.html.twig`` (the ``app/Resources/AcmeBlogBundle`` directory won't exist, so you'll need to create it). You're now free to customize the template. @@ -42,24 +24,8 @@ to create it). You're now free to customize the template. If you add a template in a new location, you *may* need to clear your cache (``php bin/console cache:clear``), even if you are in debug mode. -This logic also applies to base bundle templates. Suppose also that each -template in AcmeBlogBundle inherits from a base template called -``AcmeBlogBundle::layout.html.twig``. Just as before, Symfony will look in -the following two places for the template: - -#. ``app/Resources/AcmeBlogBundle/views/layout.html.twig`` -#. ``src/Acme/BlogBundle/Resources/views/layout.html.twig`` - -Once again, to override the template, just copy it from the bundle to -``app/Resources/AcmeBlogBundle/views/layout.html.twig``. You're now free to -customize this copy as you see fit. - -If you take a step back, you'll see that Symfony always starts by looking in -the ``app/Resources/{BUNDLE_NAME}/views/`` directory for a template. If the -template doesn't exist there, it continues by checking inside the -``Resources/views`` directory of the bundle itself. This means that all bundle -templates can be overridden by placing them in the correct ``app/Resources`` -subdirectory. +This logic also applies to *any* template that lives in a bundle: just follow the +convention: ``app/Resources/{BUNDLE_NAME}/views/{PATH/TO/TEMPLATE.html.twig}``. .. note:: diff --git a/templating/templating_service.rst b/templating/templating_service.rst deleted file mode 100644 index 48912791c31..00000000000 --- a/templating/templating_service.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. index:: - single: Templating; The templating service - -How to Configure and Use the ``templating`` Service -=================================================== - -The heart of the template system in Symfony is the templating ``Engine``. -This special object is responsible for rendering templates and returning -their content. When you render a template in a controller, for example, -you're actually using the templating engine service. For example:: - - return $this->render('article/index.html.twig'); - -is equivalent to:: - - use Symfony\Component\HttpFoundation\Response; - - $engine = $this->container->get('templating'); - $content = $engine->render('article/index.html.twig'); - - return $response = new Response($content); - -.. _template-configuration: - -The templating engine (or "service") is preconfigured to work automatically -inside Symfony. It can, of course, be configured further in the application -configuration file: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - # ... - templating: { engines: ['twig'] } - - .. code-block:: xml - - - - - - - - - twig - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - // ... - - 'templating' => array( - 'engines' => array('twig'), - ), - )); - -Several configuration options are available and are covered in the -:doc:`Configuration Appendix `. - -.. note:: - - The ``twig`` engine is mandatory to use the webprofiler (as well as many - third-party bundles). diff --git a/templating/twig_extension.rst b/templating/twig_extension.rst index bc46288a2ce..4a684718d51 100644 --- a/templating/twig_extension.rst +++ b/templating/twig_extension.rst @@ -4,31 +4,24 @@ How to Write a custom Twig Extension ==================================== -The main motivation for writing an extension is to move often used code -into a reusable class like adding support for internationalization. -An extension can define tags, filters, tests, operators, global variables, -functions, and node visitors. - -Creating an extension also makes for a better separation of code that is -executed at compilation time and code needed at runtime. As such, it makes -your code faster. - -.. tip:: - - Before writing your own extensions, have a look at the - `Twig official extension repository`_. +If you need to create custom Twig functions, filters, tests or more, you'll need +to create a Twig extension. You can read more about `Twig Extensions`_ in the Twig +documentation. Create the Extension Class -------------------------- -.. note:: +Suppose you want to create a new filter called ``price`` that formats a number into +money: + +.. code-block:: twig - This article describes how to write a custom Twig extension as of - Twig 1.12. If you are using an older version, please read - `Twig extensions documentation legacy`_. + {{ product.price|price }} -To get your custom functionality you must first create a Twig Extension class. -As an example you'll create a price filter to format a given number into price:: + {# pass in the 3 optional arguments #} + {{ product.price|price(2, ',', '.') }} + +Create a class that extends ``\Twig_Extension`` and fill in the logic:: // src/AppBundle/Twig/AppExtension.php namespace AppBundle\Twig; @@ -67,72 +60,13 @@ As an example you'll create a price filter to format a given number into price:: Register an Extension as a Service ---------------------------------- -Now you must let the Service Container know about your newly created Twig Extension: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - app.twig_extension: - class: AppBundle\Twig\AppExtension - public: false - tags: [twig.extension] - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\Twig\AppExtension; - - $container - ->register('app.twig_extension', AppExtension::class) - ->setPublic(false) - ->addTag('twig.extension'); - -Using the custom Extension --------------------------- - -Using your newly created Twig Extension is no different than any other: - -.. code-block:: twig - - {# outputs $5,500.00 #} - {{ '5500'|price }} - -Passing other arguments to your filter: - -.. code-block:: twig - - {# outputs $5500,2516 #} - {{ '5500.25155'|price(4, ',', '') }} - -Learning further ----------------- +Next, register your class as a service and tag it with ``twig.extension``. If you're +using the :ref:`default services.yml configuration `, +you're done! Symfony will automatically know about your new service and add the tag. -For a more in-depth look into Twig Extensions, please take a look at the -`Twig extensions documentation`_. +You can now start using your filter in any Twig template. -.. _`Twig official extension repository`: https://github.com/twigphp/Twig-extensions .. _`Twig extensions documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension .. _`global variables`: http://twig.sensiolabs.org/doc/advanced.html#id1 .. _`functions`: http://twig.sensiolabs.org/doc/advanced.html#id2 -.. _`Twig extensions documentation legacy`: http://twig.sensiolabs.org/doc/advanced_legacy.html#creating-an-extension +.. _`Twig Extensions`: https://twig.sensiolabs.org/doc/2.x/advanced.html#creating-an-extension diff --git a/testing/database.rst b/testing/database.rst index 8fae8b7fcb9..363520fd913 100644 --- a/testing/database.rst +++ b/testing/database.rst @@ -36,13 +36,13 @@ Suppose the class you want to test looks like this:: // src/AppBundle/Salary/SalaryCalculator.php namespace AppBundle\Salary; - use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\ORM\EntityManagerInterface; class SalaryCalculator { private $entityManager; - public function __construct(ObjectManager $entityManager) + public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } @@ -57,7 +57,7 @@ Suppose the class you want to test looks like this:: } } -Since the ``ObjectManager`` gets injected into the class through the constructor, +Since the ``EntityManagerInterface`` gets injected into the class through the constructor, it's easy to pass a mock object within a test:: // tests/AppBundle/Salary/SalaryCalculatorTest.php @@ -66,7 +66,7 @@ it's easy to pass a mock object within a test:: use AppBundle\Entity\Employee; use AppBundle\Salary\SalaryCalculator; use Doctrine\ORM\EntityRepository; - use Doctrine\Common\Persistence\ObjectManager; + use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\TestCase; class SalaryCalculatorTest extends TestCase @@ -86,7 +86,7 @@ it's easy to pass a mock object within a test:: // Now, mock the repository so it returns the mock of the employee $employeeRepository = $this - ->getMockBuilder(EntityRepository::class) + ->getMockBuilder(EntityManagerInterface::class) ->disableOriginalConstructor() ->getMock(); $employeeRepository->expects($this->once()) @@ -95,7 +95,7 @@ it's easy to pass a mock object within a test:: // Last, mock the EntityManager to return the mock of the repository $entityManager = $this - ->getMockBuilder(ObjectManager::class) + ->getMockBuilder(EntityManagerInterface::class) ->disableOriginalConstructor() ->getMock(); $entityManager->expects($this->once()) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index 41a64816197..1416de4c326 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -155,55 +155,10 @@ configured like options on core Symfony constraints. Constraint Validators with Dependencies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If your constraint validator has dependencies, such as a database connection, -it will need to be configured as a service in the Dependency Injection -Container. This service must include the ``validator.constraint_validator`` -tag so that the validation system knows about it: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - app.contains_alphanumeric_validator: - class: AppBundle\Validator\Constraints\ContainsAlphanumericValidator - tags: [validator.constraint_validator] - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\Validator\Constraints\ContainsAlphanumericValidator; - - $container - ->register('app.contains_alphanumeric_validator', ContainsAlphanumericValidator::class) - ->addTag('validator.constraint_validator'); - -Now, when Symfony looks for the ``ContainsAlphanumericValidator`` validator, it will -load this service from the container. - -.. note:: - - In earlier versions of Symfony, the tag required an ``alias`` key (usually - set to the class name). This ``alias`` is now optional, but if you define - it, your constraint's ``validatedBy()`` method must return the same value. +If you're using the :ref:`default services.yml configuration `, +then your validator is already registered as a service and :doc:`tagged ` +with the necessary ``validator.constraint_validator``. This means you can +:ref:`inject services or configuration ` like any other service. Class Constraint Validator ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/validation/raw_values.rst b/validation/raw_values.rst index 2d9d9f3abca..de56d6f2fe1 100644 --- a/validation/raw_values.rst +++ b/validation/raw_values.rst @@ -11,16 +11,17 @@ looks like this:: // ... use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Validator\ValidatorInterface; // ... - public function addEmailAction($email) + public function addEmailAction($email, ValidatorInterface $validator) { $emailConstraint = new Assert\Email(); // all constraint "options" can be set this way $emailConstraint->message = 'Invalid email address'; // use the validator to validate the value - $errorList = $this->get('validator')->validate( + $errorList = $validator->validate( $email, $emailConstraint ); diff --git a/validation/translations.rst b/validation/translations.rst index dc1fe3d58f8..f51dc4d1a6c 100644 --- a/validation/translations.rst +++ b/validation/translations.rst @@ -84,14 +84,13 @@ property is not empty, add the following: } } -Create a translation file under the ``validators`` catalog for the constraint -messages, typically in the ``Resources/translations/`` directory of the bundle. +Now, create a ``valdiators`` catalog file in the ``app/Resources/translations`` directory: .. configuration-block:: .. code-block:: xml - + @@ -106,12 +105,15 @@ messages, typically in the ``Resources/translations/`` directory of the bundle. .. code-block:: yaml - # validators.en.yml + # app/Resources/translations/validators.en.yml author.name.not_blank: Please enter an author name. .. code-block:: php - // validators.en.php + // app/Resources/translations/validators.en.php return array( 'author.name.not_blank' => 'Please enter an author name.', ); + +You may need to clear your cache (even in the dev environment) after creating this +file for the first time. From c8e1c96535642df0233ffd88bad2b4cc02b15466 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 27 May 2017 13:25:59 -0400 Subject: [PATCH 2/2] Tweaks thanks to Javier's reviews --- bundles/best_practices.rst | 2 +- console/commands_as_services.rst | 6 +++--- controller/service.rst | 10 +++++----- service_container/service_decoration.rst | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index b4961b09793..8cd849c5c47 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -386,7 +386,7 @@ For example, AcmeBlogBundle services must be prefixed with ``acme_blog``. In addition, services not meant to be used by the application directly, should be :ref:`defined as private `. For public services, :ref:`aliases should be created ` from the interface/class -to the service id. For example, in MonlogBundle, an alias is created from +to the service id. For example, in MonologBundle, an alias is created from ``Psr\Log\LoggerInterface`` to ``logger`` so that the ``LoggerInterface`` type-hint can be used for autowiring. diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index d781eca5ca2..4a51788de78 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -6,12 +6,12 @@ How to Define Commands as Services If you're using the :ref:`default services.yml configuration `, your command classes are already registered as services. Great! This is the recommended -setup, but it's not required. Symfony also looks in the ``Command`` directory of +setup, but it's not required. Symfony also looks in the ``Command/`` directory of each bundle and automatically registers those classes as commands. .. note:: - You can also manually register your command as a service by configure the service + You can also manually register your command as a service by configuring the service and :doc:`tagging it ` with ``console.command``. In either case, if your class extends :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`, @@ -45,7 +45,7 @@ For example, suppose you want to log something from within your command:: { $this ->setName('app:sunshine') - ->setDescription('Hello PhpStorm'); + ->setDescription('Good morning!'); } protected function execute(InputInterface $input, OutputInterface $output) diff --git a/controller/service.rst b/controller/service.rst index dd49d0cbfae..d4c7ae13c32 100644 --- a/controller/service.rst +++ b/controller/service.rst @@ -7,7 +7,7 @@ How to Define Controllers as Services In Symfony, a controller does *not* need to be registered as a service. But if you're using the :ref:`default services.yml configuration `, your controllers *are* already registered as services. This means you can use dependency -injection line any other normal service. +injection like any other normal service. Referencing your Service from Routing ------------------------------------- @@ -88,8 +88,8 @@ When using a controller defined as a service, you can still extend any of the use their shortcuts. But, you don't need to! You can choose to extend *nothing*, and use dependency injection to access difference services. -The base `Controller class source code`_ is a great way to see how to performance -simple tasks. For example, ``$this->render()`` is usually used to render a Twig +The base `Controller class source code`_ is a great way to see how to accomplish +common tasks. For example, ``$this->render()`` is usually used to render a Twig template and return a Response. But, you can also do this directly: In a controller that's defined as a service, you can instead inject the ``templating`` @@ -112,7 +112,7 @@ service and use it directly:: public function indexAction($name) { $content = $this->twig->renderResponse( - 'hellp/index.html.twig', + 'hello/index.html.twig', array('name' => $name) ); @@ -130,7 +130,7 @@ The best way to see how to replace base ``Controller`` convenience methods is to look at the `ControllerTrait`_ that holds its logic. If you want to know what type-hints to use for each service, see the -``getSubscribedEvents`` in `AbstractController`_. +``getSubscribedEvents()`` method in `AbstractController`_. .. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php .. _`base Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst index 91eea9f7902..6c1529427bc 100644 --- a/service_container/service_decoration.rst +++ b/service_container/service_decoration.rst @@ -48,7 +48,7 @@ the original service is lost: $container->register('app.mailer', DecoratingMailer::class); Most of the time, that's exactly what you want to do. But sometimes, -you might want to decorate the old service instead: keeping the old service so +you might want to decorate the old service instead and keep the old service so that you can reference it: .. configuration-block::