diff --git a/_build/redirection_map b/_build/redirection_map index c0ff3466573..c30723eac58 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -576,3 +576,6 @@ /components/serializer /serializer /serializer/custom_encoder /serializer/encoders#serializer-custom-encoder /components/string /string +/form/button_based_validation /form/validation_groups +/form/data_based_validation /form/validation_groups +/form/validation_group_service_resolver /form/validation_groups diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 850862a1a90..542532ee1af 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -100,8 +100,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; -That's it! To test it, start the :doc:`Symfony Local Web Server -`: +That's it! To test it, start the :ref:`Symfony local web server `: .. code-block:: terminal @@ -471,8 +470,7 @@ this: ├─ composer.json └─ composer.lock -As before you can use the :doc:`Symfony Local Web Server -`: +As before you can use the :ref:`Symfony local web server `: .. code-block:: terminal diff --git a/console.rst b/console.rst index b0e1e8d18c9..be9292f92a5 100644 --- a/console.rst +++ b/console.rst @@ -100,12 +100,8 @@ completion (by default, by pressing the Tab key). .. tip:: - If you are using the :doc:`Symfony local web server - `, it is recommended to use the built-in completion - script that will ensure the right PHP version and configuration are used when - running the Console Completion. Run ``symfony completion --help`` for the - installation instructions for your shell. The Symfony CLI will provide - completion for the ``console`` and ``composer`` commands. + If you are using the :doc:`Symfony CLI ` tool, follow + :ref:`these instructions ` to enable autocompletion. .. _console_creating-command: diff --git a/contributing/code/reproducer.rst b/contributing/code/reproducer.rst index 914ab2fd3f2..c2208b70b09 100644 --- a/contributing/code/reproducer.rst +++ b/contributing/code/reproducer.rst @@ -65,8 +65,8 @@ to a route definition. Then, after creating your project: of controllers, actions, etc. as in your original application. #. Create a small controller and add your routing definition that shows the bug. #. Don't create or modify any other file. -#. Install the :doc:`local web server ` provided by Symfony - and use the ``symfony server:start`` command to browse to the new route and +#. Install the :doc:`Symfony CLI ` tool and use the + ``symfony server:start`` command to browse to the new route and see if the bug appears or not. #. If you can see the bug, you're done and you can already share the code with us. #. If you can't see the bug, you must keep making small changes. For example, if diff --git a/create_framework/front_controller.rst b/create_framework/front_controller.rst index f2843b9067a..cc440dd8910 100644 --- a/create_framework/front_controller.rst +++ b/create_framework/front_controller.rst @@ -154,7 +154,7 @@ Now, configure your web server root directory to point to ``web/`` and all other files will no longer be accessible from the client. To test your changes in a browser (``http://localhost:4321/hello?name=Fabien``), -run the :doc:`Symfony Local Web Server `: +run the :ref:`Symfony local web server `: .. code-block:: terminal diff --git a/create_framework/introduction.rst b/create_framework/introduction.rst index d65bb4fe85e..420537a8088 100644 --- a/create_framework/introduction.rst +++ b/create_framework/introduction.rst @@ -101,7 +101,7 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $name); -You can use the :doc:`Symfony Local Web Server ` to test +You can use the :ref:`Symfony local web server ` to test this great application in a browser (``http://localhost:8000/index.php?name=Fabien``): diff --git a/form/button_based_validation.rst b/form/button_based_validation.rst deleted file mode 100644 index 47f2673b079..00000000000 --- a/form/button_based_validation.rst +++ /dev/null @@ -1,36 +0,0 @@ -How to Choose Validation Groups Based on the Clicked Button -=========================================================== - -When your form contains multiple submit buttons, you can change the validation -group depending on which button is used to submit the form. For example, -consider a form in a wizard that lets you advance to the next step or go back -to the previous step. Also assume that when returning to the previous step, -the data of the form should be saved, but not validated. - -First, we need to add the two buttons to the form:: - - $form = $this->createFormBuilder($task) - // ... - ->add('nextStep', SubmitType::class) - ->add('previousStep', SubmitType::class) - ->getForm(); - -Then, we configure the button for returning to the previous step to run -specific validation groups. In this example, we want it to suppress validation, -so we set its ``validation_groups`` option to false:: - - $form = $this->createFormBuilder($task) - // ... - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->getForm(); - -Now the form will skip your validation constraints. It will still validate -basic integrity constraints, such as checking whether an uploaded file was too -large or whether you tried to submit text in a number field. - -.. seealso:: - - To see how to use a service to resolve ``validation_groups`` dynamically - read the :doc:`/form/validation_group_service_resolver` article. diff --git a/form/data_based_validation.rst b/form/data_based_validation.rst deleted file mode 100644 index b01bea10b16..00000000000 --- a/form/data_based_validation.rst +++ /dev/null @@ -1,72 +0,0 @@ -How to Choose Validation Groups Based on the Submitted Data -=========================================================== - -If you need some advanced logic to determine the validation groups (e.g. -based on submitted data), you can set the ``validation_groups`` option -to an array callback:: - - use App\Entity\Client; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => [ - Client::class, - 'determineValidationGroups', - ], - ]); - } - -This will call the static method ``determineValidationGroups()`` on the -``Client`` class after the form is submitted, but before validation is -invoked. The Form object is passed as an argument to that method (see next -example). You can also define whole logic inline by using a ``Closure``:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['person']; - } - - return ['company']; - }, - ]); - } - -Using the ``validation_groups`` option overrides the default validation -group which is being used. If you want to validate the default constraints -of the entity as well you have to adjust the option as follows:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['Default', 'person']; - } - - return ['Default', 'company']; - }, - ]); - } - -You can find more information about how the validation groups and the default constraints -work in the article about :doc:`validation groups `. diff --git a/form/validation_group_service_resolver.rst b/form/validation_group_service_resolver.rst deleted file mode 100644 index 82a6f65d6ec..00000000000 --- a/form/validation_group_service_resolver.rst +++ /dev/null @@ -1,58 +0,0 @@ -How to Dynamically Configure Form Validation Groups -=================================================== - -Sometimes you need advanced logic to determine the validation groups. If they -can't be determined by a callback, you can use a service. Create a service -that implements ``__invoke()`` which accepts a ``FormInterface`` as a -parameter:: - - // src/Validation/ValidationGroupResolver.php - namespace App\Validation; - - use Symfony\Component\Form\FormInterface; - - class ValidationGroupResolver - { - public function __construct( - private object $service1, - private object $service2, - ) { - } - - public function __invoke(FormInterface $form): array - { - $groups = []; - - // ... determine which groups to apply and return an array - - return $groups; - } - } - -Then in your form, inject the resolver and set it as the ``validation_groups``:: - - // src/Form/MyClassType.php; - namespace App\Form; - - use App\Validation\ValidationGroupResolver; - use Symfony\Component\Form\AbstractType; - use Symfony\Component\OptionsResolver\OptionsResolver; - - class MyClassType extends AbstractType - { - public function __construct( - private ValidationGroupResolver $groupResolver, - ) { - } - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => $this->groupResolver, - ]); - } - } - -This will result in the form validator invoking your group resolver to set the -validation groups returned when validating. diff --git a/form/validation_groups.rst b/form/validation_groups.rst index 4addc1ba1a7..3157ef7bc5b 100644 --- a/form/validation_groups.rst +++ b/form/validation_groups.rst @@ -1,39 +1,163 @@ -How to Define the Validation Groups to Use -========================================== +Configuring Validation Groups in Forms +====================================== -Validation Groups ------------------ +If the object handled in your form uses :doc:`validation groups `, +you need to specify which validation group(s) the form should apply. -If your object takes advantage of :doc:`validation groups `, -you'll need to specify which validation group(s) your form should use. Pass -this as an option when :ref:`creating forms in controllers `:: +To define them when :ref:`creating forms in classes `, +use the ``configureOptions()`` method:: + + use Symfony\Component\OptionsResolver\OptionsResolver; + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // ... + 'validation_groups' => ['registration'], + ]); + } + +When :ref:`creating forms in controllers `, pass +it as a form option:: $form = $this->createFormBuilder($user, [ 'validation_groups' => ['registration'], ])->add(/* ... */); -When :ref:`creating forms in classes `, add the -following to the ``configureOptions()`` method:: +In both cases, *only* the ``registration`` group will be used to validate the +object. To apply the ``registration`` group *and* all constraints not in any +other group, add the special ``Default`` group:: + + [ + // ... + 'validation_groups' => ['Default', 'registration'], + ] +.. note:: + + You can use any name for your validation groups. Symfony recommends using + "lower snake case" (e.g. ``foo_bar``), while automatically generated + groups use "UpperCamelCase" (e.g. ``Default``, ``SomeClassName``). + +Choosing Validation Groups Based on the Clicked Button +------------------------------------------------------ + +When your form has :doc:`multiple submit buttons `, you +can change the validation group based on the clicked button. For example, in a +multi-step form like the following, you might want to skip validation when +returning to a previous step:: + + $form = $this->createFormBuilder($task) + // ... + ->add('nextStep', SubmitType::class) + ->add('previousStep', SubmitType::class) + ->getForm(); + +To do so, configure the validation groups of the ``previousStep`` button to +``false``, which is a special value that skips validation:: + + $form = $this->createFormBuilder($task) + // ... + ->add('previousStep', SubmitType::class, [ + 'validation_groups' => false, + ]) + ->getForm(); + +Now the form will skip your validation constraints when that button is clicked. +It will still validate basic integrity constraints, such as checking whether an +uploaded file was too large or whether you tried to submit text in a number field. + +Choosing Validation Groups Based on Submitted Data +-------------------------------------------------- + +To determine validation groups dynamically based on submitted data, use a +callback. This is called after the form is submitted, but before validation is +invoked. The callback receives the form object as its first argument:: + + use App\Entity\Client; + use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - // ... - 'validation_groups' => ['registration'], + 'validation_groups' => function (FormInterface $form): array { + $data = $form->getData(); + + if (Client::TYPE_PERSON === $data->getType()) { + return ['Default', 'person']; + } + + return ['Default', 'company']; + }, ]); } -In both of these cases, *only* the ``registration`` validation group will -be used to validate the underlying object. To apply the ``registration`` -group *and* all constraints that are not in a group, use:: +.. note:: + + Adding ``Default`` to the list of validation groups is common but not mandatory. + See the main :doc:`article about validation groups ` to + learn more about validation groups and the default constraints. - 'validation_groups' => ['Default', 'registration'] +You can also pass a static class method callback:: -.. note:: + 'validation_groups' => [Client::class, 'determineValidationGroups'] + +Choosing Validation Groups via a Service +---------------------------------------- + +If validation group logic requires services or can't fit in a closure, use a +dedicated validation group resolver service. The class of this service must +be invokable and receives the form object as its first argument:: + + // src/Validation/ValidationGroupResolver.php + namespace App\Validation; + + use Symfony\Component\Form\FormInterface; + + class ValidationGroupResolver + { + public function __construct( + private object $service1, + private object $service2, + ) { + } + + public function __invoke(FormInterface $form): array + { + $groups = []; + + // ... determine which groups to return + + return $groups; + } + } + +Then use the service in your form type:: + + namespace App\Form; + + use App\Validation\ValidationGroupResolver; + use Symfony\Component\Form\AbstractType; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class MyClassType extends AbstractType + { + public function __construct( + private ValidationGroupResolver $groupResolver, + ) { + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'validation_groups' => $this->groupResolver, + ]); + } + } + +Learn More +---------- - You can choose any name for your validation groups, but Symfony recommends - using "lower snake case" names (e.g. ``foo_bar``) in contrast with the - automatic validation groups created by Symfony, which use "upper camel case" - (e.g. ``Default``, ``SomeClassName``). +For more information about how validation groups work, see +:doc:`/validation/groups`. diff --git a/forms.rst b/forms.rst index 008c60a66c6..83065d7524b 100644 --- a/forms.rst +++ b/forms.rst @@ -995,8 +995,6 @@ Validation: :maxdepth: 1 /form/validation_groups - /form/validation_group_service_resolver - /form/button_based_validation /form/disabling_validation Misc.: diff --git a/frontend/encore/dev-server.rst b/frontend/encore/dev-server.rst index c3287a7bc2f..a3adb04685a 100644 --- a/frontend/encore/dev-server.rst +++ b/frontend/encore/dev-server.rst @@ -53,9 +53,9 @@ method in your ``webpack.config.js`` file: Enabling HTTPS using the Symfony Web Server ------------------------------------------- -If you're using the :doc:`Symfony web server ` locally with HTTPS, -you'll need to also tell the dev-server to use HTTPS. To do this, you can reuse the Symfony web -server SSL certificate: +If you're using the :ref:`Symfony local web server ` locally +with HTTPS, you'll need to also tell the dev-server to use HTTPS. To do this, +you can reuse the Symfony web server SSL certificate: .. code-block:: diff diff --git a/mailer.rst b/mailer.rst index 81af4c38a15..46517dfd968 100644 --- a/mailer.rst +++ b/mailer.rst @@ -2040,8 +2040,8 @@ Enabling an Email Catcher When developing locally, it is recommended to use an email catcher. If you have enabled Docker support via Symfony recipes, an email catcher is automatically -configured. In addition, if you are using the :doc:`Symfony local web server -`, the mailer DSN is automatically exposed via the +configured. In addition, if you are using the :doc:`Symfony CLI ` +tool, the mailer DSN is automatically exposed via the :ref:`symfony binary Docker integration `. Sending Test Emails diff --git a/mercure.rst b/mercure.rst index fd32d01c01f..da04ea69646 100644 --- a/mercure.rst +++ b/mercure.rst @@ -72,7 +72,7 @@ Thanks to :doc:`the Docker integration of Symfony `, :ref:`Flex ` proposes to install a Mercure hub for development. Run ``docker-compose up`` to start the hub if you have chosen this option. -If you use the :doc:`Symfony Local Web Server `, +If you use the :ref:`Symfony local web server `, you must start it with the ``--no-tls`` option to prevent mixed content and invalid TLS certificate issues: diff --git a/messenger.rst b/messenger.rst index 18fc5e03cec..61763453764 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1733,7 +1733,7 @@ The transport has a number of options: .. note:: - Set ``redeliver_timeout`` to a greater value than your slowest message + Set ``redeliver_timeout`` to a greater value than your longest message duration. Otherwise, some messages will start a second time while the first one is still being handled. diff --git a/page_creation.rst b/page_creation.rst index ce7a2b1a935..e58e99f317c 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -80,7 +80,7 @@ metadata to code): } } -That's it! If you are using :doc:`the Symfony web server `, +That's it! If you are using :ref:`the Symfony web server `, try it out by going to: http://localhost:8000/lucky/number .. tip:: diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 997bb05904e..ba7cc78e28b 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -42,8 +42,8 @@ Symfony application: Can we already load the project in a browser? Yes! You can set up :doc:`Nginx or Apache ` and configure their document root to be the ``public/`` directory. But, for development, it's better -to :doc:`install the Symfony local web server ` and run -it as follows: +to install the :doc:`Symfony CLI ` tool and run its +:ref:`local web server ` as follows: .. code-block:: terminal diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index ff900b132e1..e60e5d67c99 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -57,9 +57,14 @@ file_cache_dir The directory to store cache files for annotations, in case ``annotations.cache`` is set to ``'file'``. +.. _reference-assets: + assets ~~~~~~ +The following options configure the behavior of the +:ref:`Twig asset() function `. + .. _reference-assets-base-path: base_path @@ -67,7 +72,7 @@ base_path **type**: ``string`` -This option allows you to define a base path to be used for assets: +This option allows you to prepend a base path to the URLs generated for assets: .. configuration-block:: @@ -106,6 +111,9 @@ This option allows you to define a base path to be used for assets: ->basePath('/images'); }; +With this configuration, a call to ``asset('logo.png')`` will generate +``/images/logo.png`` instead of ``/logo.png``. + .. _reference-templating-base-urls: .. _reference-assets-base-urls: diff --git a/reference/forms/types/options/validation_groups.rst.inc b/reference/forms/types/options/validation_groups.rst.inc index 1f5c9a597a3..6957bf203a3 100644 --- a/reference/forms/types/options/validation_groups.rst.inc +++ b/reference/forms/types/options/validation_groups.rst.inc @@ -1,59 +1,14 @@ ``validation_groups`` ~~~~~~~~~~~~~~~~~~~~~ -**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` or ``null`` **default**: ``null`` +**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence`, or ``null`` **default**: ``null`` -This option is only valid on the root form and is used to specify which -groups will be used by the validator. +This option is only valid on the root form. It specifies which validation groups +will be used by the validator. -For ``null`` the validator will just use the ``Default`` group. - -If you specify the groups as an array or string they will be used by the -validator as they are:: - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => 'Registration', - ]); - } - -This is equivalent to passing the group as array:: - - 'validation_groups' => ['Registration'], - -The form's data will be :doc:`validated against all given groups `. - -If the validation groups depend on the form's data a callable may be passed to -the option. Symfony will then pass the form when calling it:: - - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $entity = $form->getData(); - - return $entity->isUser() ? ['User'] : ['Company']; - }, - ]); - } - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. - -.. note:: - - When your form contains multiple submit buttons, you can change the - validation group depending on :doc:`which button is used ` - to submit the form. - - If you need advanced logic to determine the validation groups have - a look at :doc:`/form/validation_group_service_resolver`. +If set to ``null``, the validator will use only the ``Default`` group. For the +other possible values, see the main article about +:doc:`using validation groups in Symfony forms ` In some cases, you want to validate your groups step by step. To do this, you can pass a :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 70fa429685a..5d863fbe8b4 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -102,28 +102,8 @@ validation_groups **type**: ``array`` **default**: ``null`` When your form contains multiple submit buttons, you can change the validation -group based on the button which was used to submit the form. Imagine a registration -form wizard with buttons to go to the previous or the next step:: - - use Symfony\Component\Form\Extension\Core\Type\SubmitType; - // ... - - $form = $this->createFormBuilder($user) - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->add('nextStep', SubmitType::class, [ - 'validation_groups' => ['Registration'], - ]) - ->getForm(); - -The special ``false`` ensures that no validation is performed when the previous -step button is clicked. When the second button is clicked, all constraints -from the "Registration" are validated. - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. +group based on the clicked button. Read the article about +:doc:`using validation groups in Symfony forms `. Form Variables -------------- diff --git a/serializer/encoders.rst b/serializer/encoders.rst index d2cf1f9cab8..8238d4d057d 100644 --- a/serializer/encoders.rst +++ b/serializer/encoders.rst @@ -271,7 +271,7 @@ Creating a Custom Encoder Imagine you want to serialize and deserialize `NEON`_. For that you'll have to create your own encoder:: - // src/Serializer/YamlEncoder.php + // src/Serializer/NeonEncoder.php namespace App\Serializer; use Nette\Neon\Neon; diff --git a/setup.rst b/setup.rst index 02b32b8df9b..20d71d112eb 100644 --- a/setup.rst +++ b/setup.rst @@ -121,8 +121,8 @@ development. .. _symfony-binary-web-server: However for local development, the most convenient way of running Symfony is by -using the :doc:`local web server ` provided by the -``symfony`` binary. This local server provides among other things support for +using the :ref:`local web server ` provided by the +Symfony CLI tool. This local server provides among other things support for HTTP/2, concurrent requests, TLS/SSL and automatic generation of security certificates. diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst index 0dcce759d2e..ccd79e4b423 100644 --- a/setup/symfony_cli.rst +++ b/setup/symfony_cli.rst @@ -21,6 +21,8 @@ The Symfony CLI is available as a standalone executable that supports Linux, macOS, and Windows. Download and install it following the instructions on `symfony.com/download`_. +.. _symfony-cli-autocompletion: + Shell Autocompletion ~~~~~~~~~~~~~~~~~~~~ @@ -75,6 +77,8 @@ projects quickly: Pass the ``--cloud`` option to initialize a Symfony Cloud project at the same time the Symfony project is created. +.. _symfony-cli-server: + Running the Local Web Server ---------------------------- diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index b202adc2443..4b562d4f79e 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -2,7 +2,7 @@ Configuring a Web Server ======================== The preferred way to develop your Symfony application is to use -:doc:`Symfony Local Web Server `. +:ref:`Symfony local web server `. However, when running the application in the production environment, you'll need to use a fully-featured web server. This article describes how to use Symfony diff --git a/templates.rst b/templates.rst index c33088e18dc..fc353384202 100644 --- a/templates.rst +++ b/templates.rst @@ -312,20 +312,18 @@ You can now use the ``asset()`` function: {# the JS file lives at "public/bundles/acme/js/loader.js" #} -The ``asset()`` function's main purpose is to make your application more portable. -If your application lives at the root of your host (e.g. ``https://example.com``), -then the rendered path should be ``/images/logo.png``. But if your application -lives in a subdirectory (e.g. ``https://example.com/my_app``), each asset path -should render with the subdirectory (e.g. ``/my_app/images/logo.png``). The -``asset()`` function takes care of this by determining how your application is -being used and generating the correct paths accordingly. - -.. tip:: - - The ``asset()`` function supports various cache busting techniques via the - :ref:`version `, - :ref:`version_format `, and - :ref:`json_manifest_path ` configuration options. +Using the ``asset()`` function is recommended for these reasons: + +* **Asset versioning**: ``asset()`` appends a version hash to asset URLs for + cache busting. This works both via :doc:`AssetMapper ` and the + :doc:`Asset component ` (see also the + :ref:`assets configuration options `, such as ``version`` + and ``version_format``). + +* **Application portability**: whether your app is hosted at the root + (e.g. ``https://example.com``) or in a subdirectory (e.g. ``https://example.com/my_app``), + ``asset()`` generates the correct path (e.g. ``/images/logo.png`` vs ``/my_app/images/logo.png``) + automatically based on your app's base URL. If you need absolute URLs for assets, use the ``absolute_url()`` Twig function as follows: