diff --git a/book/routing.rst b/book/routing.rst index 05a4ffbb7f3..d1dbe165558 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1582,7 +1582,7 @@ URL) rather than the ``path()`` function (which generates a relative URL): The host that's used when generating an absolute URL is automatically detected using the current ``Request`` object. When generating absolute URLs from outside the web context (for instance in a console command) this - doesn't work. See :doc:`/cookbook/console/sending_emails` to learn how to + doesn't work. See :doc:`/cookbook/console/request_context` to learn how to solve this problem. Summary diff --git a/components/dependency_injection/autowiring.rst b/components/dependency_injection/autowiring.rst new file mode 100644 index 00000000000..46196cc846d --- /dev/null +++ b/components/dependency_injection/autowiring.rst @@ -0,0 +1,405 @@ +.. index:: + single: DependencyInjection; Autowiring + +Defining Services Dependencies Automatically +============================================ + +.. versionadded:: 2.8 + Support for autowiring services was introduced in Symfony 2.8. + +Autowiring allows to register services in the container with minimal configuration. +It automatically resolves the service dependencies based on the constructor's +typehint which is useful in the field of `Rapid Application Development`_, +when designing prototypes in early stages of large projects. It makes it easy +to register a service graph and eases refactoring. + +Imagine you're building an API to publish statuses on a Twitter feed, which +has to be obfuscated with ``ROT13`` (a special case of the Caesar cipher). + +Start by creating a ROT13 transformer class:: + + // src/AppBundle/Rot13Transformer.php + namespace AppBundle; + + class Rot13Transformer + { + public function transform($value) + { + return str_rot13($value); + } + } + +And now a Twitter client using this transformer:: + + // src/AppBundle/TwitterClient.php + namespace AppBundle; + + class TwitterClient + { + private $transformer; + + public function __construct(Rot13Transformer $transformer) + { + $this->transformer = $transformer; + } + + public function tweet($user, $key, $status) + { + $transformedStatus = $this->transformer->transform($status); + + // ... connect to Twitter and send the encoded status + } + } + +The DependencyInjection component will be able to automatically register +the dependencies of this ``TwitterClient`` class when the ``twitter_client`` +service is marked as autowired: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + twitter_client: + class: 'AppBundle\TwitterClient' + autowire: true + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + // ... + $definition = new Definition('AppBundle\TwitterClient'); + $definition->setAutowired(true); + + $container->setDefinition('twitter_client', $definition); + +The autowiring subsystem will detect the dependencies of the ``TwitterClient`` +class by parsing its constructor. For instance it will find here an instance of +a ``Rot13Transformer`` as dependency. If an existing service definition (and only +one – see below) is of the required type, this service will be injected. If it's +not the case (like in this example), the subsystem is smart enough to automatically +register a private service for the ``Rot13Transformer`` class and set it as first +argument of the ``twitter_client`` service. Again, it can work only if there is one +class of the given type. If there are several classes of the same type, you must +use an explicit service definition or register a default implementation. + +As you can see, the autowiring feature drastically reduces the amount of configuration +required to define a service. No more arguments section! It also makes it easy +to change the dependencies of the ``TwitterClient`` class: just add or remove typehinted +arguments in the constructor and you are done. There is no need anymore to search +and edit related service definitions. + +Here is a typical controller using the ``twitter_client`` service:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + + class DefaultController extends Controller + { + /** + * @Route("/tweet") + * @Method("POST") + */ + public function tweetAction(Request $request) + { + $user = $request->request->get('user'); + $key = $request->request->get('key'); + $status = $request->request->get('status'); + + if (!$user || !$key || !$status) { + throw new BadRequestHttpException(); + } + + $this->get('twitter_client')->tweet($user, $key, $status); + + return new Response('OK'); + } + } + +You can give the API a try using ``curl``: + +.. code-block:: bash + + $ curl -d "user=kevin&key=ABCD&status=Hello" http://localhost:8000/tweet + +It should return ``OK``. + +Working with Interfaces +----------------------- + +You might also find yourself using abstractions instead of implementations (especially +in grown applications) as it allows to easily replace some dependencies without +modifying the class depending of them. + +To follow this best practice, constructor arguments must be typehinted with interfaces +and not concrete classes. It allows to replace easily the current implementation +if necessary. It also allows to use other transformers. + +Let's introduce a ``TransformerInterface``:: + + // src/AppBundle/TransformerInterface.php + namespace AppBundle; + + interface TransformerInterface + { + public function transform($value); + } + +Then edit ``Rot13Transformer`` to make it implementing the new interface:: + + // ... + + class Rot13Transformer implements TransformerInterface + + // ... + + +And update ``TwitterClient`` to depend of this new interface:: + + class TwitterClient + { + // ... + + public function __construct(TransformerInterface $transformer) + { + // ... + } + + // ... + } + +Finally the service definition must be updated because, obviously, the autowiring +subsystem isn't able to find itself the interface implementation to register:: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + rot13_transformer: + class: 'AppBundle\Rot13Transformer' + + twitter_client: + class: 'AppBundle\TwitterClient' + autowire: true + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + + // ... + $definition1 = new Definition('AppBundle\Rot13Transformer'); + $container->setDefinition('rot13_transformer', $definition1); + + $definition2 = new Definition('AppBundle\TwitterClient'); + $definition2->setAutowired(true); + $container->setDefinition('twitter_client', $definition2); + +The autowiring subsystem detects that the ``rot13_transformer`` service implements +the ``TransformerInterface`` and injects it automatically. Even when using +interfaces (and you should), building the service graph and refactoring the project +is easier than with standard definitions. + +Dealing with Multiple Implementations of the Same Type +------------------------------------------------------ + +Last but not least, the autowiring feature allows to specify the default implementation +of a given type. Let's introduce a new implementation of the ``TransformerInterface`` +returning the result of the ROT13 transformation uppercased:: + + // src/AppBundle/UppercaseRot13Transformer.php + namespace AppBundle; + + class UppercaseTransformer implements TransformerInterface + { + private $transformer; + + public function __construct(TransformerInterface $transformer) + { + $this->transformer = $transformer; + } + + public function transform($value) + { + return strtoupper($this->transformer->transform($value)); + } + } + +This class is intended to decorate the any transformer and return its value uppercased. + +We can now refactor the controller to add another endpoint leveraging this new +transformer:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; + + class DefaultController extends Controller + { + /** + * @Route("/tweet") + * @Method("POST") + */ + public function tweetAction(Request $request) + { + return $this->tweet($request, 'twitter_client'); + } + + /** + * @Route("/tweet-uppercase") + * @Method("POST") + */ + public function tweetUppercaseAction(Request $request) + { + return $this->tweet($request, 'uppercase_twitter_client'); + } + + private function tweet(Request $request, $service) + { + $user = $request->request->get('user'); + $key = $request->request->get('key'); + $status = $request->request->get('status'); + + if (!$user || !$key || !$status) { + throw new BadRequestHttpException(); + } + + $this->get($service)->tweet($user, $key, $status); + + return new Response('OK'); + } + } + +The last step is to update service definitions to register this new implementation +and a Twitter client using it:: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + rot13_transformer: + class: AppBundle\Rot13Transformer + autowiring_types: AppBundle\TransformerInterface + + twitter_client: + class: AppBundle\TwitterClient + autowire: true + + uppercase_rot13_transformer: + class: AppBundle\UppercaseRot13Transformer + autowire: true + + uppercase_twitter_client: + class: AppBundle\TwitterClient + arguments: ['@uppercase_rot13_transformer'] + + .. code-block:: xml + + + + + + + + AppBundle\TransformerInterface + + + + + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Reference; + use Symfony\Component\DependencyInjection\Definition; + + // ... + $definition1 = new Definition('AppBundle\Rot13Transformer'); + $definition1->setAutowiringTypes(array('AppBundle\TransformerInterface')); + $container->setDefinition('rot13_transformer', $definition1); + + $definition2 = new Definition('AppBundle\TwitterClient'); + $definition2->setAutowired(true); + $container->setDefinition('twitter_client', $definition2); + + $definition3 = new Definition('AppBundle\UppercaseRot13Transformer'); + $definition3->setAutowired(true); + $container->setDefinition('uppercase_rot13_transformer', $definition3); + + $definition4 = new Definition('AppBundle\TwitterClient'); + $definition4->addArgument(new Reference('uppercase_rot13_transformer')); + $container->setDefinition('uppercase_twitter_client', $definition4); + +This deserves some explanations. You now have two services implementing the +``TransformerInterface``. The autowiring subsystem cannot guess which one +to use which leads to errors like this: + +.. code-block:: text + + [Symfony\Component\DependencyInjection\Exception\RuntimeException] + Unable to autowire argument of type "AppBundle\TransformerInterface" for the service "twitter_client". + +Fortunately, the ``autowiring_types`` key is here to specify which implementation +to use by default. This key can take a list of types if necessary. + +Thanks to this setting, the ``rot13_transformer`` service is automatically injected +as an argument of the ``uppercase_rot13_transformer`` and ``twitter_client`` services. For +the ``uppercase_twitter_client``, we use a standard service definition to inject +the specific ``uppercase_rot13_transformer`` service. + +As for other RAD features such as the FrameworkBundle controller or annotations, +keep in mind to not use autowiring in public bundles nor in large projects with +complex maintenance needs. + +.. _Rapid Application Development: https://en.wikipedia.org/wiki/Rapid_application_development +.. _ROT13: https://en.wikipedia.org/wiki/ROT13 diff --git a/components/dependency_injection/index.rst b/components/dependency_injection/index.rst index 313f29af3f4..bc6ec0daf73 100644 --- a/components/dependency_injection/index.rst +++ b/components/dependency_injection/index.rst @@ -8,6 +8,7 @@ DependencyInjection types parameters definitions + autowiring synthetic_services compilation tags diff --git a/components/map.rst.inc b/components/map.rst.inc index 8a8334ff25e..04064756636 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -50,6 +50,7 @@ * :doc:`/components/dependency_injection/types` * :doc:`/components/dependency_injection/parameters` * :doc:`/components/dependency_injection/definitions` + * :doc:`/components/dependency_injection/autowiring` * :doc:`/components/dependency_injection/synthetic_services` * :doc:`/components/dependency_injection/compilation` * :doc:`/components/dependency_injection/tags` diff --git a/cookbook/console/index.rst b/cookbook/console/index.rst index a063afcf920..76a3014f5e3 100644 --- a/cookbook/console/index.rst +++ b/cookbook/console/index.rst @@ -8,6 +8,6 @@ Console usage style command_in_controller - sending_emails + request_context logging commands_as_services diff --git a/cookbook/console/sending_emails.rst b/cookbook/console/request_context.rst similarity index 67% rename from cookbook/console/sending_emails.rst rename to cookbook/console/request_context.rst index 300ced4fd9b..ff90b182ede 100644 --- a/cookbook/console/sending_emails.rst +++ b/cookbook/console/request_context.rst @@ -1,9 +1,8 @@ .. index:: - single: Console; Sending emails single: Console; Generating URLs -How to Generate URLs and Send Emails from the Console -===================================================== +How to Generate URLs from the Console +===================================== Unfortunately, the command line context does not know about your VirtualHost or domain name. This means that if you generate absolute URLs within a @@ -81,39 +80,3 @@ from the ``router`` service and override its settings:: // ... your code here } } - -Using Memory Spooling ---------------------- - -Sending emails in a console command works the same way as described in the -:doc:`/cookbook/email/email` cookbook except if memory spooling is used. - -When using memory spooling (see the :doc:`/cookbook/email/spool` cookbook for more -information), you must be aware that because of how Symfony handles console -commands, emails are not sent automatically. You must take care of flushing -the queue yourself. Use the following code to send emails inside your -console command:: - - $message = new \Swift_Message(); - - // ... prepare the message - - $container = $this->getContainer(); - $mailer = $container->get('mailer'); - - $mailer->send($message); - - // now manually flush the queue - $spool = $mailer->getTransport()->getSpool(); - $transport = $container->get('swiftmailer.transport.real'); - - $spool->flushQueue($transport); - -Another option is to create an environment which is only used by console -commands and uses a different spooling method. - -.. note:: - - Taking care of the spooling is only needed when memory spooling is used. - If you are using file spooling (or no spooling at all), there is no need - to flush the queue manually within the command. diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 9ca3bef00a6..5a1c47d0503 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -47,7 +47,7 @@ * :doc:`/cookbook/console/usage` * :doc:`/cookbook/console/style` * :doc:`/cookbook/console/command_in_controller` - * :doc:`/cookbook/console/sending_emails` + * :doc:`/cookbook/console/request_context` * :doc:`/cookbook/console/logging` * :doc:`/cookbook/console/commands_as_services` @@ -176,6 +176,7 @@ * :doc:`/cookbook/security/csrf_in_login_form` * :doc:`/cookbook/security/named_encoders` * :doc:`/cookbook/security/multiple_user_providers` + * :doc:`/cookbook/security/multiple_guard_authenticators` * :doc:`/cookbook/security/firewall_restriction` * :doc:`/cookbook/security/host_restriction` * :doc:`/cookbook/security/user_checkers` @@ -259,3 +260,4 @@ * :doc:`/cookbook/workflow/new_project_git` * :doc:`/cookbook/workflow/new_project_svn` + * :doc:`/cookbook/workflow/homestead` diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index ed13a2116f9..61efff1aa23 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -22,6 +22,7 @@ Authentication (Identifying/Logging in the User) csrf_in_login_form named_encoders multiple_user_providers + multiple_guard_authenticators firewall_restriction host_restriction user_checkers diff --git a/cookbook/security/multiple_guard_authenticators.rst b/cookbook/security/multiple_guard_authenticators.rst new file mode 100644 index 00000000000..acb1f774422 --- /dev/null +++ b/cookbook/security/multiple_guard_authenticators.rst @@ -0,0 +1,172 @@ +How to Use Multiple Guard Authenticators +======================================== + +.. versionadded:: 2.8 + The ``Guard`` component was introduced in Symfony 2.8. + +The Guard authentication component allows you to easily use many different +authenticators at a time. + +An entry point is a service id (of one of your authenticators) whose +``start()`` method is called to start the authentication process. + +Multiple Authenticators with Shared Entry Point +----------------------------------------------- + +Sometimes you want to offer your users different authentication mechanisms like +a form login and a Facebook login while both entry points redirect the user to +the same login page. +However, in your configuration you have to explicitly say which entry point +you want to use. + +This is how your security configuration can look in action: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + firewalls: + default: + anonymous: ~ + guard: + authenticators: + - app.form_login_authenticator + - app.facebook_connect_authenticator + entry_point: app.form_login_authenticator + + .. code-block:: xml + + + + + + + + + + + app.form_login_authenticator + app.facebook_connect_authenticator + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + 'default' => array( + 'anonymous' => null, + 'guard' => array( + 'entry_point' => 'app.form_login_authenticator', + 'authenticators' => array( + 'app.form_login_authenticator', + 'app.facebook_connect_authenticator' + ), + ), + ), + ), + )); + +There is one limitation with this approach - you have to use exactly one entry point. + +Multiple Authenticators with Separate Entry Points +-------------------------------------------------- + +However, there are use cases where you have authenticators that protect different +parts of your application. For example, you have a login form that protects +the secured area of your application front-end and API end points that are +protected with API tokens. As you can only configure one entry point per firewall, +the solution is to split the configuration into two separate firewalls: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + firewalls: + api: + pattern: ^/api/ + guard: + authenticators: + - app.api_token_authenticator + default: + anonymous: ~ + guard: + authenticators: + - app.form_login_authenticator + access_control: + - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/api, roles: ROLE_API_USER } + - { path: ^/, roles: ROLE_USER } + + .. code-block:: xml + + + + + + + + + + app.api_token_authenticator + + + + + + app.form_login_authenticator + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + 'api' => array( + 'pattern' => '^/api', + 'guard' => array( + 'authenticators' => array( + 'app.api_token_authenticator', + ), + ), + ), + 'default' => array( + 'anonymous' => null, + 'guard' => array( + 'authenticators' => array( + 'app.form_login_authenticator', + ), + ), + ), + ), + 'access_control' => array( + array('path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), + array('path' => '^/api', 'role' => 'ROLE_API_USER'), + array('path' => '^/', 'role' => 'ROLE_USER'), + ), + )); diff --git a/cookbook/workflow/homestead.rst b/cookbook/workflow/homestead.rst new file mode 100644 index 00000000000..2f50a3305ac --- /dev/null +++ b/cookbook/workflow/homestead.rst @@ -0,0 +1,76 @@ +.. index:: Vagrant, Homestead + +Using Symfony with Homestead/Vagrant +==================================== + +In order to develop a Symfony application, you might want to use a virtual +development environment instead of the built-in server or WAMP/LAMP. Homestead_ +is an easy-to-use Vagrant_ box to get a virtual environment up and running +quickly. + +.. tip:: + + Due to the amount of filesystem operations in Symfony (e.g. updating cache + files and writing to log files), Symfony can slow down signifcantly. To + improve the speed, consider :ref:`overriding the cache and log directories ` + to a location outside the NFS share (for instance, by using + :phpfunction:`sys_get_temp_dir`). You can read `this blog post`_ for more + tips to speed up Symfony on Vagrant. + +Install Vagrant and Homestead +----------------------------- + +Before you can use Homestead, you need to install and configure Vagrant and +Homestead as explained in `the Homestead documentation`_. + +Setting Up a Symfony Application +-------------------------------- + +Imagine you've installed your Symfony application in +``~/projects/symfony_demo`` on your local system. You first need Homestead to +sync your files in this project. Execute ``homestead edit`` to edit the +Homestead configuration and configure the ``~/projects`` directory: + +.. code-block:: yaml + + # ... + folders: + - map: ~/projects + to: /home/vagrant/projects + +The ``projects/`` directory on your PC is now accessible at +``/home/vagrant/projects`` in the Homestead environment. + +After you've done this, configure the Symfony application in the Homestead +configuration: + +.. code-block:: yaml + + # ... + sites: + - map: symfony-demo.dev + to: /home/vagrant/projects/symfony_demo/web + type: symfony + +The ``type`` option tells Homestead to use the Symfony nginx configuration. + +At last, edit the hosts file on your local machine to map ``symfony-demo.dev`` +to ``192.168.10.10`` (which is the IP used by Homestead):: + + # /etc/hosts (unix) or C:\Windows\System32\drivers\etc\hosts (Windows) + 192.168.10.10 symfony-demo.dev + +Now, navigate to ``http://symfony-demo.dev`` in your web browser and enjoy +developing your Symfony application! + +.. seealso:: + + To learn more features of Homestead, including Blackfire Profiler + integration, automatic creation of MySQL databases and more, read the + `Daily Usage`_ section of the Homestead documentation. + +.. _Homestead: http://laravel.com/docs/homestead +.. _Vagrant: https://www.vagrantup.com/ +.. _the Homestead documentation: http://laravel.com/docs/homestead#installation-and-setup +.. _Daily Usage: http://laravel.com/docs/5.1/homestead#daily-usage +.. _this blog post: http://www.whitewashing.de/2013/08/19/speedup_symfony2_on_vagrant_boxes.html diff --git a/cookbook/workflow/index.rst b/cookbook/workflow/index.rst index 6b2382ae235..7da4491bbc1 100644 --- a/cookbook/workflow/index.rst +++ b/cookbook/workflow/index.rst @@ -6,3 +6,4 @@ Workflow new_project_git new_project_svn + homestead diff --git a/redirection_map b/redirection_map index cc69e78c53b..beb2459613e 100644 --- a/redirection_map +++ b/redirection_map @@ -1,5 +1,6 @@ /book/stable_api /contributing/code/bc /book/internals /reference/events +/cookbook/console/sending_emails /cookbook/console/request_context /cookbook/deployment-tools /cookbook/deployment/tools /cookbook/doctrine/migrations /bundles/DoctrineFixturesBundle/index /cookbook/doctrine/doctrine_fixtures /bundles/DoctrineFixturesBundle/index diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c867b1918e4..e17154be914 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -133,6 +133,15 @@ Each part will be explained in the next section. provider: some_key_from_above http_digest: provider: some_key_from_above + guard: + # A key from the "providers" section of your security config, in case your user provider is different than the firewall + provider: ~ + + # A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication + entry_point: null + + # An array of service ids for all of your "authenticators" + authenticators: [] form_login: # submit the login form here check_path: /login_check diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index aaf48529124..89bd9328997 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -16,6 +16,7 @@ To use this field, you must specify *either* ``choices`` or ``choice_loader`` op | | - `choice_loader`_ | | | - `choice_label`_ | | | - `choice_attr`_ | +| | - `choice_translation_domain`_ | | | - `placeholder`_ | | | - `expanded`_ | | | - `multiple`_ | @@ -38,6 +39,7 @@ To use this field, you must specify *either* ``choices`` or ``choice_loader`` op | | - `label_format`_ | | | - `mapped`_ | | | - `required`_ | +| | - `translation_domain`_ | +-------------+------------------------------------------------------------------------------+ | Parent type | :doc:`FormType ` | +-------------+------------------------------------------------------------------------------+ @@ -253,6 +255,8 @@ These options inherit from the :doc:`FormType `: .. include:: /reference/forms/types/options/required.rst.inc +.. include:: /reference/forms/types/options/choice_type_translation_domain.rst.inc + Field Variables --------------- diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index 86ca95e54c3..4ecaad43473 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -24,6 +24,7 @@ objects from the database. | options | | | | - `placeholder`_ | | | - `choice_translation_domain`_ | +| | - `translation_domain`_ | | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | @@ -190,7 +191,6 @@ em If specified, this entity manager will be used to load the choices instead of the ``default`` entity manager. - Overridden Options ------------------ @@ -212,6 +212,8 @@ These options inherit from the :doc:`ChoiceType ` .. include:: /reference/forms/types/options/choice_translation_domain.rst.inc +.. include:: /reference/forms/types/options/choice_type_translation_domain.rst.inc + .. include:: /reference/forms/types/options/expanded.rst.inc .. include:: /reference/forms/types/options/multiple.rst.inc diff --git a/reference/forms/types/options/choice_type_translation_domain.rst.inc b/reference/forms/types/options/choice_type_translation_domain.rst.inc new file mode 100644 index 00000000000..87cd76cff9a --- /dev/null +++ b/reference/forms/types/options/choice_type_translation_domain.rst.inc @@ -0,0 +1,8 @@ +translation_domain +~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``messages`` + +In case `choice_translation_domain`_ is set to ``true`` or ``null``, this +configures the exact translation domain that will be used for any labels or +options that are rendered for this field