From 02f31ac96e9a079e2305188423b64475c6b3be79 Mon Sep 17 00:00:00 2001 From: Abdellatif Ait boudad Date: Mon, 14 Aug 2017 11:38:08 +0100 Subject: [PATCH 0001/8077] [Translation] create custom message formatter. --- components/translation.rst | 3 +- .../translation/custom_message_formatter.rst | 55 +++++++++++++++++++ reference/configuration/framework.rst | 15 +++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 components/translation/custom_message_formatter.rst diff --git a/components/translation.rst b/components/translation.rst index dfbf9fd6259..30b69797414 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -34,9 +34,8 @@ The constructor of the ``Translator`` class needs one argument: The locale. .. code-block:: php use Symfony\Component\Translation\Translator; - use Symfony\Component\Translation\MessageSelector; - $translator = new Translator('fr_FR', new MessageSelector()); + $translator = new Translator('fr_FR'); .. note:: diff --git a/components/translation/custom_message_formatter.rst b/components/translation/custom_message_formatter.rst new file mode 100644 index 00000000000..22a650ea23c --- /dev/null +++ b/components/translation/custom_message_formatter.rst @@ -0,0 +1,55 @@ +.. index:: + single: Translation; Create Custom Message formatter + +Create Custom Message Formatter +=============================== + +The default Message Formatter provide a simple and easy way that deals with the most common use-cases +such as message placeholders and pluralization. But in some cases, you may want to use a custom message formatter +that fit to your specific needs, for example, handle nested conditions of pluralization or select sub-messages +via a fixed set of keywords (e.g. gender). + +Suppose in your application you want to displays different text depending on arbitrary conditions, +for example upon whether the guest is male or female. To do this, we will use the `ICU Message Format`_ +which the most suitable ones you first need to create a `IntlMessageFormatter` and pass it to the `Translator`. + +.. _components-translation-message-formatter: + +Creating a Custom Message Formatter +----------------------------------- + +To define a custom message formatter that is able to read these kinds of rules, you must create a +new class that implements the +:class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`:: + + use Symfony\Component\Translation\Formatter\MessageFormatterInterface; + + class IntlMessageFormatter implements MessageFormatterInterface + { + public function format($message, $locale, array $parameters = array()) + { + $formatter = new \MessageFormatter($locale, $message); + if (null === $formatter) { + throw new \InvalidArgumentException(sprintf('Invalid message format. Reason: %s (error #%d)', intl_get_error_message(), intl_get_error_code())); + } + + $message = $formatter->format($parameters); + if ($formatter->getErrorCode() !== U_ZERO_ERROR) { + throw new \InvalidArgumentException(sprintf('Unable to format message. Reason: %s (error #%s)', $formatter->getErrorMessage(), $formatter->getErrorCode())); + } + + return $message; + } + } + +Once created, simply pass it as the second argument to the `Translator`:: + + use Symfony\Component\Translation\Translator; + + $translator = new Translator('fr_FR', new IntlMessageFormatter()); + + var_dump($translator->trans('The guest is {gender, select, m {male} f {female}}', [ 'gender' => 'm' ])); + +It will print *"The guest is male"*. + +.. _`ICU Message Format`: http://userguide.icu-project.org/formatparse/messages diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 50c5ac2cde0..5cc0aa04b4d 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -85,6 +85,7 @@ Configuration * :ref:`enabled ` * `fallbacks`_ * `logging`_ + * `formatter`_ * :ref:`paths ` * `property_access`_ * `magic_call`_ @@ -1481,6 +1482,20 @@ for a given key. The logs are made to the ``translation`` channel and at the ``debug`` for level for keys where there is a translation in the fallback locale and the ``warning`` level if there is no translation to use at all. +.. _reference-framework-translator-formatter: + +formatter +......... + +**type**: ``string`` **default**: ``translator.formatter.default`` + +The service that is used to format message. The service +has to implement the :class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`. + +.. seealso:: + + For more details, see :doc:`/components/translation/custom_message_formatter`. + .. _reference-translator-paths: paths From aecac404308e20cd32d9e2b2e2eea8c68105314f Mon Sep 17 00:00:00 2001 From: Benoit GALATI Date: Fri, 5 Apr 2019 14:05:58 +0200 Subject: [PATCH 0002/8077] Alias Monolog\Formatter\JsonFormatter instead of creating a new one --- logging/formatter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging/formatter.rst b/logging/formatter.rst index dfde6aabbff..816aac77931 100644 --- a/logging/formatter.rst +++ b/logging/formatter.rst @@ -17,7 +17,7 @@ configure your handler to use it: services: # ... - Monolog\Formatter\JsonFormatter: ~ + Monolog\Formatter\JsonFormatter: '@monolog.formatter.json' # app/config/config_prod.yml (and/or config_dev.yml) monolog: From dc91bfd7dfd8d61a053e01bbafe69286e3e880c4 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 19 Apr 2019 19:59:08 +0300 Subject: [PATCH 0003/8077] #21571 updated "Authentication Success and Failure Events" section --- components/security/authentication.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 9430808c825..ddf2ec3b993 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -289,9 +289,9 @@ Authentication Success and Failure Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When a provider authenticates the user, a ``security.authentication.success`` -event is dispatched. But beware - this event will fire, for example, on *every* -request if you have session-based authentication. See ``security.interactive_login`` -below if you need to do something when a user *actually* logs in. +event is dispatched. But beware - this event may fire, for example, on *every* +request if you have session-based authentication, if ``always_authenticate_before_granting`` is enabled or if token is not authenticated before AccessListener is invoked. +See ``security.interactive_login`` below if you need to do something when a user *actually* logs in. When a provider attempts authentication but fails (i.e. throws an ``AuthenticationException``), a ``security.authentication.failure`` event is dispatched. You could listen on From d0161b468386be35db3947fba83961300ca75161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Berislav=20Balogovi=C4=87?= Date: Mon, 22 Apr 2019 22:20:01 +0200 Subject: [PATCH 0004/8077] Extend framework lock configuration reference --- components/lock.rst | 2 + reference/configuration/framework.rst | 132 +++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 529696e082c..08c5177dfa0 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -69,6 +69,8 @@ method can be safely called repeatedly, even if the lock is already acquired. across several requests. To disable the automatic release behavior, set the third argument of the ``createLock()`` method to ``false``. +.. _lock-blocking-locks: + Blocking Locks -------------- diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index de74aedfc8c..60fadc0b0d8 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -87,6 +87,12 @@ Configuration * `http_method_override`_ * `ide`_ * :ref:`lock ` + + * :ref:`enabled ` + * :ref:`resources ` + + * :ref:`name ` + * `php_errors`_ * `log`_ @@ -164,7 +170,7 @@ Configuration * `engines`_ * :ref:`form ` - * `resources`_ + * :ref:`resources ` * `hinclude_default_template`_ * `loaders`_ @@ -1518,6 +1524,8 @@ is disabled. This can be either a template name or the content itself. form .... +.. _reference-templating-form-resources: + resources """"""""" @@ -2175,11 +2183,131 @@ example, when warming caches offline). lock ~~~~ -**type**: ``string`` +**type**: ``string`` | ``array`` The default lock adapter. If not defined, the value is set to ``semaphore`` when available, or to ``flock`` otherwise. Store's DSN are also allowed. +.. _reference-lock-enabled: + +enabled +....... + +**type**: ``boolean`` **default**: ``true`` + +Whether to enable the support for lock or not. This setting is +automatically set to ``true`` when one of the child settings is configured. + +.. _reference-lock-resources: + +resources +......... + +**type**: ``array`` + +A list of lock stores to be created by the framework extension. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + # these are all the supported lock stores + lock: ~ + lock: 'flock' + lock: 'semaphore' + lock: 'memcached://m1.docker' + lock: ['memcached://m1.docker', 'memcached://m2.docker'] + lock: 'redis://r1.docker' + lock: ['redis://r1.docker', 'redis://r2.docker'] + lock: '%env(MEMCACHED_OR_REDIS_URL)%' + + # named locks + lock: + invoice: ['redis://r1.docker', 'redis://r2.docker'] + report: 'semaphore' + + .. code-block:: xml + + + + + + + + + flock + + semaphore + + memcached://m1.docker + + memcached://m1.docker + memcached://m2.docker + + redis://r1.docker + + redis://r1.docker + redis://r2.docker + + %env(REDIS_URL)% + + + redis://r1.docker + redis://r2.docker + semaphore + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', [ + // these are all the supported lock stores + 'lock' => null, + 'lock' => 'flock', + 'lock' => 'semaphore', + 'lock' => 'memcached://m1.docker', + 'lock' => ['memcached://m1.docker', 'memcached://m2.docker'], + 'lock' => 'redis://r1.docker', + 'lock' => ['redis://r1.docker', 'redis://r2.docker'], + 'lock' => '%env(MEMCACHED_OR_REDIS_URL)%', + + // named locks + 'lock' => [ + 'invoice' => ['redis://r1.docker', 'redis://r2.docker'], + 'report' => 'semaphore', + ], + ]); + +.. _reference-lock-resources-name: + +name +"""" + +**type**: ``prototype`` + +Name of the lock you want to create. + +.. tip:: + + If you want to use the `RetryTillSaveStore` for :ref:`non-blocking locks `, + you can do it by :doc:`decorating the store ` service: + + .. code-block:: yaml + + lock.invoice.retry_till_save.store: + class: Symfony\Component\Lock\Store\RetryTillSaveStore + decorates: lock.invoice.store + arguments: ['@lock.invoice.retry.till.save.store.inner', 100, 50] + workflows ~~~~~~~~~ From ce4c2d98f168adbbc39601e77095acb103875cdb Mon Sep 17 00:00:00 2001 From: Patrick Reimers Date: Tue, 28 May 2019 19:45:37 +0200 Subject: [PATCH 0005/8077] Add note about extra configuration for mod_xsendfile --- components/http_foundation.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index c959f4e8a65..14d73c310a4 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -523,6 +523,26 @@ if it should:: BinaryFileResponse::trustXSendfileTypeHeader(); +.. note:: + + The ``BinaryFileResponse`` will only handle ``X-Sendfile`` if the particular header is present. + For Apache, this is not the default case. + + To add the header use the ``mod_headers`` Apache module and add the following to the Apache configuration. + + .. code-block:: apache + + + # This is already present somewhere... + XSendFile on + XSendFilePath ...some path... + + # This needs to be added: + + RequestHeader set X-Sendfile-Type X-Sendfile + + + With the ``BinaryFileResponse``, you can still set the ``Content-Type`` of the sent file, or change its ``Content-Disposition``:: From 75ad0337b2b524d0b8590a6c223073d7591222c4 Mon Sep 17 00:00:00 2001 From: Joe Bennett Date: Thu, 6 Jun 2019 19:34:40 +1000 Subject: [PATCH 0006/8077] #27345 Removed Lock MongoDbStore, it will be added in symfony 4.4 --- components/lock.rst | 82 ++------------------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 92d917b2fc3..ed28f9e67b0 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -171,7 +171,6 @@ Store Scope Blocking Expiring ============================================ ====== ======== ======== :ref:`FlockStore ` local yes no :ref:`MemcachedStore ` remote no yes -:ref:`MongoDbStore ` remote no yes :ref:`PdoStore ` remote no yes :ref:`RedisStore ` remote no yes :ref:`SemaphoreStore ` local yes no @@ -218,39 +217,6 @@ support blocking, and expects a TTL to avoid stalled locks:: Memcached does not support TTL lower than 1 second. -.. _lock-store-mongodb: - -MongoDbStore -~~~~~~~~~~~~ - -.. versionadded:: 4.3 - - The ``MongoDbStore`` was introduced in Symfony 4.3. - -The MongoDbStore saves locks on a MongoDB server, it requires a -``\MongoDB\Client`` connection from `mongodb/mongodb`_. This store does not -support blocking and expects a TTL to avoid stalled locks:: - - use Symfony\Component\Lock\Store\MongoDbStore; - - $mongoClient = new \MongoDB\Client('mongo://localhost/'); - - $options = [ - 'database' => 'my-app', - ]; - - $store = new MongoDbStore($mongoClient, $options); - -The ``MongoDbStore`` takes the following ``$options``: - -============ ========= ======================================================================== -Option Default Description -============ ========= ======================================================================== -database The name of the database [Mandatory] -collection ``lock`` The name of the collection -gcProbablity ``0.001`` Should a TTL Index be created expressed as a probability from 0.0 to 1.0 -============ ========= ======================================================================== - .. _lock-store-pdo: PdoStore @@ -387,7 +353,7 @@ Remote Stores ~~~~~~~~~~~~~ Remote stores (:ref:`MemcachedStore `, -:ref:`MongoDbStore `, :ref:`PdoStore `, +:ref:`PdoStore `, :ref:`RedisStore ` and :ref:`ZookeeperStore `) use a unique token to recognize the true owner of the lock. This token is stored in the @@ -412,7 +378,7 @@ Expiring Stores ~~~~~~~~~~~~~~~ Expiring stores (:ref:`MemcachedStore `, -:ref:`MongoDbStore `, :ref:`PdoStore ` and +:ref:`PdoStore ` and :ref:`RedisStore `) guarantee that the lock is acquired only for the defined duration of time. If the task takes longer to be accomplished, then the lock can be released by the @@ -530,46 +496,6 @@ method uses the Memcached's ``flush()`` method which purges and removes everythi The method ``flush()`` must not be called, or locks should be stored in a dedicated Memcached service away from Cache. -MongoDbStore -~~~~~~~~~~~~ - -.. caution:: - - The locked resource name is indexed in the ``_id`` field of the lock - collection. Beware that in MongoDB an indexed field's value can be - `a maximum of 1024 bytes in length`_ inclusive of structural overhead. - -A TTL index MUST BE used on MongoDB 2.2+ to automatically clean up expired locks. -Such an index can be created manually: - -.. code-block:: javascript - - db.lock.ensureIndex( - { "expires_at": 1 }, - { "expireAfterSeconds": 0 } - ) - -Alternatively, the method ``MongoDbStore::createTtlIndex(int $expireAfterSeconds = 0)`` -can be called once to create the TTL index during database setup. Read more -about `Expire Data from Collections by Setting TTL`_ in MongoDB. - -.. tip:: - - ``MongoDbStore`` will attempt to automatically create a TTL index on MongoDB - 2.2+. It's recommended to set constructor option ``gcProbablity = 0.0`` to - disable this behavior if you have manually dealt with TTL index creation. - -.. caution:: - - This store relies on all PHP application and database nodes to have - synchronized clocks for lock expiry to occur at the correct time. To ensure - locks don't expire prematurely; the lock TTL should be set with enough extra - time in ``expireAfterSeconds`` to account for any clock drift between nodes. - -``writeConcern``, ``readConcern`` and ``readPreference`` are not specified by -MongoDbStore meaning the collection's settings will take effect. Read more -about `Replica Set Read and Write Semantics`_ in MongoDB. - PdoStore ~~~~~~~~~~ @@ -690,13 +616,9 @@ are still running. .. _`ACID`: https://en.wikipedia.org/wiki/ACID .. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science) -.. _`mongodb/mongodb`: https://packagist.org/packages/mongodb/mongodb .. _Packagist: https://packagist.org/packages/symfony/lock .. _`PHP semaphore functions`: http://php.net/manual/en/book.sem.php .. _`PDO`: https://php.net/pdo .. _`Doctrine DBAL Connection`: https://github.com/doctrine/dbal/blob/master/lib/Doctrine/DBAL/Connection.php .. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name .. _`ZooKeeper`: https://zookeeper.apache.org/ -.. _`a maximum of 1024 bytes in length`: https://docs.mongodb.com/manual/reference/limits/#Index-Key-Limit -.. _`Expire Data from Collections by Setting TTL`: https://docs.mongodb.com/manual/tutorial/expire-data/ -.. _`Replica Set Read and Write Semantics`: https://docs.mongodb.com/manual/applications/replication/ From ff1e1cf3e8489880b80591814c8721534039cd31 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Fri, 7 Jun 2019 08:58:40 +0200 Subject: [PATCH 0007/8077] fix validation files paths --- components/validator/resources.rst | 4 ++-- forms.rst | 2 +- reference/constraints/IsTrue.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/validator/resources.rst b/components/validator/resources.rst index 1a0df6fbe75..1009b099780 100644 --- a/components/validator/resources.rst +++ b/components/validator/resources.rst @@ -71,7 +71,7 @@ configure the locations of these files:: use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder() - ->addYamlMapping('config/validation.yaml') + ->addYamlMapping('validator/validation.yaml') ->getValidator(); .. note:: @@ -136,7 +136,7 @@ multiple mappings:: $validator = Validation::createValidatorBuilder() ->enableAnnotationMapping() ->addMethodMapping('loadValidatorMetadata') - ->addXmlMapping('config/validation.xml') + ->addXmlMapping('validator/validation.xml') ->getValidator(); Caching diff --git a/forms.rst b/forms.rst index 3ca4fac0f51..4fccc340c43 100644 --- a/forms.rst +++ b/forms.rst @@ -347,7 +347,7 @@ object. .. code-block:: xml - + + Date: Tue, 11 Jun 2019 22:33:00 -0300 Subject: [PATCH 0008/8077] [Symfony Server] Tweaks --- setup/symfony_server.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index 02325786f99..b84709b01d4 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -14,8 +14,8 @@ PHP application and even with HTML/SPA (single page applications). Installation ------------ -The Symfony server is distributed as a free installable binary without any -dependency and support for Linux, macOS and Windows. Go to `symfony.com/download`_ +The Symfony server is distributed as a free installable binary, without any +dependency, and has support for Linux, macOS and Windows. Go to `symfony.com/download`_ and follow the instructions for your operating system. .. note:: @@ -104,7 +104,7 @@ root directory: directory to set the same PHP version for a group of projects under that directory. -Run command if you don't remember all the PHP versions installed on your +Run the command below if you don't remember all the PHP versions installed on your computer: .. code-block:: terminal From 108fed6ce6e1f4b750e01880f5ccbc8d76968f50 Mon Sep 17 00:00:00 2001 From: CrayD Date: Wed, 12 Jun 2019 10:08:56 +0300 Subject: [PATCH 0009/8077] Fix typo in word invitation --- translation/message_format.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translation/message_format.rst b/translation/message_format.rst index a0761ec75f6..341da90e039 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -132,13 +132,13 @@ the function name is ``select`` and its statement contains the "cases" of this select. This function is applied over the ``organizer_gender`` variable:: // prints "Ryan has invited you for his party!" - echo $translator->trans('invition_title', [ + echo $translator->trans('invitation_title', [ 'organizer_name' => 'Ryan', 'organizer_gender' => 'male', ]); // prints "John & Jane have invited you for their party!" - echo $translator->trans('invition_title', [ + echo $translator->trans('invitation_title', [ 'organizer_name' => 'John & Jane', 'organizer_gender' => 'not_applicable', ]); From dbb2450921bb8c6d7c43e911c0669d0a9f2d46df Mon Sep 17 00:00:00 2001 From: Matthieu Calie Date: Wed, 12 Jun 2019 14:42:23 +0200 Subject: [PATCH 0010/8077] [Messenger] Multiple buses: typo Fix autowireable eventbus typo --- messenger/multiple_buses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 30e4659a389..9742fba1a52 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -103,7 +103,7 @@ This will create three new services: * ``messenger.bus.queries``: autowireable with the ``MessageBusInterface $messengerBusQueries``; -* ``messenger.bus.queries``: autowireable with the ``MessageBusInterface $messengerBusEvents``. +* ``messenger.bus.events``: autowireable with the ``MessageBusInterface $messengerBusEvents``. Type-hints and Auto-wiring -------------------------- From c76afd38e1891e445eb94e3d64e5402970ff5f1d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 12 Jun 2019 10:44:44 -0400 Subject: [PATCH 0011/8077] Redis 4.3 is required --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 7221e4ad2e6..3f8d5412313 100644 --- a/messenger.rst +++ b/messenger.rst @@ -877,7 +877,7 @@ The Redis transport uses `streams`_ to queue messages. # .env MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages -To use the Redis transport, you will need the Redis PHP extension (^4.2) and +To use the Redis transport, you will need the Redis PHP extension (^4.3) and a running Redis server (^5.0). .. caution:: From 27bd3bb9e295a20c49d82b18d9152a8d31dbb5f0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 6 Jun 2019 17:47:11 +0200 Subject: [PATCH 0012/8077] Revamped the main configuration file --- _build/redirection_map | 4 + best_practices/configuration.rst | 7 +- bundles.rst | 2 +- configuration.rst | 609 +++++++++++++----- configuration/configuration_organization.rst | 182 ------ ...t_variables.rst => env_var_processors.rst} | 180 +----- configuration/environments.rst | 364 ----------- .../front_controllers_and_kernel.rst | 144 ++++- configuration/multiple_kernels.rst | 2 +- configuration/override_dir_structure.rst | 68 +- controller/error_pages.rst | 2 +- doctrine/pdo_session_storage.rst | 2 +- reference/configuration/kernel.rst | 4 +- reference/configuration/swiftmailer.rst | 2 +- service_container/parameters.rst | 2 +- setup/upgrade_major.rst | 2 +- 16 files changed, 646 insertions(+), 930 deletions(-) delete mode 100644 configuration/configuration_organization.rst rename configuration/{environment_variables.rst => env_var_processors.rst} (71%) delete mode 100644 configuration/environments.rst diff --git a/_build/redirection_map b/_build/redirection_map index 7e8c54bd737..b3344cdca95 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -417,3 +417,7 @@ /workflow/state-machines /workflow/introduction /workflow/usage /workflow /introduction/from_flat_php_to_symfony2 /introduction/from_flat_php_to_symfony +/configuration/environment_variables /configuration/env_var_processors +/configuration/configuration_organization /configuration +/configuration/environments /configuration +/configuration/configuration_organization /configuration diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst index 604c28974f5..80d4273c6a1 100644 --- a/best_practices/configuration.rst +++ b/best_practices/configuration.rst @@ -16,9 +16,8 @@ application behavior. .. best-practice:: Define the infrastructure-related configuration options as - :doc:`environment variables `. During - development, use the ``.env`` and ``.env.local`` files at the root of your - project to set these. + :ref:`environment variables `. During development, use the + ``.env`` and ``.env.local`` files at the root of your project to set these. By default, Symfony adds these types of options to the ``.env`` file when installing new dependencies in the app: @@ -84,7 +83,7 @@ layer of configuration that's not needed because you don't need or want these configuration values to change on each server. The configuration options defined in the ``services.yaml`` may vary from one -:doc:`environment ` to another. That's why Symfony +:ref:`environment ` to another. That's why Symfony supports defining ``config/services_dev.yaml`` and ``config/services_prod.yaml`` files so that you can override specific values for each environment. diff --git a/bundles.rst b/bundles.rst index 2f0fb608c04..a16c2870ab6 100644 --- a/bundles.rst +++ b/bundles.rst @@ -18,7 +18,7 @@ SecurityBundle, DebugBundle, etc.) They are also used to add new features in your application via `third-party bundles`_. Bundles used in your applications must be enabled per -:doc:`environment ` in the ``config/bundles.php`` +:ref:`environment ` in the ``config/bundles.php`` file:: // config/bundles.php diff --git a/configuration.rst b/configuration.rst index a875db604c1..a695b1012e1 100644 --- a/configuration.rst +++ b/configuration.rst @@ -1,128 +1,147 @@ .. index:: single: Configuration -Configuring Symfony (and Environments) -====================================== +Configuring Symfony +=================== -Symfony applications can install third-party packages (bundles, libraries, etc.) -to bring in new features (:doc:`services `) to your project. -Each package can be customized via configuration files that live - by default - -in the ``config/`` directory. +Symfony applications are configured with the files stored in the ``config/`` +directory, which has this default structure: -Configuration: config/packages/ -------------------------------- +.. code-block:: text -The configuration for each package can be found in ``config/packages/``. For -instance, the framework bundle is configured in ``config/packages/framework.yaml``: + your-project/ + ├─ config/ + │ ├─ packages/ + │ ├─ bundles.php + │ ├─ routes.yaml + │ └─ services.yaml + ├─ ... -.. configuration-block:: +The ``routes.yaml`` file defines the :doc:`routing configuration `; +the ``services.yaml`` file configures the services of the +:doc:`service container `; the ``bundles.php`` file enables/ +disables packages in your application and is managed automatically by +:doc:`Symfony Flex `. - .. code-block:: yaml +You'll be working most in the ``config/packages/`` directory. This directory +stores the configuration of every package installed in your application. +Packages (also called "bundles" in Symfony and "plugins/modules" in other +projects) add ready-to-use features to your projects. - # config/packages/framework.yaml - framework: - secret: '%env(APP_SECRET)%' - #default_locale: en - #csrf_protection: true - #http_method_override: true +Most packages add a configuration file in ``config/packages/``. For example, +this is the default file for the "API Platform" package: - # Enables session support. Note that the session will ONLY be started if you read or write from it. - # Remove or comment this section to explicitly disable session support. - session: - handler_id: ~ +.. code-block:: yaml - #esi: true - #fragments: true - php_errors: - log: true + # config/packages/api_platform.yaml + api_platform: + mapping: + paths: ['%kernel.project_dir%/src/Entity'] - .. code-block:: xml +Splitting the configuration into lots of small files is intimidating for some +Symfony newcomers. However, you'll get used to them quickly and you rarely need +to change these files after package installation - - - - - - - - - - - - - - +.. tip:: - .. code-block:: php + To learn about all the available configuration options, check out the + :doc:`Symfony Configuration Reference ` or run the + ``config:dump-reference`` command. - // config/packages/framework.php - $container->loadFromExtension('framework', [ - 'secret' => '%env(APP_SECRET)%', - //'default_locale' => 'en', - //'csrf_protection' => true, - //'http_method_override' => true, - - // Enables session support. Note that the session will ONLY be started if you read or write from it. - // Remove or comment this section to explicitly disable session support. - 'session' => [ - 'handler_id' => null, - ], - //'esi' => true, - //'fragments' => true, - 'php_errors' => [ - 'log' => true, - ], - ]); +Configuration Formats +--------------------- -The top-level key (here ``framework``) references configuration for a specific -bundle (:doc:`FrameworkBundle ` in this case). +Unlike other frameworks, Symfony doesn't impose you a specific format to +configure your applications. Symfony lets you choose between YAML, XML and PHP +and throughout the Symfony documentation, all configuration examples will be +shown in these three formats. -.. sidebar:: Configuration Formats +There isn't any practical difference between formats. In fact, Symfony +transforms and caches all of them into PHP before running the application, so +there's not even any performance difference between them. - Throughout the documentation, all configuration examples will be shown in - three formats (YAML, XML and PHP). YAML is used by default, but you can - choose whatever you like best. There is no performance difference: +YAML is used by default when installing packages because it's concise and very +readable. These are the main advantages and disadvantages of each format: - * :doc:`/components/yaml/yaml_format`: Simple, clean and readable; - * *XML*: More powerful than YAML at times & supports IDE autocompletion; - * *PHP*: Very powerful but less readable than standard configuration formats. +* :doc:`YAML `: simple, clean and readable, but + requires using a dedicated parser; +* *XML*: supports IDE autocompletion/validation and is parsed natively by PHP, + but sometimes it generates too verbose configuration; +* *PHP*: very powerful and it allows to create dynamic configuration, but the + resulting configuration is less readable than the other formats. -Configuration Reference & Dumping ---------------------------------- +Importing Configuration Files +----------------------------- -There are *two* ways to know *what* keys you can configure: +Symfony loads configuration files using the :doc:`Config component +`, which provides advanced features such as importing +configuration files, even if they use a different format: -#. Use the :doc:`Reference Section `; -#. Use the ``config:dump-reference`` command. +.. configuration-block:: -For example, if you want to configure something related to the framework bundle, -you can see an example dump of all available configuration options by running: + .. code-block:: yaml -.. code-block:: terminal + # config/services.yaml + imports: + - { resource: 'legacy_config.php' } + # ignore_errors silently discards errors if the loaded file doesn't exist + - { resource: 'my_config_file.xml', ignore_errors: true } + # glob expressions are also supported to load multiple files + - { resource: '/etc/myapp/*.yaml' } - $ php bin/console config:dump-reference framework + # ... -.. index:: - single: Environments; Introduction + .. code-block:: xml -.. _page-creation-environments: -.. _page-creation-prod-cache-clear: + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + $loader->import('legacy_config.xml'); + // the third optional argument of import() is 'ignore_errors', which + // silently discards errors if the loaded file doesn't exist + $loader->import('my_config_file.yaml', null, true); + // glob expressions are also supported to load multiple files + $loader->import('/etc/myapp/*.yaml'); + + // ... + +.. tip:: + + If you use any other configuration format, you have to define your own loader + class extending it from :class:`Symfony\\Component\\DependencyInjection\\Loader\\FileLoader`. + In addition, you can define your own services to load configurations from + databases or web services. .. _config-parameter-intro: +.. _config-parameters-yml: -The parameters Key: Parameters (Variables) ------------------------------------------- +Configuration Parameters +------------------------ -The configuration has some special top-level keys. One of them is called -``parameters``: it's used to define *variables* that can be referenced in *any* -other configuration file. For example, when you install the *translation* -package, a ``locale`` parameter is added to ``config/services.yaml``: +Sometimes the same configuration value is used in several configuration files. +Instead of repeating it, you can define it as a "parameter", which is like a +reusable configuration value. By convention, parameters are defined under the +``parameters`` key in the ``config/services.yaml`` file: .. configuration-block:: @@ -130,7 +149,10 @@ package, a ``locale`` parameter is added to ``config/services.yaml``: # config/services.yaml parameters: - locale: en + # the parameter name is an arbitrary string (the 'app.' prefix is recommended + # to better differentiate your parameters from Symfony parameters). + # the parameter value can be any valid scalar (numbers, strings, booleans, arrays) + app.admin_email: 'something@example.com' # ... @@ -147,7 +169,10 @@ package, a ``locale`` parameter is added to ``config/services.yaml``: https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - en + + something@example.com @@ -156,26 +181,30 @@ package, a ``locale`` parameter is added to ``config/services.yaml``: .. code-block:: php // config/services.php - $container->setParameter('locale', 'en'); + // the parameter name is an arbitrary string (the 'app.' prefix is recommended + // to better differentiate your parameters from Symfony parameters). + // the parameter value can be any valid scalar (numbers, strings, booleans, arrays) + $container->setParameter('app.admin_email', 'something@example.com'); // ... -This parameter is then referenced in the framework config in -``config/packages/translation.yaml``: +Once defined, you can reference this parameter value from any other +configuration file using a special syntax: wrap the parameter name in two ``%`` +(e.g. ``%app.admin_email%``): .. configuration-block:: .. code-block:: yaml - # config/packages/translation.yaml - framework: + # config/packages/some_package.yaml + some_package: # any string surrounded by two % is replaced by that parameter value - default_locale: '%locale%' + email_address: '%app.admin_email%' # ... .. code-block:: xml - + - + - + .. code-block:: php - // config/packages/translation.php - $container->loadFromExtension('framework', [ + // config/packages/some_package.php + $container->loadFromExtension('some_package', [ // any string surrounded by two % is replaced by that parameter value - 'default_locale' => '%locale%', + 'email_address' => '%app.admin_email%', // ... ]); -You can define whatever parameter names you want under the ``parameters`` key of -any configuration file. To reference a parameter, surround its name with two -percent signs - e.g. ``%locale%``. +.. include:: /components/dependency_injection/_imports-parameters-note.rst.inc + +Configuration parameters are very common in Symfony applications. Some packages +even define their own parameters (e.g. when installing the translation package, +a new ``locale`` parameter is added to the ``config/services.yaml`` file). + +.. seealso:: + + Read the :ref:`service parameters ` article to + learn about how to use these configuration parameters in services and controllers. + +.. index:: + single: Environments; Introduction + +.. _page-creation-environments: +.. _page-creation-prod-cache-clear: +.. _configuration-environments: + +Configuration Environments +-------------------------- + +You have just one application, but whether you realize it or not, you need it +to behave differently at different times: + +* While **developing**, you want to log everything and expose nice debugging tools; +* After deploying to **production**, you want that same application to be + optimized for speed and only log errors. + +The files stored in ``config/packages/`` are used by Symfony to configure the +:doc:`application services `. In other words, you can change +the application behavior by changing which configuration files are loaded. +That's the idea of Symfony's **configuration environments**. + +A typical Symfony application begins with three environments: ``dev`` (for local +development), ``prod`` (for production servers) and ``test`` (for +:doc:`automated tests `). When running the application, Symfony loads +the configuration files in this order (the last files can override the values +set in the previous ones): + +#. ``config/packages/*.yaml`` (and ``.xml`` and ``*.php`` files too); +#. ``config/packages//*.yaml`` (and ``.xml`` and ``*.php`` files too); +#. ``config/packages/services.yaml`` (and ``services.xml`` and ``services.php`` files too); + +Take the ``framework`` package, installed by default, as an example: + +* First, ``config/packages/framework.yaml`` is loaded in all environments and + it configures the framework with some options; +* In the **prod** environment, nothing extra will be set as there is no + ``config/packages/prod/framework.yaml`` file; +* In the **dev** environment, there is no file either ( + ``config/packages/dev/framework.yaml`` does not exist). +* In the **test** environment, the ``config/packages/test/framework.yaml`` file + is loaded to override some of the settings previously configured in + ``config/packages/framework.yaml``. + +In reality, each environment differs only somewhat from others. This means that +all environments share a large base of common configurations, which is put in +files directly in the ``config/packages/`` directory. + +.. seealso:: + + See the ``configureContainer()`` method of + :doc:`the Kernel class ` to + learn everything about the loading order of configuration files. + +.. _selecting-the-active-environment: + +Selecting the Active Environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symfony applications come with a file called ``.env`` located at the project +root directory. This file is used to define the value of environment variables +and it's explained in detail :ref:`later in this article `. + +Open the ``.env`` file and edit the value of the ``APP_ENV`` variable to change +the environment in which the application runs. For example, to run the +application in production: + +.. code-block:: bash + + # .env + APP_ENV=prod + +This value is used both for the web and for the console commands. However, you +can override it for commands by setting the ``APP_ENV`` value before running them: + +.. code-block:: terminal + + # Use the environment defined in the .env file + $ php bin/console command_name + + # Ignore the .env file and run this command in production + $ APP_ENV=prod php bin/console command_name + +.. deprecated:: 4.2 + + In previous Symfony versions you could configure the environment with the + ``--env`` command option, which was deprecated in Symfony 4.2. + +Creating a New Environment +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default three environments provided by Symfony are enough for most projects, +but you can define your own environments too. For example, this is how you can +define a ``staging`` environment where the client can test the project before +going to production: + +#. Create a configuration directory with the same name as the environment (in + this case, ``config/packages/staging/``); +#. Add the needed configuration files in ``config/packages/staging/`` to + define the behavior of the new environment. Symfony loads first the files in + ``config/packages/*.yaml``, so you must only configure the differences with + those files; +#. Select the ``staging`` environment using the ``APP_ENV`` env var as explained + in the previous section. + +.. tip:: + + It's common for environments to be similar between each other, so you can + use `symbolic links`_ between ``config/packages//`` + directories to reuse the same configuration. + +.. _config-env-vars: + +Configuration Based on Environment Variables +-------------------------------------------- + +Using `environment variables`_ (or "env vars" for short) is a common practice to +configure options that depend on where the application is run (e.g. the database +credentials are usually different in production and in your local machine). + +Instead of defining those as regular options, you can define them as environment +variables and reference them in the configuration files using the special syntax +``%env(ENV_VAR_NAME)%``. The values of these options are resolved at runtime +(only once per request, to not impact performance). + +This example shows how to configure the database connection using an env var: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/doctrine.yaml + doctrine: + dbal: + # by convention the env var names are always uppercase + url: '%env(DATABASE_URL)%' + # ... + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // config/packages/doctrine.php + $container->loadFromExtension('doctrine', [ + 'dbal' => [ + // by convention the env var names are always uppercase + 'url' => '%env(DATABASE_URL)%', + ] + ]); + +The next step is to define the value of those env vars in your shell, your web +server, etc. This is explained in the following sections, but to protect your +application from undefined env vars, you can give them a default value using the +``parameters`` key: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + parameters: + env(DATABASE_URL): 'sqlite:///%kernel.project_dir%/var/data.db' + + .. code-block:: xml + + + + + + + sqlite:///%kernel.project_dir%/var/data.db + + + + .. code-block:: php + + // config/services.php + $container->setParameter('env(DATABASE_URL)', 'sqlite:///%kernel.project_dir%/var/data.db'); .. seealso:: - You can also set parameters dynamically, like from environment variables. - See :doc:`/configuration/environment_variables`. + The values of env vars can only be strings, but Symfony includes some + :doc:`env var processors ` to transform + their contents (e.g. to turn a string value into an integer). -For more information about parameters - including how to reference them from inside -a controller - see :ref:`service-container-parameters`. +In order to define the actual values of env vars, Symfony proposes different +solutions depending if the application is running in production or in your local +development machine. +.. _configuration-env-var-in-dev: .. _config-dot-env: -.. _config-parameters-yml: -The .env File & Environment Variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configuring Environment Variables in Development +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There is also a ``.env`` file which is loaded and its contents become environment -variables. This is useful during development, or if setting environment variables -is difficult for your deployment. +Instead of defining env vars in your shell or your web server, Symfony proposes +a convenient way of defining them in your local machine based on a file called +``.env`` (with a leading dot) located at the root of your project. -When you install packages, more environment variables are added to this file. But -you can also add your own. +The ``.env`` file is read and parsed on every request and its env vars are added +to the ``$_ENV`` PHP variable. The existing env vars are never overwritten by +the values defined in ``.env``, so you can combine both. -Environment variables can be referenced in any other configuration files by using -a special syntax. For example, if you install the ``doctrine`` package, then you -will have an environment variable called ``DATABASE_URL`` in your ``.env`` file. -This is referenced inside ``config/packages/doctrine.yaml``: +This is for example the content of the ``.env`` file to define the value of the +``DATABASE_URL`` env var shown earlier in this article: -.. code-block:: yaml +.. code-block:: bash + + # .env + DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" + +In addition to your own env vars, this ``.env`` file also contains the env vars +defined by the third-party packages installed in your application (they are +added automatically by :doc:`Symfony Flex ` when installing packages). + +Managing Multiple .env Files +............................ - # config/packages/doctrine.yaml - doctrine: - dbal: - url: '%env(DATABASE_URL)%' +The ``.env`` file defines the default values for all env vars. However, it's +common to override some of those values depending on the environment (e.g. to +use a different database for tests) or depending on the machine (e.g. to use a +different OAuth token in your local machine while developing). - # The `resolve:` prefix replaces container params by their values inside the env variable: - # url: '%env(resolve:DATABASE_URL)%' +That's why you can define multiple ``.env`` files which override the default env +vars. These are the files in priority order (an env var found in one file +overrides the same env var for all the files below): -For more details about environment variables, see :ref:`config-env-vars`. +* ``.env..local`` (e.g. ``.env.test.local``): defines/overrides + env vars only for some environment and only in your local machine; +* ``.env.`` (e.g. ``.env.test``): defines/overrides env vars for + some environment and for all machines; +* ``.env.local``: defines/overrides env vars for all environments but only in + your local machine; +* ``.env``: defines the default value of env vars. + +The ``.env`` and ``.env.`` files should be committed to the shared +repository because they are the same for all developers. However, the +``.env.local`` and ``.env..local`` and files **should not be +committed** because only you will use them. In fact, the ``.gitignore`` file +that comes with Symfony prevents them from being committed. .. caution:: @@ -249,48 +507,65 @@ For more details about environment variables, see :ref:`config-env-vars`. involving a ``.env.dist`` file. For information about upgrading, see: :doc:`configuration/dot-env-changes`. -The ``.env`` file is special, because it defines the values that usually change -on each server. For example, the database credentials on your local development -machine might be different from your workmates. The ``.env`` file should contain -sensible, non-secret *default* values for all of your environment variables and -*should* be commited to your repository. +.. _configuration-env-var-in-prod: + +Configuring Environment Variables in Production +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In production, the ``.env`` files are also parsed and loaded on each request so +you can override the env vars already defined in the server. In order to improve +performance, you can run the ``dump-env`` command (available when using +:doc:`Symfony Flex ` 1.2 or later). + +This command parses all the ``.env`` files once and compiles their contents into +a new PHP-optimized file called ``.env.local.php``. From that moment, Symfony +will load the parsed file instead of parsing the ``.env`` files again: -To override these variables with machine-specific or sensitive values, create a -``.env.local`` file. This file is **not committed to the shared repository** and -is only stored on your machine. In fact, the ``.gitignore`` file that comes with -Symfony prevents it from being committed. +.. code-block:: terminal + + $ composer dump-env prod + +.. tip:: -You can also create a few other ``.env`` files that will be loaded: + Update your deployment tools/workflow to run the ``dump-env`` command after + each deploy to improve the application performance. + +.. caution:: -* ``.env.{environment}``: e.g. ``.env.test`` will be loaded in the ``test`` environment - and committed to your repository. + Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables + or outputting the ``phpinfo()`` contents will display the values of the + environment variables, exposing sensitive information such as the database + credentials. -* ``.env.{environment}.local``: e.g. ``.env.prod.local`` will be loaded in the - ``prod`` environment but will *not* be committed to your repository. + The values of the env vars are also exposed in the web interface of the + :doc:`Symfony profiler `. In practice this shouldn't be a + problem because the web profiler must **never** be enabled in production. -If you decide to set real environment variables on production, the ``.env`` files -*are* still loaded, but your real environment variables will override those values. +Defining Environment Variables in the Web Server +................................................ -Environments & the Other Config Files -------------------------------------- +Using the ``.env`` files explained earlier is the recommended way of using env +vars in Symfony applications. However, you can also define env vars in the web +server configuration if you prefer that: -You have just *one* app, but whether you realize it or not, you need it to -behave *differently* at different times: +.. configuration-block:: + + .. code-block:: apache + + + # ... -* While **developing**, you want your app to log everything and expose nice - debugging tools; + SetEnv DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name" + -* After deploying to **production**, you want that *same* app to be optimized - for speed and only log errors. + .. code-block:: nginx -How can you make *one* application behave in two different ways? With -*environments*. + fastcgi_param DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name"; -You've probably already been using the ``dev`` environment without even knowing -it. After you deploy, you'll use the ``prod`` environment. +.. tip:: -To learn more about *how* to execute and control each environment, see -:doc:`/configuration/environments`. + SymfonyCloud, the cloud service optimized for Symfony applications, defines + some `utilities to manage env vars`_ in production. Keep Going! ----------- @@ -316,4 +591,6 @@ Learn more configuration/* -.. _`Incenteev Parameter Handler`: https://github.com/Incenteev/ParameterHandler +.. _`environment variables`: https://en.wikipedia.org/wiki/Environment_variable +.. _`symbolic links`: https://en.wikipedia.org/wiki/Symbolic_link +.. _`utilities to manage env vars`: https://symfony.com/doc/master/cloud/cookbooks/env.html diff --git a/configuration/configuration_organization.rst b/configuration/configuration_organization.rst deleted file mode 100644 index 5fe86fe290d..00000000000 --- a/configuration/configuration_organization.rst +++ /dev/null @@ -1,182 +0,0 @@ -.. index:: - single: Configuration - -How to Organize Configuration Files -=================================== - -The Symfony skeleton defines three :doc:`execution environments ` -called ``dev``, ``prod`` and ``test``. An environment represents a way -to execute the same codebase with different configurations. - -In order to select the configuration file to load for each environment, Symfony -executes the ``configureContainer()`` method of the ``Kernel`` class:: - - // src/Kernel.php - use Symfony\Component\Config\Loader\LoaderInterface; - use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\HttpKernel\Kernel as BaseKernel; - - class Kernel extends BaseKernel - { - const CONFIG_EXTS = '.{php,xml,yaml,yml}'; - - // ... - - public function configureContainer(ContainerBuilder $container, LoaderInterface $loader) - { - $confDir = $this->getProjectDir().'/config'; - $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); - if (is_dir($confDir.'/packages/'.$this->environment)) { - $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); - } - $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); - $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); - } - } - -For the ``dev`` environment, Symfony loads the following config files and -directories and in this order: - -#. ``config/packages/*`` -#. ``config/packages/dev/*`` -#. ``config/services.yaml`` -#. ``config/services_dev.yaml`` - -Therefore, the configuration files of the default Symfony applications follow -this structure: - -.. code-block:: text - - your-project/ - ├─ config/ - │ ├─ packages/ - │ │ ├─ dev/ - │ │ │ ├─ framework.yaml - │ │ │ └─ ... - │ │ ├─ prod/ - │ │ │ └─ ... - │ │ ├─ test/ - │ │ │ └─ ... - │ │ ├─ framework.yaml - │ │ └─ ... - │ ├─ services.yaml - │ └─ services_dev.yaml - ├─ ... - -This default structure was chosen for its simplicity — one file per package and -environment. But as any other Symfony feature, you can customize it to better -suit your needs. - -Advanced Techniques -------------------- - -Symfony loads configuration files using the -:doc:`Config component `, which provides some -advanced features. - -Mix and Match Configuration Formats -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Configuration files can import files defined with any other built-in configuration -format (``.yaml``, ``.xml``, ``.php``, ``.ini``): - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - imports: - - { resource: 'my_config_file.xml' } - - { resource: 'legacy.php' } - - # ... - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // config/services.php - $loader->import('my_config_file.yaml'); - $loader->import('legacy.xml'); - - // ... - -If you use any other configuration format, you have to define your own loader -class extending it from :class:`Symfony\\Component\\DependencyInjection\\Loader\\FileLoader`. -When the configuration values are dynamic, you can use the PHP configuration -file to execute your own logic. In addition, you can define your own services -to load configurations from databases or web services. - -.. include:: /components/dependency_injection/_imports-parameters-note.rst.inc - -Global Configuration Files -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some system administrators may prefer to store sensitive parameters in files -outside the project directory. Imagine that the database credentials for your -website are stored in the ``/etc/sites/mysite.com/parameters.yaml`` file. You -can load files from outside the project folder by indicating the full file path -when importing it from any other configuration file: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - imports: - - { resource: '/etc/sites/mysite.com/parameters.yaml', ignore_errors: true } - - # ... - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // config/services.php - $loader->import('/etc/sites/mysite.com/parameters.yaml', null, true); - - // ... - -.. tip:: - - The ``ignore_errors`` option (which is the third optional argument in the - loader's ``import()`` method) silently discards errors when the loaded file - doesn't exist. This is needed in this case because most of the time, local - developers won't have the same files that exist on the production servers. - -As you've seen, there are lots of ways to organize your configuration files. You -can choose one of these or even create your own custom way of organizing the -files. For even more customization, see ":doc:`/configuration/override_dir_structure`". diff --git a/configuration/environment_variables.rst b/configuration/env_var_processors.rst similarity index 71% rename from configuration/environment_variables.rst rename to configuration/env_var_processors.rst index 2fc75dcc3d7..fb54a11ecce 100644 --- a/configuration/environment_variables.rst +++ b/configuration/env_var_processors.rst @@ -1,145 +1,18 @@ .. index:: - single: Environment Variables; env vars - -How to Configure Symfony With Environment Variables -=================================================== - -In :doc:`/configuration`, you learned how to manage your application -configuration. In this article you'll learn how to use environment variables (or -"env vars" for short) to configure some of those options, which is a common -practice to configure sensitive options such as credentials and passwords. - -.. _config-env-vars: - -Referencing Env Vars in Configuration Files -------------------------------------------- - -First, define the value of the env var, using your shell environment or the -``.env`` file at the project root directory. For example, consider the -``DATABASE_URL`` env var defined when installing the ``doctrine`` recipe (by -convention the env var names are always uppercase): - -.. code-block:: bash - - # .env - DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" - -Then, you can reference those env vars in any configuration option enclosing -their names with ``env()``. Their actual values will be resolved at runtime -(once per request), so that dumped containers can be reconfigured dynamically -even after being compiled: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/doctrine.yaml - doctrine: - dbal: - url: '%env(DATABASE_URL)%' - # ... - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // config/packages/doctrine.php - $container->loadFromExtension('doctrine', [ - 'dbal' => [ - 'url' => '%env(DATABASE_URL)%', - ] - ]); - -You can also give the ``env()`` parameters a default value, which will be used -whenever the corresponding environment variable is *not* found: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - parameters: - env(DATABASE_HOST): localhost - - .. code-block:: xml - - - - - - - localhost - - - - .. code-block:: php - - // config/services.php - $container->setParameter('env(DATABASE_HOST)', 'localhost'); - -.. _configuration-env-var-in-prod: - -Configuring Environment Variables in Production ------------------------------------------------ - -During development, you'll use the ``.env`` file to configure your environment -variables. On your production server, it is recommended to configure these at -the web server level. If you're using Apache or Nginx, you can use e.g. one of -the following: - -.. configuration-block:: - - .. code-block:: apache - - - # ... - - SetEnv DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name" - - - .. code-block:: nginx - - fastcgi_param DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name"; - -.. caution:: - - Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables - or outputting the ``phpinfo()`` contents will display the values of the - environment variables, exposing sensitive information such as the database - credentials. - - The values of the env vars are also exposed in the web interface of the - :doc:`Symfony profiler `. In practice this shouldn't be a - problem because the web profiler must **never** be enabled in production. + single: Environment Variable Processors; env vars Environment Variable Processors -------------------------------- +=============================== -The values of environment variables are considered strings by default. -However, your code may expect other data types, like integers or booleans. -Symfony solves this problem with *processors*, which modify the contents of the -given environment variables. The following example uses the integer processor to -turn the value of the ``HTTP_PORT`` env var into an integer: +:ref:`Using env vars to configure Symfony applications ` is a +common practice to hide sensitive configuration (e.g. database credentials) and +to make your applications truly dynamic. + +The main issue of env vars is that their values can only be strings and your +application may need other data types (integer, boolean, etc.). Symfony solves +this problem with "env var processors", which transform the original contents of +the given environment variables. The following example uses the integer +processor to turn the value of the ``HTTP_PORT`` env var into an integer: .. configuration-block:: @@ -176,6 +49,9 @@ turn the value of the ``HTTP_PORT`` env var into an integer: ], ]); +Built-In Environment Variable Processors +---------------------------------------- + Symfony provides the following env var processors: ``env(string:FOO)`` @@ -500,7 +376,7 @@ It is also possible to combine any number of processors: auth: '%env(json:file:resolve:AUTH_FILE)%' Custom Environment Variable Processors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------------- It's also possible to add your own processors for environment variables. First, create a class that implements @@ -530,29 +406,3 @@ To enable the new processor in the app, register it as a service and tag. If you're using the :ref:`default services.yaml configuration `, this is already done for you, thanks to :ref:`autoconfiguration `. - -Constants ---------- - -The container also has support for setting PHP constants as parameters. -See :ref:`component-di-parameters-constants` for more details. - -Miscellaneous Configuration ---------------------------- - -You can mix whatever configuration format you like (YAML, XML and PHP) in -``config/packages/``. Importing a PHP file gives you the flexibility to add -whatever is needed in the container. For instance, you can create a -``drupal.php`` file in which you set a database URL based on Drupal's database -configuration:: - - // config/packages/drupal.php - - // import Drupal's configuration - include_once('/path/to/drupal/sites/default/settings.php'); - - // set a app.database_url parameter - $container->setParameter('app.database_url', $db_url); - -.. _`SetEnv`: http://httpd.apache.org/docs/current/env.html -.. _`fastcgi_param`: http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_param diff --git a/configuration/environments.rst b/configuration/environments.rst deleted file mode 100644 index 0efac1eb899..00000000000 --- a/configuration/environments.rst +++ /dev/null @@ -1,364 +0,0 @@ -.. index:: - single: Environments - -How to Master and Create new Environments -========================================= - -Every application is the combination of code and a set of configuration that -dictates how that code should function. The configuration may define the database -being used, if something should be cached or how verbose logging should be. - -In Symfony, the idea of "environments" is that the same codebase can be run using -multiple different configurations. For example, the ``dev`` environment should use -configuration that makes development comfortable and friendly, while the ``prod`` -environment should use a set of configuration optimized for speed and security. - -.. index:: - single: Environments; Configuration files - -Different Environments, different Configuration Files ------------------------------------------------------ - -A typical Symfony application begins with three environments: ``dev``, -``prod`` and ``test``. As mentioned, each environment represents a way to -execute the same codebase with different configuration. It should be no -surprise then that each environment loads its own individual configuration -files. These different files are organized by environment: - -* for the ``dev`` environment: ``config/packages/dev/`` -* for the ``prod`` environment: ``config/packages/prod/`` -* for the ``test`` environment: ``config/packages/test/`` - -In reality, each environment differs only somewhat from others. This means that -all environments share a large base of common configurations. This configuration -is put in files directly in the ``config/packages/`` directory. - -The location of these files is defined by the application's kernel:: - - // src/Kernel.php - - // ... - class Kernel extends BaseKernel - { - // ... - - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) - { - // ... - $confDir = $this->getProjectDir().'/config'; - - // always load all files in /config/packages/ - $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); - - // then, if available, load the files in the specific environment directory - if (is_dir($confDir.'/packages/'.$this->environment)) { - $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); - } - - // load a special services.(yaml/xml/php) and, if available, services_ENVIRONMENT.(yaml/xml/php) file - $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); - $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); - } - } - -Take the framework package, installed by default, as an example: - -* Loaded in all environments, ``config/packages/framework.yaml`` configures the - framework with some ``secret`` setting; -* In the **prod** environment, nothing extra will be set as there is no - ``config/packages/prod/`` directory; -* The same applies to **dev**, as there is no - ``config/packages/dev/framework.yaml``. There are however other packages (e.g. - ``routing.yaml``) with special dev settings; -* At last, during the **test** environment, the framework's test features are - enabled in ``config/packages/test/framework.yaml``. - -.. index:: - single: Environments; Executing different environments - -Executing an Application in different Environments --------------------------------------------------- - -To execute the application in each environment, change the ``APP_ENV`` -environment variable. During development, this is done in ``.env`` or in ``.env.local``: - -.. code-block:: bash - - # .env or .env.local - APP_ENV=dev - - # or for test: - #APP_ENV=test - -Visit the ``http://localhost:8000/index.php`` page in your web browser to see -your application in the configured environment. - -.. tip:: - - In production, you can use real environment variables via - your :ref:`web server configuration `. - -.. note:: - - The given URLs assume that your web server is configured to use the ``public/`` - directory of the application as its root. Read more in :doc:`Installing Symfony `. - -If you open the file you just visited (``public/index.php``), you'll see that -the environment variable is passed to the kernel:: - - // public/index.php - - // ... - $kernel = new Kernel($_SERVER['APP_ENV'], $_SERVER['APP_DEBUG']); - - // ... - -.. note:: - - The ``test`` environment is used when writing functional tests and is - usually not accessed in the browser directly via a front controller. - -.. index:: - single: Configuration; Debug mode - -.. sidebar:: *Debug* Mode - - Important, but unrelated to the topic of *environments* is the second - argument to the ``Kernel`` constructor. This specifies if the application - should run in "debug mode". Regardless of the environment, a Symfony - application can be run with debug mode set to ``true`` or ``false`` - (respectively ``1`` or ``0`` for the ``APP_DEBUG`` variable defined in - ``.env``). This affects many things in the application, such as displaying - stacktraces on error pages or if cache files are dynamically rebuilt on - each request. Though not a requirement, debug mode is generally set to - ``true`` for the ``dev`` and ``test`` environments and ``false`` for the - ``prod`` environment. - - Internally, the value of the debug mode becomes the ``kernel.debug`` - parameter used inside the :doc:`service container `. - If you look inside the application configuration file, you'll see the - parameter used, for example, to turn Twig's debug mode on: - - .. configuration-block:: - - .. code-block:: yaml - - # config/packages/twig.yaml - twig: - debug: '%kernel.debug%' - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - $container->loadFromExtension('twig', [ - 'debug' => '%kernel.debug%', - // ... - ]); - -Selecting the Environment for Console Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, Symfony commands are executed in whatever environment is defined by -the ``APP_ENV`` environment variable (usually configured in your ``.env`` file). -In previous Symfony versions you could use the ``--env`` (and ``--no-debug``) -command line options to override this value. However, those options were -deprecated in Symfony 4.2. - -Use the ``APP_ENV`` (and ``APP_DEBUG``) environment variables to change the -environment and the debug behavior of the commands: - -.. code-block:: terminal - - # Symfony's default: 'dev' environment and debug enabled - $ php bin/console command_name - - # 'prod' environment (debug is always disabled for 'prod') - $ APP_ENV=prod php bin/console command_name - - # 'test' environment and debug disabled - $ APP_ENV=test APP_DEBUG=0 php bin/console command_name - -.. index:: - single: Environments; Creating a new environment - -Creating a new Environment --------------------------- - -Since an environment is nothing more than a string that corresponds to a set of -configuration, you can also create your own environments for specific purposes. - -Suppose, for example, that before deployment, you need to benchmark your -application. One way to benchmark the application is to use near-production -settings, but with Symfony's ``web_profiler`` enabled. This allows Symfony -to record information about your application while benchmarking. - -The best way to accomplish this is via a new environment called, for example, -``benchmark``. Start by creating a new configuration directory and a -configuration file: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/benchmark/web_profiler.yaml - framework: - profiler: { only_exceptions: false } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // config/packages/benchmark/web_profiler.php - $container->loadFromExtension('framework', [ - 'profiler' => ['only_exceptions' => false], - ]); - -And... you're finished! The application now supports a new environment called -``benchmark``. - -Change the ``APP_ENV`` variable to ``benchmark`` to be able to access the new -environment through your browser: - -.. code-block:: bash - - # .env or .env.local - APP_ENV=benchmark - -.. sidebar:: Importing configuration - - Besides loading files in the Kernel, you can also import files in the - configuration directly. For instance, to make sure the benchmark - environment is identical to the prod environment, you might want to load - all its configuration as well. - - You can achieve this by using a special ``imports`` key: - - .. configuration-block:: - - .. code-block:: yaml - - # config/packages/benchmark/other.yaml - imports: - - { resource: '../prod/' } - - # other resources are possible as well, like importing other - # files or using globs: - #- { resource: '/etc/myapp/some_special_config.xml' } - #- { resource: '/etc/myapp/*.yaml' } - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // config/packages/benchmark/other.php - $loader->import('../prod/'); - - // other resources are possible as well, like importing other - // files or using globs: - //$loader->import('/etc/myapp/some_special_config.yaml'); - //$loader->import('/etc/myapp/*.php'); - -.. index:: - single: Environments; Cache directory - -Environments and the Cache Directory ------------------------------------- - -Symfony takes advantage of caching in many ways: the application configuration, -routing configuration, Twig templates and more are cached to PHP objects -stored in files on the filesystem. - -By default, these cached files are largely stored in the ``var/cache/`` directory. -However, each environment caches its own set of files: - -.. code-block:: text - - your-project/ - ├─ var/ - │ ├─ cache/ - │ │ ├─ dev/ # cache directory for the *dev* environment - │ │ └─ prod/ # cache directory for the *prod* environment - │ ├─ ... - -Sometimes, when debugging, it may be helpful to inspect a cached file to -understand how something is working. When doing so, remember to look in -the directory of the environment you're using (most commonly ``dev/`` while -developing and debugging). While it can vary, the ``var/cache/dev/`` directory -includes the following: - -``appDevDebugProjectContainer.php`` - The cached "service container" that represents the cached application - configuration. - -``appDevUrlGenerator.php`` - The PHP class generated from the routing configuration and used when - generating URLs. - -``appDevUrlMatcher.php`` - The PHP class used for route matching - look here to see the compiled regular - expression logic used to match incoming URLs to different routes. - -``twig/`` - This directory contains all the cached Twig templates. - -.. note:: - - You can change the directory location and name. For more information - read the article :doc:`/configuration/override_dir_structure`. - -Going further -------------- - -Read the article on :doc:`/configuration/environment_variables`. diff --git a/configuration/front_controllers_and_kernel.rst b/configuration/front_controllers_and_kernel.rst index a0127c34494..865a4077157 100644 --- a/configuration/front_controllers_and_kernel.rst +++ b/configuration/front_controllers_and_kernel.rst @@ -5,11 +5,11 @@ Understanding how the Front Controller, Kernel and Environments Work together ============================================================================= -The section :doc:`/configuration/environments` explained the basics on how -Symfony uses environments to run your application with different configuration -settings. This section will explain a bit more in-depth what happens when your -application is bootstrapped. To hook into this process, you need to understand -three parts that work together: +The :ref:`configuration environments ` section +explained the basics on how Symfony uses environments to run your application +with different configuration settings. This section will explain a bit more +in-depth what happens when your application is bootstrapped. To hook into this +process, you need to understand three parts that work together: * `The Front Controller`_ * `The Kernel Class`_ @@ -120,6 +120,82 @@ new kernel. But odds are high that you don't need to change things like this on the fly by having several ``Kernel`` implementations. +.. index:: + single: Configuration; Debug mode + +Debug Mode +~~~~~~~~~~ + +The second argument to the ``Kernel`` constructor specifies if the application +should run in "debug mode". Regardless of the +:ref:`configuration environment `, a Symfony +application can be run with debug mode set to ``true`` or ``false``. + +This affects many things in the application, such as displaying stacktraces on +error pages or if cache files are dynamically rebuilt on each request. Though +not a requirement, debug mode is generally set to ``true`` for the ``dev`` and +``test`` environments and ``false`` for the ``prod`` environment. + +Similar to :ref:`configuring the environment ` +you can also enable/disable the debug mode using :ref:`the .env file `: + +.. code-block:: bash + + # .env + # set it to 1 to enable the debug mode + APP_DEBUG=0 + +This value can be overridden for commands by passing the ``APP_DEBUG`` value +before running them: + +.. code-block:: terminal + + # Use the debug mode defined in the .env file + $ php bin/console command_name + + # Ignore the .env file and enable the debug mode for this command + $ APP_DEBUG=1 php bin/console command_name + +.. deprecated:: 4.2 + + In previous Symfony versions you could configure the debug mode with the + ``--no-debug`` command option, which was deprecated in Symfony 4.2. + +Internally, the value of the debug mode becomes the ``kernel.debug`` +parameter used inside the :doc:`service container `. +If you look inside the application configuration file, you'll see the +parameter used, for example, to turn Twig's debug mode on: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/twig.yaml + twig: + debug: '%kernel.debug%' + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('twig', [ + 'debug' => '%kernel.debug%', + // ... + ]); + The Environments ---------------- @@ -128,9 +204,9 @@ As mentioned above, the ``Kernel`` has to implement another method - This method is responsible for loading the application's configuration from the right *environment*. -Environments have been covered extensively :doc:`in the previous article -`, and you probably remember that the Symfony uses -by default three of them - ``dev``, ``prod`` and ``test``. +:ref:`Configuration environments ` allow to execute +the same code using different configuration. Symfony provides three environments +by default called ``dev``, ``prod`` and ``test``. More technically, these names are nothing more than strings passed from the front controller to the ``Kernel``'s constructor. This name can then be used in @@ -141,5 +217,53 @@ config files found on ``config/packages/*`` and then, the files found on ``config/packages/ENVIRONMENT_NAME/``. You are free to implement this method differently if you need a more sophisticated way of loading your configuration. -.. _front controller: https://en.wikipedia.org/wiki/Front_Controller_pattern -.. _decorate: https://en.wikipedia.org/wiki/Decorator_pattern +.. index:: + single: Environments; Cache directory + +Environments and the Cache Directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symfony takes advantage of caching in many ways: the application configuration, +routing configuration, Twig templates and more are cached to PHP objects +stored in files on the filesystem. + +By default, these cached files are largely stored in the ``var/cache/`` directory. +However, each environment caches its own set of files: + +.. code-block:: text + + your-project/ + ├─ var/ + │ ├─ cache/ + │ │ ├─ dev/ # cache directory for the *dev* environment + │ │ └─ prod/ # cache directory for the *prod* environment + │ ├─ ... + +Sometimes, when debugging, it may be helpful to inspect a cached file to +understand how something is working. When doing so, remember to look in +the directory of the environment you're using (most commonly ``dev/`` while +developing and debugging). While it can vary, the ``var/cache/dev/`` directory +includes the following: + +``appDevDebugProjectContainer.php`` + The cached "service container" that represents the cached application + configuration. + +``appDevUrlGenerator.php`` + The PHP class generated from the routing configuration and used when + generating URLs. + +``appDevUrlMatcher.php`` + The PHP class used for route matching - look here to see the compiled regular + expression logic used to match incoming URLs to different routes. + +``twig/`` + This directory contains all the cached Twig templates. + +.. note:: + + You can change the cache directory location and name. For more information + read the article :doc:`/configuration/override_dir_structure`. + +.. _`front controller`: https://en.wikipedia.org/wiki/Front_Controller_pattern +.. _`decorate`: https://en.wikipedia.org/wiki/Decorator_pattern diff --git a/configuration/multiple_kernels.rst b/configuration/multiple_kernels.rst index 7cad1df7863..235305ce090 100644 --- a/configuration/multiple_kernels.rst +++ b/configuration/multiple_kernels.rst @@ -16,7 +16,7 @@ request to generate the response. This single kernel approach is a convenient default, but Symfony applications can define any number of kernels. Whereas -:doc:`environments ` execute the same application +:ref:`environments ` execute the same application with different configurations, kernels can execute different parts of the same application. diff --git a/configuration/override_dir_structure.rst b/configuration/override_dir_structure.rst index 0d312de3826..fbfa119cc14 100644 --- a/configuration/override_dir_structure.rst +++ b/configuration/override_dir_structure.rst @@ -4,9 +4,8 @@ How to Override Symfony's default Directory Structure ===================================================== -Symfony automatically ships with a default directory structure. You can -override this directory structure to create your own. The default -directory structure is: +Symfony applications have the following default directory structure, but you can +override it to create your own structure: .. code-block:: text @@ -28,10 +27,19 @@ directory structure is: │ └─ ... └─ vendor/ +.. _override-config-dir: + +Override the Configuration Directory +------------------------------------ + +The configuration directory is the only one which cannot be overridden in a +Symfony application. Its location is hardcoded as the ``config/`` directory +at your project root directory. + .. _override-cache-dir: -Override the ``cache`` Directory --------------------------------- +Override the Cache Directory +---------------------------- You can change the default cache directory by overriding the ``getCacheDir()`` method in the ``Kernel`` class of your application:: @@ -51,21 +59,21 @@ method in the ``Kernel`` class of your application:: In this code, ``$this->environment`` is the current environment (i.e. ``dev``). In this case you have changed the location of the cache directory to -``var/{environment}/cache``. +``var/{environment}/cache/``. .. caution:: - You should keep the ``cache`` directory different for each environment, + You should keep the cache directory different for each environment, otherwise some unexpected behavior may happen. Each environment generates its own cached configuration files, and so each needs its own directory to store those cache files. .. _override-logs-dir: -Override the ``logs`` Directory -------------------------------- +Override the Log Directory +-------------------------- -Overriding the ``logs`` directory is the same as overriding the ``cache`` +Overriding the ``var/log/`` directory is the same as overriding the ``var/cache/`` directory. The only difference is that you need to override the ``getLogDir()`` method:: @@ -82,7 +90,7 @@ method:: } } -Here you have changed the location of the directory to ``var/{environment}/log``. +Here you have changed the location of the directory to ``var/{environment}/log/``. .. _override-templates-dir: @@ -180,19 +188,19 @@ configuration option to define your own translations directory (or directories): .. _override-web-dir: .. _override-the-web-directory: -Override the ``public`` Directory ---------------------------------- +Override the Public Directory +----------------------------- -If you need to rename or move your ``public`` directory, the only thing you need -to guarantee is that the path to the ``var`` directory is still correct in your -``index.php`` front controller. If you renamed the directory, you're -fine. But if you moved it in some way, you may need to modify these paths inside -those files:: +If you need to rename or move your ``public/`` directory, the only thing you +need to guarantee is that the path to the ``var/`` directory is still correct in +your ``index.php`` front controller. If you renamed the directory, you're fine. +But if you moved it in some way, you may need to modify these paths inside those +files:: require_once __DIR__.'/../path/to/vendor/autoload.php'; -You also need to change the ``extra.public-dir`` option in the -``composer.json`` file: +You also need to change the ``extra.public-dir`` option in the ``composer.json`` +file: .. code-block:: json @@ -206,17 +214,17 @@ You also need to change the ``extra.public-dir`` option in the .. tip:: - Some shared hosts have a ``public_html`` web directory root. Renaming - your web directory from ``public`` to ``public_html`` is one way to make + Some shared hosts have a ``public_html/`` web directory root. Renaming + your web directory from ``public/`` to ``public_html/`` is one way to make your Symfony project work on your shared host. Another way is to deploy your application to a directory outside of your web root, delete your - ``public_html`` directory, and then replace it with a symbolic link to - the ``public`` dir in your project. + ``public_html/`` directory, and then replace it with a symbolic link to + the ``public/`` dir in your project. -Override the ``vendor`` Directory ---------------------------------- +Override the Vendor Directory +----------------------------- -To override the ``vendor`` directory, you need to define the ``vendor-dir`` +To override the ``vendor/`` directory, you need to define the ``vendor-dir`` option in your ``composer.json`` file like this: .. code-block:: json @@ -230,6 +238,6 @@ option in your ``composer.json`` file like this: .. tip:: - This modification can be of interest if you are working in a virtual environment - and cannot use NFS - for example, if you're running a Symfony application using - Vagrant/VirtualBox in a guest operating system. + This modification can be of interest if you are working in a virtual + environment and cannot use NFS - for example, if you're running a Symfony + application using Vagrant/VirtualBox in a guest operating system. diff --git a/controller/error_pages.rst b/controller/error_pages.rst index 06e1251b00c..989938bcd5b 100644 --- a/controller/error_pages.rst +++ b/controller/error_pages.rst @@ -17,7 +17,7 @@ customize error pages, so run this command to make sure the bundle is installed: $ composer require twig -In the :doc:`development environment `, +In the :ref:`development environment `, Symfony catches all the exceptions and displays a special **exception page** with lots of debug information to help you discover the root problem: diff --git a/doctrine/pdo_session_storage.rst b/doctrine/pdo_session_storage.rst index 0dd065d10d6..81fbbd2fead 100644 --- a/doctrine/pdo_session_storage.rst +++ b/doctrine/pdo_session_storage.rst @@ -69,7 +69,7 @@ To use it, first register a new handler service: .. tip:: Configure the database credentials - :doc:`using environment variables in the config file ` + :ref:`using environment variables in the config file ` to make your application more secure. Next, tell Symfony to use your service as the session handler: diff --git a/reference/configuration/kernel.rst b/reference/configuration/kernel.rst index 2081ba5ffad..08413ef59aa 100644 --- a/reference/configuration/kernel.rst +++ b/reference/configuration/kernel.rst @@ -109,7 +109,7 @@ Cache Directory This returns the absolute path of the cache directory of your Symfony project. It's calculated automatically based on the current -:doc:`environment `. +:ref:`environment `. This value is exposed via the ``kernel.cache_dir`` configuration parameter and the :method:`Symfony\\Component\\HttpKernel\\Kernel::getCacheDir` method. To @@ -123,7 +123,7 @@ Log Directory This returns the absolute path of the log directory of your Symfony project. It's calculated automatically based on the current -:doc:`environment `. +:ref:`environment `. This value is exposed via the ``kernel.log_dir`` configuration parameter and the :method:`Symfony\\Component\\HttpKernel\\Kernel::getLogDir` method. To diff --git a/reference/configuration/swiftmailer.rst b/reference/configuration/swiftmailer.rst index d53935b8d59..977c834e14f 100644 --- a/reference/configuration/swiftmailer.rst +++ b/reference/configuration/swiftmailer.rst @@ -256,7 +256,7 @@ the information will be available in the profiler. The following options can be set via environment variables: ``url``, ``transport``, ``username``, ``password``, ``host``, ``port``, ``timeout``, ``source_ip``, ``local_domain``, ``encryption``, ``auth_mode``. For details, - see: :doc:`/configuration/environment_variables`. + see: :ref:`config-env-vars`. Using Multiple Mailers ---------------------- diff --git a/service_container/parameters.rst b/service_container/parameters.rst index 48dbac2c83c..c1c7d570447 100644 --- a/service_container/parameters.rst +++ b/service_container/parameters.rst @@ -245,7 +245,7 @@ for all parameters that are arrays. Environment Variables and Dynamic Values ---------------------------------------- -See :doc:`/configuration/environment_variables`. +See :ref:`config-env-vars`. .. _component-di-parameters-constants: diff --git a/setup/upgrade_major.rst b/setup/upgrade_major.rst index b8c00234fc8..83e1b41b583 100644 --- a/setup/upgrade_major.rst +++ b/setup/upgrade_major.rst @@ -37,7 +37,7 @@ using these deprecated features in the last version before the major (e.g. To help you with this, deprecation notices are triggered whenever you end up using a deprecated feature. When visiting your application in the -:doc:`dev environment ` +:ref:`dev environment ` in your browser, these notices are shown in the web dev toolbar: .. image:: /_images/install/deprecations-in-profiler.png From 77b0d47adc1c08ff3e6d84d82a209de8301620ff Mon Sep 17 00:00:00 2001 From: Victor Bocharsky Date: Wed, 12 Jun 2019 22:39:19 +0300 Subject: [PATCH 0013/8077] Replace SwiftMailer with the new Mailer Component --- components/messenger.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/messenger.rst b/components/messenger.rst index be06f27f3cf..7958f4ce5e2 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -214,15 +214,17 @@ you can create your own message sender:: namespace App\MessageSender; use App\Message\ImportantAction; + use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\Sender\SenderInterface; + use Symfony\Component\Mime\Email; class ImportantActionToEmailSender implements SenderInterface { private $mailer; private $toEmail; - public function __construct(\Swift_Mailer $mailer, string $toEmail) + public function __construct(MailerInterface $mailer, string $toEmail) { $this->mailer = $mailer; $this->toEmail = $toEmail; @@ -237,12 +239,10 @@ you can create your own message sender:: } $this->mailer->send( - (new \Swift_Message('Important action made')) - ->setTo($this->toEmail) - ->setBody( - '

Important action

Made by '.$message->getUsername().'

', - 'text/html' - ) + (new Email()) + ->to($this->toEmail) + ->subject('Important action made') + ->html('

Important action

Made by '.$message->getUsername().'

') ); return $envelope; From c0f6a6bfa5e5f8647e420f1dce7e0bf0ddcd8bdc Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 13 Jun 2019 09:24:03 +0200 Subject: [PATCH 0014/8077] No explicit use of code-block:: php --- translation/message_format.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/translation/message_format.rst b/translation/message_format.rst index 341da90e039..05042d6c455 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -377,9 +377,7 @@ using the :phpclass:`IntlDateFormatter`: ]; The "function statement" for the ``time`` and ``date`` functions can be one of -short, medium, long or full, as documented on PHP.net. - -.. code-block:: php +short, medium, long or full, as documented on PHP.net:: // prints "Published at Jan 25, 2019 - 11:30 AM" echo $translator->trans('published_at', ['publication_date' => new \DateTime('2019-01-25 11:30:00')]); From 013c72ccfe64ba2dc92c4c444c0ab2a31d7773bd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 09:34:09 +0200 Subject: [PATCH 0015/8077] Minor tweak --- components/messenger.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/messenger.rst b/components/messenger.rst index 7958f4ce5e2..678dd403ec6 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -206,7 +206,8 @@ Your own Sender Imagine that you already have an ``ImportantAction`` message going through the message bus and being handled by a handler. Now, you also want to send this -message as an email. +message as an email (using the :doc:`Mime ` and +:doc:`Mailer ` components). Using the :class:`Symfony\\Component\\Messenger\\Transport\\Sender\\SenderInterface`, you can create your own message sender:: From 5e9de272ec6e9339e67736dca1725d2636fcf2e2 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 13 Jun 2019 09:58:54 +0200 Subject: [PATCH 0016/8077] Use ordered use statements for php code examples --- components/http_client.rst | 2 +- messenger/message-recorder.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/http_client.rst b/components/http_client.rst index 883de230ae3..ad4263eee7e 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -555,8 +555,8 @@ so that the :doc:`HttpKernel component ` needs to be installed in your application:: use Symfony\Component\HttpClient\CachingHttpClient; - use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpClient\HttpClient; + use Symfony\Component\HttpKernel\HttpCache\Store; $store = new Store('/path/to/cache/storage/'); $client = HttpClient::create(); diff --git a/messenger/message-recorder.rst b/messenger/message-recorder.rst index 8ac3039b503..acdff765cb2 100644 --- a/messenger/message-recorder.rst +++ b/messenger/message-recorder.rst @@ -52,8 +52,8 @@ using the ``DispatchAfterCurrentBusMiddleware`` and adding a use App\Messenger\Event\UserRegistered; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Messenger\Envelope; - use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp; use Symfony\Component\Messenger\MessageBusInterface; + use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp; class RegisterUserHandler { From d14cca5880c331f380b40d4f7efda570b3b4e601 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 13 Jun 2019 10:03:43 +0200 Subject: [PATCH 0017/8077] Fix indention --- components/validator/resources.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/validator/resources.rst b/components/validator/resources.rst index e6cc02227d1..bbeac55889e 100644 --- a/components/validator/resources.rst +++ b/components/validator/resources.rst @@ -100,8 +100,8 @@ prefixed classes included in doc block comments (``/** ... */``). For example:: class User { /** - * @Assert\NotBlank - */ + * @Assert\NotBlank + */ protected $name; } From 9ba114236916f29343fded40dc5bd110e149d78f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 13:33:37 +0200 Subject: [PATCH 0018/8077] [Configuration] Some tweaks about Flex usage --- configuration.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/configuration.rst b/configuration.rst index a695b1012e1..39369c0cab2 100644 --- a/configuration.rst +++ b/configuration.rst @@ -20,16 +20,17 @@ directory, which has this default structure: The ``routes.yaml`` file defines the :doc:`routing configuration `; the ``services.yaml`` file configures the services of the :doc:`service container `; the ``bundles.php`` file enables/ -disables packages in your application and is managed automatically by -:doc:`Symfony Flex `. +disables packages in your application. You'll be working most in the ``config/packages/`` directory. This directory stores the configuration of every package installed in your application. Packages (also called "bundles" in Symfony and "plugins/modules" in other projects) add ready-to-use features to your projects. -Most packages add a configuration file in ``config/packages/``. For example, -this is the default file for the "API Platform" package: +When using :doc:`Symfony Flex `, which is enabled by default in +Symfony applications, packages update the ``bundles.php`` file and create new +files in ``config/packages/`` automatically during their installation. For +example, this is the default file created by the "API Platform" package: .. code-block:: yaml From 1c2bae2f7293dd5ad4656990db19da7f6cc9a7dc Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Thu, 13 Jun 2019 14:34:39 +0300 Subject: [PATCH 0019/8077] added "special case" --- contributing/code/pull_requests.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst index 6cfd3c499fe..06ea20cdfb5 100644 --- a/contributing/code/pull_requests.rst +++ b/contributing/code/pull_requests.rst @@ -131,6 +131,13 @@ work: * ``master``, if you are adding a new feature. +.. note:: + + A new **Symfony major version** (e.g. 3.0, 4.0) comes out every *two years*. + It is a very special case when new features must go to development branch. + + Check `Symfony Roadmap`_ for latest development branch and not to ``master`` + .. note:: All bug fixes merged into maintenance branches are also merged into more @@ -423,3 +430,4 @@ before merging. .. _`Symfony Slack`: https://symfony.com/slack-invite .. _`Travis-CI`: https://travis-ci.org/symfony/symfony .. _`draft status`: https://help.github.com/en/articles/about-pull-requests#draft-pull-requests +.. _`Symfony Roadmap`: https://symfony.com/roadmap From 25c0c63faad7d4deae353c5f26f0a2b90745b6c6 Mon Sep 17 00:00:00 2001 From: Patrick PawseyVale Date: Wed, 12 Jun 2019 10:43:43 +0100 Subject: [PATCH 0020/8077] Updates the tip explaining how to set the base phpunit version --- components/phpunit_bridge.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index b36507c0c80..20a518eea01 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -573,8 +573,14 @@ If you have installed the bridge through Composer, you can run it by calling e.g .. tip:: - Set the ``SYMFONY_PHPUNIT_VERSION`` env var to e.g. ``5.5`` to change the - base version of PHPUnit to ``5.5`` instead of the default ``5.3``. + It's possible to to change the base version of PHPUnit to e.g. ``5.5`` + instead of the default ``5.3`` by setting the ``SYMFONY_PHPUNIT_VERSION`` + env var in the ``phpunit.xml.dist`` file e.g. + ````. This is the + preferred method as it can be committed to your version control repository. + + It's also possible set ``SYMFONY_PHPUNIT_VERSION`` as a real env var + (not in a :doc:`dotenv ` file). .. tip:: From 7780a7ecc0ee688709264062ae7463ae3e129a1a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 15:30:12 +0200 Subject: [PATCH 0021/8077] Minor tweak --- components/phpunit_bridge.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index 20a518eea01..cfe3c4c8a3f 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -378,7 +378,7 @@ different class, do it explicitly using ``ClockMock::register(MyClass::class)``: use App\MyClass; use PHPUnit\Framework\TestCase; - use Symfony\Bridge\PhpUnit\ClockMock; + use Symfony\Bridge\PhpUnit\ClockMock; /** * @group time-sensitive @@ -573,14 +573,13 @@ If you have installed the bridge through Composer, you can run it by calling e.g .. tip:: - It's possible to to change the base version of PHPUnit to e.g. ``5.5`` - instead of the default ``5.3`` by setting the ``SYMFONY_PHPUNIT_VERSION`` - env var in the ``phpunit.xml.dist`` file e.g. - ````. This is the + It's possible to change the base version of PHPUnit by setting the + ``SYMFONY_PHPUNIT_VERSION`` env var in the ``phpunit.xml.dist`` file (e.g. + ````). This is the preferred method as it can be committed to your version control repository. - It's also possible set ``SYMFONY_PHPUNIT_VERSION`` as a real env var - (not in a :doc:`dotenv ` file). + It's also possible to set ``SYMFONY_PHPUNIT_VERSION`` as a real env var + (not defined in a :doc:`dotenv ` file). .. tip:: From 9530eb08a7082f1f0e5eadf23243d31d764b2eb1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 15:33:45 +0200 Subject: [PATCH 0022/8077] Minor tweaks --- components/phpunit_bridge.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index abe68552f01..eb882bfe078 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -601,9 +601,7 @@ If you have installed the bridge through Composer, you can run it by calling e.g preferred method as it can be committed to your version control repository. It's also possible to set ``SYMFONY_PHPUNIT_VERSION`` as a real env var - (not defined in a :doc:`dotenv ` file). - - It's also possible to set this env var in the ``phpunit.xml.dist`` file. + (not defined in a :ref:`dotenv file `). .. tip:: From 727d8856986c41368c4a7fc4ddbcf2ac6a39dc75 Mon Sep 17 00:00:00 2001 From: mervinmcdougall <32658654+mervinmcdougall@users.noreply.github.com> Date: Tue, 4 Jun 2019 06:59:44 -0400 Subject: [PATCH 0023/8077] Update controller.rst --- controller.rst | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/controller.rst b/controller.rst index 78837f3f9c2..c8cccbcf4aa 100644 --- a/controller.rst +++ b/controller.rst @@ -21,7 +21,7 @@ to render the content of a page. A Simple Controller ------------------- -While a controller can be any PHP callable (a function, method on an object, +While a controller can be any PHP callable (function, method on an object, or a ``Closure``), a controller is usually a method inside a controller class:: @@ -46,7 +46,7 @@ class:: } } -The controller is the ``number()`` method, which lives inside a +The controller is the ``number()`` method, which lives inside the controller class ``LuckyController``. This controller is pretty straightforward: @@ -91,9 +91,9 @@ For more information on routing, see :doc:`/routing`. The Base Controller Class & Services ------------------------------------ -To make life nicer, Symfony comes with an optional base controller class called +To aid development, Symfony comes with an optional base controller class called :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. -You can extend it to get access to some `helper methods`_. +It can be extended to gain access to `helper methods`_. Add the ``use`` statement atop your controller class and then modify ``LuckyController`` to extend it: @@ -354,8 +354,8 @@ The Request object as a Controller Argument ------------------------------------------- What if you need to read query parameters, grab a request header or get access -to an uploaded file? All of that information is stored in Symfony's ``Request`` -object. To get it in your controller, add it as an argument and +to an uploaded file? That information is stored in Symfony's ``Request`` +object. To access it in your controller, add it as an argument and **type-hint it with the Request class**:: use Symfony\Component\HttpFoundation\Request; @@ -531,13 +531,13 @@ the ``Request`` class:: The ``Request`` class has several public properties and methods that return any information you need about the request. -Like the ``Request``, the ``Response`` object has also a public ``headers`` property. -This is a :class:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag` that has -some nice methods for getting and setting response headers. The header names are -normalized so that using ``Content-Type`` is equivalent to ``content-type`` or even -``content_type``. +Like the ``Request``, the ``Response`` object has a public ``headers`` property. +This object is of the type :class:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag` +and provides methods for getting and setting response headers. The header names are +normalized. As a result, the name ``Content-Type`` is equivalent to +the name ``content-type`` or ``content_type``. -The only requirement for a controller is to return a ``Response`` object:: +In Symfony, a controller is required to return a ``Response`` object:: use Symfony\Component\HttpFoundation\Response; @@ -548,15 +548,15 @@ The only requirement for a controller is to return a ``Response`` object:: $response = new Response(''); $response->headers->set('Content-Type', 'text/css'); -There are special classes that make certain kinds of responses easier. Some of these -are mentioned below. To learn more about the ``Request`` and ``Response`` (and special +To facilitate this, different response objects are included to address different response types. +Some of these are mentioned below. To learn more about the ``Request`` and ``Response`` (and different ``Response`` classes), see the :ref:`HttpFoundation component documentation `. Returning JSON Response ~~~~~~~~~~~~~~~~~~~~~~~ To return JSON from a controller, use the ``json()`` helper method. This returns a -special ``JsonResponse`` object that encodes the data automatically:: +``JsonResponse`` object that encodes the data automatically:: // ... public function index() @@ -606,13 +606,15 @@ The ``file()`` helper provides some arguments to configure its behavior:: Final Thoughts -------------- -Whenever you create a page, you'll ultimately need to write some code that -contains the logic for that page. In Symfony, this is called a controller, -and it's a PHP function where you can do anything in order to return the -final ``Response`` object that will be returned to the user. +In Symfony, a controller is usually a class method which is used to accept requests, +and return a ``Response`` object. When mapped with a url, a controller becomes accessible +and its response can be viewed. -To make life easier, you'll probably extend the base ``AbstractController`` class because -this gives access to shortcut methods (like ``render()`` and ``redirectToRoute()``). +To facilitate the development of controllers, Symfony provides an ``AbstractController``. It +can be used to extend the controller class allowing access to some frequently used utilities +such as ``render`` and ``redirectToRoute``. The ``AbstractController`` also +provides the ``createNotFoundException`` utility which is used to return a page +not found response. In other articles, you'll learn how to use specific services from inside your controller that will help you persist and fetch objects from a database, process form submissions, From ee191a60455ae2ec8d3e23757103d3f51500aa4f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 16:27:00 +0200 Subject: [PATCH 0024/8077] Minor tweaks --- controller.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/controller.rst b/controller.rst index c8cccbcf4aa..58da948abe2 100644 --- a/controller.rst +++ b/controller.rst @@ -534,7 +534,7 @@ information you need about the request. Like the ``Request``, the ``Response`` object has a public ``headers`` property. This object is of the type :class:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag` and provides methods for getting and setting response headers. The header names are -normalized. As a result, the name ``Content-Type`` is equivalent to +normalized. As a result, the name ``Content-Type`` is equivalent to the name ``content-type`` or ``content_type``. In Symfony, a controller is required to return a ``Response`` object:: @@ -548,9 +548,10 @@ In Symfony, a controller is required to return a ``Response`` object:: $response = new Response(''); $response->headers->set('Content-Type', 'text/css'); -To facilitate this, different response objects are included to address different response types. -Some of these are mentioned below. To learn more about the ``Request`` and ``Response`` (and different -``Response`` classes), see the :ref:`HttpFoundation component documentation `. +To facilitate this, different response objects are included to address different +response types. Some of these are mentioned below. To learn more about the +``Request`` and ``Response`` (and different ``Response`` classes), see the +:ref:`HttpFoundation component documentation `. Returning JSON Response ~~~~~~~~~~~~~~~~~~~~~~~ @@ -606,15 +607,16 @@ The ``file()`` helper provides some arguments to configure its behavior:: Final Thoughts -------------- -In Symfony, a controller is usually a class method which is used to accept requests, -and return a ``Response`` object. When mapped with a url, a controller becomes accessible -and its response can be viewed. +In Symfony, a controller is usually a class method which is used to accept +requests, and return a ``Response`` object. When mapped with a URL, a controller +becomes accessible and its response can be viewed. -To facilitate the development of controllers, Symfony provides an ``AbstractController``. It -can be used to extend the controller class allowing access to some frequently used utilities -such as ``render`` and ``redirectToRoute``. The ``AbstractController`` also -provides the ``createNotFoundException`` utility which is used to return a page -not found response. +To facilitate the development of controllers, Symfony provides an +``AbstractController``. It can be used to extend the controller class allowing +access to some frequently used utilities such as ``render()`` and +``redirectToRoute()``. The ``AbstractController`` also provides the +``createNotFoundException()`` utility which is used to return a page not found +response. In other articles, you'll learn how to use specific services from inside your controller that will help you persist and fetch objects from a database, process form submissions, From 1b884c3f5ebb768161cda9ac2338b571bace6143 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2019 16:39:24 +0200 Subject: [PATCH 0025/8077] Minor reword --- translation/message_format.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/translation/message_format.rst b/translation/message_format.rst index 05042d6c455..e0b6bea9dc8 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -230,7 +230,7 @@ Usage of this string is the same as with variables and select:: You can also set an ``offset`` variable to determine whether the pluralization should be offset (e.g. in sentences like ``You and # other people`` - / ``You and # other person``). + / ``You and # other person``). .. tip:: @@ -239,23 +239,23 @@ Usage of this string is the same as with variables and select:: .. code-block:: text - {gender_of_host, select, + {gender_of_host, select, female { - {num_guests, plural, offset:1 + {num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to her party.} =2 {{host} invites {guest} and one other person to her party.} other {{host} invites {guest} and # other people to her party.}} } male { - {num_guests, plural, offset:1 + {num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to his party.} =2 {{host} invites {guest} and one other person to his party.} other {{host} invites {guest} and # other people to his party.}} } other { - {num_guests, plural, offset:1 + {num_guests, plural, offset:1 =0 {{host} does not give a party.} =1 {{host} invites {guest} to their party.} =2 {{host} invites {guest} and one other person to their party.} @@ -377,7 +377,8 @@ using the :phpclass:`IntlDateFormatter`: ]; The "function statement" for the ``time`` and ``date`` functions can be one of -short, medium, long or full, as documented on PHP.net:: +``short``, ``medium``, ``long`` or ``full``, which correspond to the +`constants defined by the IntlDateFormatter class`_:: // prints "Published at Jan 25, 2019 - 11:30 AM" echo $translator->trans('published_at', ['publication_date' => new \DateTime('2019-01-25 11:30:00')]); @@ -439,3 +440,4 @@ The ``number`` formatter allows you to format numbers using Intl's :phpclass:`Nu .. _`ICU MessageFormat`: http://userguide.icu-project.org/formatparse/messages .. _`switch statement`: https://php.net/control-structures.switch .. _`Language Plural Rules`: http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html +.. _`constants defined by the IntlDateFormatter class`: https://php.net/manual/en/class.intldateformatter.php From 27c6979331980ecd9ad9daafa0752aa8ab23cb76 Mon Sep 17 00:00:00 2001 From: Sylvester Date: Thu, 13 Jun 2019 15:43:02 +0100 Subject: [PATCH 0026/8077] Fix a spelling mistake. --- doctrine/reverse_engineering.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doctrine/reverse_engineering.rst b/doctrine/reverse_engineering.rst index 50274e0f5bc..57559479cbc 100644 --- a/doctrine/reverse_engineering.rst +++ b/doctrine/reverse_engineering.rst @@ -94,7 +94,7 @@ The generated PHP classes now have properties and annotation metadata, but they do *not* have any getter or setter methods. If you generated XML or YAML metadata, you don't even have the PHP classes! -To generate the missing getter/setter methods (or to *create* the classes if neceesary), +To generate the missing getter/setter methods (or to *create* the classes if necessary), run: .. code-block:: terminal From 49c0a0a66da94be8b58600d12ed8955a20fc3b66 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 13 Jun 2019 14:51:57 -0400 Subject: [PATCH 0027/8077] renaming services to get better autowireable names and removing unnecessary section --- messenger/multiple_buses.rst | 80 +++++++----------------------------- 1 file changed, 15 insertions(+), 65 deletions(-) diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 9742fba1a52..37af04fe812 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -24,16 +24,16 @@ an **event bus**. The event bus could have zero or more subscribers. framework: messenger: # The bus that is going to be injected when injecting MessageBusInterface - default_bus: messenger.bus.commands + default_bus: command.bus buses: - messenger.bus.commands: + command.bus: middleware: - validation - doctrine_transaction - messenger.bus.queries: + query.bus: middleware: - validation - messenger.bus.events: + event.bus: default_middleware: allow_no_handlers middleware: - validation @@ -52,15 +52,15 @@ an **event bus**. The event bus could have zero or more subscribers. - - + + - + - + @@ -73,20 +73,20 @@ an **event bus**. The event bus could have zero or more subscribers. $container->loadFromExtension('framework', [ 'messenger' => [ // The bus that is going to be injected when injecting MessageBusInterface - 'default_bus' => 'messenger.bus.commands', + 'default_bus' => 'command.bus', 'buses' => [ - 'messenger.bus.commands' => [ + 'command.bus' => [ 'middleware' => [ 'validation', 'doctrine_transaction', ], ], - 'messenger.bus.queries' => [ + 'query.bus' => [ 'middleware' => [ 'validation', ], ], - 'messenger.bus.events' => [ + 'event.bus' => [ 'default_middleware' => 'allow_no_handlers', 'middleware' => [ 'validation', @@ -98,62 +98,12 @@ an **event bus**. The event bus could have zero or more subscribers. This will create three new services: -* ``messenger.bus.commands``: autowireable with the :class:`Symfony\\Component\\Messenger\\MessageBusInterface` +* ``command.bus``: autowireable with the :class:`Symfony\\Component\\Messenger\\MessageBusInterface` type-hint (because this is the ``default_bus``); -* ``messenger.bus.queries``: autowireable with the ``MessageBusInterface $messengerBusQueries``; +* ``query.bus``: autowireable with ``MessageBusInterface $queryBus``; -* ``messenger.bus.events``: autowireable with the ``MessageBusInterface $messengerBusEvents``. - -Type-hints and Auto-wiring --------------------------- - -Auto-wiring is a great feature that allows you to reduce the amount of configuration -required for your service container to be created. By using ``MessageBusInterface`` -as argument type-hint in your services, the default configured bus will be injected -(i.e ``messenger.bus.commands`` in above examples). - -When working with multiple buses, you can use the ``DependencyInjection`` component's -binding capabilities to clarify which bus will be injected based on the argument's name: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - _defaults: - # ... - - bind: - $commandBus: '@messenger.bus.commands' - $queryBus: '@messenger.bus.queries' - $eventBus: '@messenger.bus.events' - - .. code-block:: xml - - - - - - - - - - - - - - - .. code-block:: php - - // config/services.php - $container->bind('$commandBus', 'messenger.bus.commands'); - $container->bind('$queryBus', 'messenger.bus.queries'); - $container->bind('$eventBus', 'messenger.bus.events'); +* ``event.bus``: autowireable with ``MessageBusInterface $eventBus``. Restrict Handlers per Bus ------------------------- From d0f9bfa3d747457ed0f8f09269df279cbb64b7a0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 08:53:58 +0200 Subject: [PATCH 0028/8077] [OptionsResolver] Minor reword in the description --- components/options_resolver.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 29a31c60809..9fd02f3741c 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -5,9 +5,10 @@ The OptionsResolver Component ============================= - The OptionsResolver component is :phpfunction:`array_replace` on steroids. - It allows you to create an options system with required options, defaults, - validation (type, value), normalization and more. + The OptionsResolver component is an improved replacement for the + :phpfunction:`array_replace` PHP function. It allows you to create an + options system with required options, defaults, validation (type, value), + normalization and more. Installation ------------ From 24ac112285f5979be68933a7f4783f2211774a3b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 14 Jun 2019 10:40:36 +0200 Subject: [PATCH 0029/8077] use Cookie::create() --- components/http_foundation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 6b3d9746e57..be6db4ab7ab 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -413,7 +413,7 @@ attribute:: use Symfony\Component\HttpFoundation\Cookie; - $response->headers->setCookie(new Cookie('foo', 'bar')); + $response->headers->setCookie(Cookie::create('foo', 'bar')); The :method:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag::setCookie` From 790468f06a0a5679d6524188f5cc0c766a3cb821 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 14 Jun 2019 10:46:40 +0200 Subject: [PATCH 0030/8077] [#11618] minor tweaks --- components/http_foundation.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 14d73c310a4..603df5663e9 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -528,20 +528,20 @@ if it should:: The ``BinaryFileResponse`` will only handle ``X-Sendfile`` if the particular header is present. For Apache, this is not the default case. - To add the header use the ``mod_headers`` Apache module and add the following to the Apache configuration. + To add the header use the ``mod_headers`` Apache module and add the following to the Apache configuration: .. code-block:: apache - - # This is already present somewhere... - XSendFile on - XSendFilePath ...some path... + + # This is already present somewhere... + XSendFile on + XSendFilePath ...some path... - # This needs to be added: - - RequestHeader set X-Sendfile-Type X-Sendfile + # This needs to be added: + + RequestHeader set X-Sendfile-Type X-Sendfile + - With the ``BinaryFileResponse``, you can still set the ``Content-Type`` of the sent file, or change its ``Content-Disposition``:: From 92c592c321fa2cf819f3ff4f023079a4f9cb4f71 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 12:54:03 +0200 Subject: [PATCH 0031/8077] Added the missing "experimental" notices in some components --- components/http_client.rst | 3 ++- components/mailer.rst | 5 +++++ components/mime.rst | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/components/http_client.rst b/components/http_client.rst index ad4263eee7e..22000ea627d 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -11,7 +11,8 @@ The HttpClient Component .. versionadded:: 4.3 - The HttpClient component was introduced in Symfony 4.3. + The HttpClient component was introduced in Symfony 4.3 and it's still + considered an :doc:`experimental feature `. Installation ------------ diff --git a/components/mailer.rst b/components/mailer.rst index b36b44efb3d..032675c2b46 100644 --- a/components/mailer.rst +++ b/components/mailer.rst @@ -7,6 +7,11 @@ The Mailer Component The Mailer component helps sending emails. +.. versionadded:: 4.3 + + The Mailer component was introduced in Symfony 4.3 and it's still + considered an :doc:`experimental feature `. + Installation ------------ diff --git a/components/mime.rst b/components/mime.rst index 5b258080bcc..820333c1728 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -6,9 +6,14 @@ The Mime Component ================== - The MIME component allows manipulating the MIME messages used to send emails + The Mime component allows manipulating the MIME messages used to send emails and provides utilities related to MIME types. +.. versionadded:: 4.3 + + The Mime component was introduced in Symfony 4.3 and it's still + considered an :doc:`experimental feature `. + Installation ------------ From d78f17daa5a9d193f712671f3786606d6e198545 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 13 Jun 2019 12:11:02 +0200 Subject: [PATCH 0032/8077] Uncomment code and add info --- email.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/email.rst b/email.rst index f0a1f181c18..0e60f8b520f 100644 --- a/email.rst +++ b/email.rst @@ -114,8 +114,8 @@ an email is pretty straightforward:: ), 'text/html' ) - /* - * If you also want to include a plaintext version of the message + + // you can remove the following code if you don't define a text version for your emails ->addPart( $this->renderView( 'Emails/registration.txt.twig', @@ -123,7 +123,6 @@ an email is pretty straightforward:: ), 'text/plain' ) - */ ; $mailer->send($message); From d8367f0b7c16b1c483bb446f56ef8ff146bde162 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Fri, 14 Jun 2019 13:42:15 +0200 Subject: [PATCH 0033/8077] Remove space before self closing xml tag --- components/phpunit_bridge.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index cfe3c4c8a3f..c258abbb077 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -575,7 +575,7 @@ If you have installed the bridge through Composer, you can run it by calling e.g It's possible to change the base version of PHPUnit by setting the ``SYMFONY_PHPUNIT_VERSION`` env var in the ``phpunit.xml.dist`` file (e.g. - ````). This is the + ````). This is the preferred method as it can be committed to your version control repository. It's also possible to set ``SYMFONY_PHPUNIT_VERSION`` as a real env var From 2412bf2f1a2e453fc5917d3021140ffe0914c6e9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 09:08:42 +0200 Subject: [PATCH 0034/8077] Reworded the Setup article about the local Symfony server --- setup.rst | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/setup.rst b/setup.rst index f2bbb1396cb..5887f946b70 100644 --- a/setup.rst +++ b/setup.rst @@ -37,25 +37,25 @@ In other words, your new app is ready! Running your Symfony Application -------------------------------- -On production, you should use a web server like Nginx or Apache -(see :doc:`configuring a web server to run Symfony `). -But for development, it's convenient to use the :doc:`Symfony Local Web Server `. +On production, you should use a web server like Nginx or Apache (see +:doc:`configuring a web server to run Symfony `). +But for development, it's more convenient to use the +:doc:`Symfony Local Web Server `. -.. note:: +This local server provides support for HTTP/2, TLS/SSL, automatic generation of +security certificates and many other features. It works with any PHP application, +not only Symfony projects, so it's a very useful development tool. - If you want to use a virtual machine (VM) with Vagrant, check out - :doc:`Homestead `. - -Move into your new project and start the server: +Once installed, move into your new project and start the local web server: .. code-block:: terminal $ cd my-project $ symfony server:start -Open your browser and navigate to ``http://localhost:8000/``. If everything is working, -you'll see a welcome page. Later, when you are finished working, stop the server -by pressing ``Ctrl+C`` from your terminal. +Open your browser and navigate to ``http://localhost:8000/``. If everything is +working, you'll see a welcome page. Later, when you are finished working, stop +the server by pressing ``Ctrl+C`` from your terminal. .. tip:: @@ -63,6 +63,11 @@ by pressing ``Ctrl+C`` from your terminal. some technical requirements. Use the :doc:`Symfony Requirements Checker ` tool to make sure your system is set up. +.. note:: + + If you want to use a virtual machine (VM) with Vagrant, check out + :doc:`Homestead `. + .. tip:: If you're using a VM, you may need to tell the server to bind to all IP addresses: From 3c94f804acae05cba32deffb652cc10d1ab5153c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 15:50:58 +0200 Subject: [PATCH 0035/8077] Remove a tip about Symfony setup and VMs --- setup.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/setup.rst b/setup.rst index 5887f946b70..de463436926 100644 --- a/setup.rst +++ b/setup.rst @@ -68,17 +68,6 @@ the server by pressing ``Ctrl+C`` from your terminal. If you want to use a virtual machine (VM) with Vagrant, check out :doc:`Homestead `. -.. tip:: - - If you're using a VM, you may need to tell the server to bind to all IP addresses: - - .. code-block:: terminal - - $ php bin/console server:start 0.0.0.0:8000 - - You should **NEVER** listen to all interfaces on a computer that is - directly accessible from the Internet. - Storing your Project in git --------------------------- From f3ef5de4110651f629ead4a45cf5447624f0e4c4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 16:36:11 +0200 Subject: [PATCH 0036/8077] Rewords --- contributing/code/pull_requests.rst | 11 +++++------ contributing/community/releases.rst | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst index 06ea20cdfb5..52342af3230 100644 --- a/contributing/code/pull_requests.rst +++ b/contributing/code/pull_requests.rst @@ -131,12 +131,11 @@ work: * ``master``, if you are adding a new feature. -.. note:: - - A new **Symfony major version** (e.g. 3.0, 4.0) comes out every *two years*. - It is a very special case when new features must go to development branch. - - Check `Symfony Roadmap`_ for latest development branch and not to ``master`` + The only exception is when a new :doc:`major Symfony version ` + (4.0, 5.0, etc.) comes out every two years. Because of the + :ref:`special development process ` of those versions, + you need to use the previous minor version for the features (e.g. use ``3.4`` + instead of ``4.0``, use ``4.4`` instead of ``5.0``, etc.) .. note:: diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 3bbd4dd23ac..d2336e21e86 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -84,6 +84,23 @@ adds a new preferred one along side. Read the :ref:`conventions ` document to learn more about how deprecations are handled in Symfony. +.. _major-version-development: + +This deprecation policy also requires a custom development process for major +versions (4.0, 5.0, 6.0, etc.) In those cases, Symfony develops at the same time +two versions: the new major one (e.g. 4.0) and the latest version of the +previous branch (e.g. 3.4). + +Both versions have the same new features, but they differ in the deprecated +features. The oldest version (3.4 in this example) contains all the deprecated +features whereas the new version (4.0 in this example) removes all of them. + +This allows you to upgrade your projects to the latest minor version (e.g. 3.4), +see all the deprecation messages and fix them. Once you have fixed all those +deprecations, you can upgrade to the new major version (e.g. 4.0) without +effort, because it contains the same features (the only difference are the +deprecated features, which your project no longer uses). + Rationale --------- From c8a6ec5543bdfb5e63cf690727e0cfc99eaebb1b Mon Sep 17 00:00:00 2001 From: Dmitry Simushev Date: Sat, 8 Jun 2019 23:30:33 +0300 Subject: [PATCH 0037/8077] [DomCrawler] Add hint about Form::getName method --- components/dom_crawler.rst | 2 ++ testing.rst | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index c6163f0fbcb..6d2745066b2 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -478,6 +478,8 @@ useful methods for working with forms:: $method = $form->getMethod(); + $name = $form->getName(); + The :method:`Symfony\\Component\\DomCrawler\\Form::getUri` method does more than just return the ``action`` attribute of the form. If the form method is GET, then it mimics the browser's behavior and returns the ``action`` diff --git a/testing.rst b/testing.rst index 3163d57721a..2b86ef7bd5e 100644 --- a/testing.rst +++ b/testing.rst @@ -801,6 +801,12 @@ their type:: $form['my_form[field][O]']->upload('/path/to/lucas.jpg'); $form['my_form[field][1]']->upload('/path/to/lisa.jpg'); +.. tip:: + + All field names in forms generated by Symfony are prefixed with the form's name. + If the name is autogenerated you can get it using + :method:`Symfony\\Component\\DomCrawler\\Form::getName` method. + .. tip:: If you purposefully want to select "invalid" select/radio values, see From 8c33fce7e9453c7b51871ca404bbad1e83daaebe Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 17 Jun 2019 08:35:03 +0200 Subject: [PATCH 0038/8077] Remove old versionadded directives --- reference/constraints/Type.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/reference/constraints/Type.rst b/reference/constraints/Type.rst index 319387416ec..81eed39be6c 100644 --- a/reference/constraints/Type.rst +++ b/reference/constraints/Type.rst @@ -126,11 +126,6 @@ function) and ``accessCode`` contains either only letters or only digits (using } } -.. versionadded:: 4.4 - - The feature to define multiple types in the ``type`` option was introduced - in Symfony 4.4. - Options ------- @@ -161,11 +156,6 @@ type **type**: ``string`` or ``array`` [:ref:`default option `] -.. versionadded:: 4.4 - - The feature to define multiple types in the ``type`` option was introduced - in Symfony 4.4. - This required option defines the type or collection of types allowed for the given value. Each type is defined as the fully qualified class name or one of the PHP datatypes as determined by PHP's ``is_*()`` functions. From 73206d1062fa4c069dc21d901abbc781baab9605 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 17 Jun 2019 09:02:12 +0200 Subject: [PATCH 0039/8077] add CODEOWNERS file for the docs --- .github/CODEOWNERS | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..d917bbee7ac --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,27 @@ +# Console +/console* @chalasr +/components/console* @chalasr + +# Form +/forms.rst @xabbuh +/components/form* @xabbuh +/reference/forms* @xabbuh + +# PropertyInfo +/components/property_info* @dunglas + +# Security +/security* @chalasr +/components/security* @chalasr + +# Validator +/validation/* +/components/validator* @xabbuh +/reference/constraints* @xabbuh + +# Workflow +/workflow* @lyrixx +/components/workflow* @lyrixx + +# Yaml +/components/yaml* @xabbuh From 1440ca358e629b61d99df23f1f5e1d557ab79c8a Mon Sep 17 00:00:00 2001 From: murtukov Date: Sun, 16 Jun 2019 19:03:41 +0200 Subject: [PATCH 0040/8077] Update Collection.rst The line: "personal_email: Email" contains a mistake, as every constraint name should start on a new line and with the - symbol. If I use the code sample in the yaml file, it would throw an exception: "The value Email is not an instance of Constraint in constraint Symfony\Component\Validator\Constraints\Required" --- reference/constraints/Collection.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reference/constraints/Collection.rst b/reference/constraints/Collection.rst index 5c25f89b3ce..9984e3885e0 100644 --- a/reference/constraints/Collection.rst +++ b/reference/constraints/Collection.rst @@ -94,7 +94,8 @@ following: profileData: - Collection: fields: - personal_email: Email + personal_email: + - Email: ~ short_bio: - NotBlank: ~ - Length: From 35278b4310e2ecd6e55ce8ab812dfa59d437abea Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2019 11:38:08 +0200 Subject: [PATCH 0041/8077] fix symbol option description --- reference/forms/types/percent.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/percent.rst b/reference/forms/types/percent.rst index b90d57de2b5..87ad13ac4e9 100644 --- a/reference/forms/types/percent.rst +++ b/reference/forms/types/percent.rst @@ -9,7 +9,7 @@ percentage data. If your percentage data is stored as a decimal (e.g. ``0.95``), you can use this field out-of-the-box. If you store your data as a number (e.g. ``95``), you should set the ``type`` option to ``integer``. -When ``symbol`` is ``true``, the field will add a percentage sign "``%``" after +When ``symbol`` is not ``false``, the field will render the given string after the input. +-------------+-----------------------------------------------------------------------+ @@ -59,7 +59,7 @@ use this option. symbol ~~~~~~ -**type**: ``boolean`` or ``string`` **default**: ``true`` +**type**: ``boolean`` or ``string`` **default**: ``%`` .. versionadded:: 4.3 From abe4c85a409f9cf5e536e729341b0467022b5d46 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 18 Jun 2019 10:06:28 +0200 Subject: [PATCH 0042/8077] Use valid number of backslashes, depending on 1 or 2 ticks @javiereguiluz I created a rule for the doctor :+1: --- components/cache/cache_pools.rst | 14 +++++++------- components/form.rst | 4 ++-- contributing/code/bc.rst | 2 +- create_framework/separation_of_concerns.rst | 2 +- reference/dic_tags.rst | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/cache/cache_pools.rst b/components/cache/cache_pools.rst index 45bc9e38f53..57a4fe33337 100644 --- a/components/cache/cache_pools.rst +++ b/components/cache/cache_pools.rst @@ -69,7 +69,7 @@ Saving Cache Items ------------------ The most common method to save cache items is -``Psr\\Cache\\CacheItemPoolInterface::save``, which stores the +``Psr\Cache\CacheItemPoolInterface::save``, which stores the item in the cache immediately (it returns ``true`` if the item was saved or ``false`` if some error occurred):: @@ -80,9 +80,9 @@ item in the cache immediately (it returns ``true`` if the item was saved or Sometimes you may prefer to not save the objects immediately in order to increase the application performance. In those cases, use the -``Psr\\Cache\\CacheItemPoolInterface::saveDeferred`` method to mark cache +``Psr\Cache\CacheItemPoolInterface::saveDeferred`` method to mark cache items as "ready to be persisted" and then call to -``Psr\\Cache\\CacheItemPoolInterface::commit`` method when you are ready +``Psr\Cache\CacheItemPoolInterface::commit`` method when you are ready to persist them all:: // ... @@ -103,14 +103,14 @@ Removing Cache Items -------------------- Cache Pools include methods to delete a cache item, some of them or all of them. -The most common is ``Psr\\Cache\\CacheItemPoolInterface::deleteItem``, +The most common is ``Psr\Cache\CacheItemPoolInterface::deleteItem``, which deletes the cache item identified by the given key (it returns ``true`` when the item is successfully deleted or doesn't exist and ``false`` otherwise):: // ... $isDeleted = $cache->deleteItem('user_'.$userId); -Use the ``Psr\\Cache\\CacheItemPoolInterface::deleteItems`` method to +Use the ``Psr\Cache\CacheItemPoolInterface::deleteItems`` method to delete several cache items simultaneously (it returns ``true`` only if all the items have been deleted, even when any or some of them don't exist):: @@ -118,7 +118,7 @@ items have been deleted, even when any or some of them don't exist):: $areDeleted = $cache->deleteItems(['category1', 'category2']); Finally, to remove all the cache items stored in the pool, use the -``Psr\\Cache\\CacheItemPoolInterface::clear`` method (which returns ``true`` +``Psr\Cache\CacheItemPoolInterface::clear`` method (which returns ``true`` when all items are successfully deleted):: // ... @@ -158,7 +158,7 @@ Pruning Cache Items Some cache pools do not include an automated mechanism for pruning expired cache items. For example, the :ref:`FilesystemAdapter ` cache does not remove expired cache items *until an item is explicitly requested and determined to -be expired*, for example, via a call to ``Psr\\Cache\\CacheItemPoolInterface::getItem``. +be expired*, for example, via a call to ``Psr\Cache\CacheItemPoolInterface::getItem``. Under certain workloads, this can cause stale cache entries to persist well past their expiration, resulting in a sizable consumption of wasted disk or memory space from excess, expired cache items. diff --git a/components/form.rst b/components/form.rst index 9a7ba0433b4..6b74f8407c1 100644 --- a/components/form.rst +++ b/components/form.rst @@ -223,7 +223,7 @@ to bootstrap or access Twig and add the :class:`Symfony\\Bridge\\Twig\\Extension .. versionadded:: 1.30 - The ``Twig\\RuntimeLoader\\FactoryRuntimeLoader`` was introduced in Twig 1.30. + The ``Twig\RuntimeLoader\FactoryRuntimeLoader`` was introduced in Twig 1.30. The exact details of your `Twig Configuration`_ will vary, but the goal is always to add the :class:`Symfony\\Bridge\\Twig\\Extension\\FormExtension` @@ -263,7 +263,7 @@ installed: $ composer require symfony/translation symfony/config Next, add the :class:`Symfony\\Bridge\\Twig\\Extension\\TranslationExtension` -to your ``Twig\\Environment`` instance:: +to your ``Twig\Environment`` instance:: use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Component\Form\Forms; diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index b9654f53002..fbecf1ab880 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -94,7 +94,7 @@ public methods and properties. .. caution:: Classes, properties and methods that bear the tag ``@internal`` as well as - the classes located in the various ``*\\Tests\\`` namespaces are an + the classes located in the various ``*\Tests\`` namespaces are an exception to this rule. They are meant for internal use only and should not be accessed by your own code. diff --git a/create_framework/separation_of_concerns.rst b/create_framework/separation_of_concerns.rst index c318d2da459..7444137ed71 100644 --- a/create_framework/separation_of_concerns.rst +++ b/create_framework/separation_of_concerns.rst @@ -13,7 +13,7 @@ simple principle: the logic is about creating the Response associated with a Request. Let's create our very own namespace for our framework: ``Simplex``. Move the -request handling logic into its own ``Simplex\\Framework`` class:: +request handling logic into its own ``Simplex\Framework`` class:: // example.com/src/Simplex/Framework.php namespace Simplex; diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 2494272e8fb..deafbbef14f 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -199,7 +199,7 @@ assetic.formula_loader **Purpose**: Add a formula loader to the current asset manager A Formula loader is a class implementing -``Assetic\\Factory\Loader\\FormulaLoaderInterface`` interface. This class +``Assetic\Factory\Loader\FormulaLoaderInterface`` interface. This class is responsible for loading assets from a particular kind of resources (for instance, twig template). Assetic ships loaders for PHP and Twig templates. From 501c50981a07c55fca4ebf8844d50b4280e0976e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 18 Jun 2019 10:24:35 +0200 Subject: [PATCH 0043/8077] Use valid number of backslashes, depending on 1 or 2 ticks --- components/cache/cache_items.rst | 2 +- messenger/multiple_buses.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/cache/cache_items.rst b/components/cache/cache_items.rst index 04d32547b65..e9fafe348b0 100644 --- a/components/cache/cache_items.rst +++ b/components/cache/cache_items.rst @@ -42,7 +42,7 @@ pool:: // $cache pool object was created before $productsCount = $cache->getItem('stats.products_count'); -Then, use the ``Psr\\Cache\\CacheItemInterface::set`` method to set the data stored +Then, use the ``Psr\Cache\CacheItemInterface::set`` method to set the data stored in the cache item (this step is done automatically when using the Cache Contracts):: // storing a simple integer diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 5099a389005..86b10116e82 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -209,7 +209,7 @@ the correct tag: # config/services.yaml - # put this after the `App\` line that registers all your services + # put this after the "App\" line that registers all your services command_handlers: namespace: App\MessageHandler\ resource: '%kernel.project_dir%/src/MessageHandler/*CommandHandler.php' From a299af7eec26a82fa02d9e8b41e6bd09b8747e48 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 18 Jun 2019 10:28:43 +0200 Subject: [PATCH 0044/8077] Use valid number of backslashes, depending on 1 or 2 ticks --- messenger.rst | 2 +- reference/configuration/twig.rst | 2 +- reference/forms/types/collection.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index 3f8d5412313..7f8eed31192 100644 --- a/messenger.rst +++ b/messenger.rst @@ -249,7 +249,7 @@ you can configure them to be sent to a transport: ], ]); -Thanks to this, the ``App\\Message\\SmsNotification`` will be sent to the ``async`` +Thanks to this, the ``App\Message\SmsNotification`` will be sent to the ``async`` transport and its handler(s) will *not* be called immediately. Any messages not matched under ``routing`` will still be handled immediately. diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 1e801569f28..a95a5899f91 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -112,7 +112,7 @@ called to determine the default escaping applied to the template. base_template_class ~~~~~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``'Twig\\Template'`` +**type**: ``string`` **default**: ``Twig\Template`` Twig templates are compiled into PHP classes before using them to render contents. This option defines the base class from which all the template classes diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 76a0aee8f5b..8b873c207d3 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -324,7 +324,7 @@ type:: entry_type ~~~~~~~~~~ -**type**: ``string`` **default**: ``Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType`` +**type**: ``string`` **default**: ``Symfony\Component\Form\Extension\Core\Type\TextType`` This is the field type for each item in this collection (e.g. ``TextType``, ``ChoiceType``, etc). For example, if you have an array of email addresses, From 4d84a697cae7e1d8eb2ae105a79508c01ecfadf4 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 18 Jun 2019 10:58:02 +0200 Subject: [PATCH 0045/8077] kernel.debug is a boolean, not a string --- reference/configuration/twig.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index d37ee5f10ce..84f4595f24a 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -55,7 +55,7 @@ Configuration auto_reload ~~~~~~~~~~~ -**type**: ``boolean`` **default**: ``'%kernel.debug%'`` +**type**: ``boolean`` **default**: ``%kernel.debug%`` If ``true``, whenever a template is rendered, Symfony checks first if its source code has changed since it was compiled. If it has changed, the template is @@ -174,7 +174,7 @@ specific timezone is passed as argument. debug ~~~~~ -**type**: ``boolean`` **default**: ``'%kernel.debug%'`` +**type**: ``boolean`` **default**: ``%kernel.debug%`` If ``true``, the compiled templates include a ``__toString()`` method that can be used to display their nodes. From 25b70a3f54ef6b7c1970e1d82e7392b3e21c7848 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 18 Jun 2019 11:07:43 +0200 Subject: [PATCH 0046/8077] Removed old deprecated directives. --- components/serializer.rst | 17 ------ configuration.rst | 5 -- .../front_controllers_and_kernel.rst | 5 -- reference/twig_reference.rst | 52 ------------------- translation/templates.rst | 40 ++------------ 5 files changed, 3 insertions(+), 116 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index b000fd6ede9..b5604d8996c 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -660,11 +660,6 @@ When serializing, you can set a callback to format a specific object property:: $serializer->serialize($person, 'json'); // Output: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"} -.. deprecated:: 4.2 - - The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setCallbacks` is deprecated since - Symfony 4.2, use the "callbacks" key of the context instead. - .. _component-serializer-normalizers: Normalizers @@ -951,12 +946,6 @@ having unique identifiers:: var_dump($serializer->serialize($org, 'json')); // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} -.. deprecated:: 4.2 - - The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setCircularReferenceHandler` - method is deprecated since Symfony 4.2. Use the ``circular_reference_handler`` - key of the context instead. - Handling Serialization Depth ---------------------------- @@ -1108,12 +1097,6 @@ having unique identifiers:: ]; */ -.. deprecated:: 4.2 - - The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setMaxDepthHandler` - method is deprecated since Symfony 4.2. Use the ``max_depth_handler`` - key of the context instead. - Handling Arrays --------------- diff --git a/configuration.rst b/configuration.rst index 39369c0cab2..2bda9d4e394 100644 --- a/configuration.rst +++ b/configuration.rst @@ -325,11 +325,6 @@ can override it for commands by setting the ``APP_ENV`` value before running the # Ignore the .env file and run this command in production $ APP_ENV=prod php bin/console command_name -.. deprecated:: 4.2 - - In previous Symfony versions you could configure the environment with the - ``--env`` command option, which was deprecated in Symfony 4.2. - Creating a New Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/configuration/front_controllers_and_kernel.rst b/configuration/front_controllers_and_kernel.rst index 865a4077157..d986d7471b7 100644 --- a/configuration/front_controllers_and_kernel.rst +++ b/configuration/front_controllers_and_kernel.rst @@ -156,11 +156,6 @@ before running them: # Ignore the .env file and enable the debug mode for this command $ APP_DEBUG=1 php bin/console command_name -.. deprecated:: 4.2 - - In previous Symfony versions you could configure the debug mode with the - ``--no-debug`` command option, which was deprecated in Symfony 4.2. - Internally, the value of the debug mode becomes the ``kernel.debug`` parameter used inside the :doc:`service container `. If you look inside the application configuration file, you'll see the diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 083583727e7..6642d775911 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -356,33 +356,6 @@ trans Translates the text into the current language. More information in :ref:`Translation Filters `. -transchoice -~~~~~~~~~~~ - -.. deprecated:: 4.2 - - The ``transchoice`` filter is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` filter instead. - -.. code-block:: twig - - {{ message|transchoice(count, arguments = [], domain = null, locale = null) }} - -``message`` - **type**: ``string`` -``count`` - **type**: ``integer`` -``arguments`` *(optional)* - **type**: ``array`` **default**: ``[]`` -``domain`` *(optional)* - **type**: ``string`` **default**: ``null`` -``locale`` *(optional)* - **type**: ``string`` **default**: ``null`` - -Translates the text with pluralization support. More information in -:ref:`Translation Filters `. - yaml_encode ~~~~~~~~~~~ @@ -591,31 +564,6 @@ trans Renders the translation of the content. More information in :ref:`translation-tags`. -transchoice -~~~~~~~~~~~ - -.. deprecated:: 4.2 - - The ``transchoice`` tag is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` tag instead. - -.. code-block:: twig - - {% transchoice count with vars from domain into locale %}{% endtranschoice %} - -``count`` - **type**: ``integer`` -``vars`` *(optional)* - **type**: ``array`` **default**: ``[]`` -``domain`` *(optional)* - **type**: ``string`` **default**: ``null`` -``locale`` *(optional)* - **type**: ``string`` **default**: ``null`` - -Renders the translation of the content with pluralization support, more -information in :ref:`translation-tags`. - trans_default_domain ~~~~~~~~~~~~~~~~~~~~ diff --git a/translation/templates.rst b/translation/templates.rst index 903f1934d92..b820bfb0fba 100644 --- a/translation/templates.rst +++ b/translation/templates.rst @@ -9,27 +9,13 @@ Twig Templates Using Twig Tags ~~~~~~~~~~~~~~~ -Symfony provides specialized Twig tags (``trans`` and ``transchoice``) to -help with message translation of *static blocks of text*: +Symfony provides a specialized Twig tag ``trans`` to help with message +translation of *static blocks of text*: .. code-block:: twig {% trans %}Hello %name%{% endtrans %} - {% transchoice count %} - {0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples - {% endtranschoice %} - -The ``transchoice`` tag automatically gets the ``%count%`` variable from -the current context and passes it to the translator. This mechanism only -works when you use a placeholder following the ``%var%`` pattern. - -.. deprecated:: 4.2 - - The ``transchoice`` tag is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` tag instead. - .. caution:: The ``%var%`` notation of placeholders is required when translating in @@ -48,34 +34,19 @@ You can also specify the message domain and pass some additional variables: {% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %} - {% transchoice count with {'%name%': 'Fabien'} from 'app' %} - {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples - {% endtranschoice %} - .. _translation-filters: Using Twig Filters ~~~~~~~~~~~~~~~~~~ -The ``trans`` and ``transchoice`` filters can be used to translate *variable -texts* and complex expressions: +The ``trans`` filter can be used to translate *variable texts* and complex expressions: .. code-block:: twig {{ message|trans }} - {{ message|transchoice(5) }} - {{ message|trans({'%name%': 'Fabien'}, 'app') }} - {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }} - -.. deprecated:: 4.2 - - The ``transchoice`` filter is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` filter instead. - .. tip:: Using the translation tags or filters have the same effect, but with @@ -116,8 +87,3 @@ The translator service is accessible in PHP templates through the trans('Symfony is great') ?> - transChoice( - '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - 10, - ['%count%' => 10] - ) ?> From d05ecdb3182b3f8a8a10684f9d63448757681712 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 18 Jun 2019 21:49:00 +0200 Subject: [PATCH 0047/8077] update used PHPUnit versions --- components/phpunit_bridge.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index f81e4a09e0b..d6c45fde25f 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -624,8 +624,9 @@ its ``bin/simple-phpunit`` command. It has the following features: * Does not embed ``symfony/yaml`` nor ``prophecy`` to prevent any conflicts with these dependencies; -* Uses PHPUnit 4.8 when run with PHP <=5.5, PHPUnit 5.7 when run with PHP >=5.6 - and PHPUnit 6.5 when run with PHP >=7.2; +* Uses PHPUnit 4.8 when run with PHP <=5.5, PHPUnit 5.7 when run with PHP 5.6, + PHPUnit 6.5 when run with PHP 7.0, PHPUnit 7.5 when run with PHP 7.1 and + PHPUnit 8.2 when run with PHP >=7.2; * Collects and replays skipped tests when the ``SYMFONY_PHPUNIT_SKIPPED_TESTS`` env var is defined: the env var should specify a file name that will be used for storing skipped tests on a first run, and replay them on the second run; From cd23bdea876508071c633e27280064e3e58c2f46 Mon Sep 17 00:00:00 2001 From: laurent negre Date: Tue, 18 Jun 2019 21:54:55 +0200 Subject: [PATCH 0048/8077] Update serializer.rst Fix name variable in code exemple. --- components/serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/serializer.rst b/components/serializer.rst index c820527c4a2..e39e1ee5578 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -661,7 +661,7 @@ When serializing, you can set a callback to format a specific object property:: $defaultContext = [ AbstractNormalizer::CALLBACKS => [ - 'createdAt' => $callback, + 'createdAt' => $dateCallback, ], ]; From 7c2e9b022007702ad194df04d6b5d2647b63ff0f Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 19 Jun 2019 08:33:52 +0200 Subject: [PATCH 0049/8077] [HttpClient] Fixed missing parenthesis [HttpClient] Fixed missing parenthesis in Progress callback example --- components/http_client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_client.rst b/components/http_client.rst index 22000ea627d..c8405954bba 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -290,7 +290,7 @@ called when new data is uploaded or downloaded and at least once per second:: // $dlSize is the total size to be downloaded or -1 if it is unknown // $info is what $response->getInfo() would return at this very time }, - ]; + ]); Any exceptions thrown from the callback will be wrapped in an instance of ``TransportExceptionInterface`` and will abort the request. From ba61bca7c5613956d72e08a57d179bbb2b88e00a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Jun 2019 17:47:48 +0200 Subject: [PATCH 0050/8077] Added some link in the \"Learn More\" section of Asset component --- components/asset.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/asset.rst b/components/asset.rst index ca11f502641..f2255cdfedc 100644 --- a/components/asset.rst +++ b/components/asset.rst @@ -153,7 +153,7 @@ corresponding output file: "...": "..." } -In those cases, use the +In those cases, use the :class:`Symfony\\Component\\Asset\\VersionStrategy\\JsonManifestVersionStrategy`:: use Symfony\Component\Asset\Package; @@ -373,5 +373,8 @@ document inside a template:: Learn more ---------- +* :doc:`How to manage CSS and JavaScript assets in Symfony applications ` +* :doc:`WebLink component ` to preload assets using HTTP/2. + .. _Packagist: https://packagist.org/packages/symfony/asset .. _`Webpack`: https://webpack.js.org/ From 108491317f80b5761ec0cb9bf1b4ba29667e730c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Jun 2019 17:19:49 +0200 Subject: [PATCH 0051/8077] Removed an article related to contributing --- _build/redirection_map | 1 + contributing/community/index.rst | 1 - contributing/community/other.rst | 15 --------------- contributing/map.rst.inc | 1 - 4 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 contributing/community/other.rst diff --git a/_build/redirection_map b/_build/redirection_map index c1190906624..96f93cc858b 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -365,3 +365,4 @@ /workflow/state-machines /workflow/introduction /workflow/usage /workflow /introduction/from_flat_php_to_symfony2 /introduction/from_flat_php_to_symfony +/contributing/community/other /contributing/community diff --git a/contributing/community/index.rst b/contributing/community/index.rst index 70cb60c740e..4a5aab91265 100644 --- a/contributing/community/index.rst +++ b/contributing/community/index.rst @@ -9,4 +9,3 @@ Community reviews mentoring speaker-mentoring - other diff --git a/contributing/community/other.rst b/contributing/community/other.rst deleted file mode 100644 index 2196ccb925c..00000000000 --- a/contributing/community/other.rst +++ /dev/null @@ -1,15 +0,0 @@ -Other Resources -=============== - -In order to follow what is happening in the community you might find helpful -these additional resources: - -* List of open `pull requests`_ -* List of recent `commits`_ -* List of open `bugs and enhancements`_ -* List of open source `bundles`_ - -.. _pull requests: https://github.com/symfony/symfony/pulls -.. _commits: https://github.com/symfony/symfony/commits/master -.. _bugs and enhancements: https://github.com/symfony/symfony/issues -.. _bundles: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories diff --git a/contributing/map.rst.inc b/contributing/map.rst.inc index f4a9afca973..b9c9311a6cb 100644 --- a/contributing/map.rst.inc +++ b/contributing/map.rst.inc @@ -32,7 +32,6 @@ * :doc:`Release Process ` * :doc:`Respectful Review comments ` * :doc:`Community Reviews ` - * :doc:`Other Resources ` * **Diversity** From 01d0c6a5bdf680bfca1d44b75bf66ed18cf76810 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 20 Jun 2019 10:29:19 +0200 Subject: [PATCH 0052/8077] Add missing asterisk (*) --- components/event_dispatcher.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/event_dispatcher.rst b/components/event_dispatcher.rst index 3221612dbb8..36e828b23b0 100644 --- a/components/event_dispatcher.rst +++ b/components/event_dispatcher.rst @@ -86,7 +86,7 @@ The unique event name can be any string, but optionally follows a few simple naming conventions: * Use only lowercase letters, numbers, dots (``.``) and underscores (``_``); -* Prefix names with a namespace followed by a dot (e.g. ``order.``, ``user.*``); +* Prefix names with a namespace followed by a dot (e.g. ``order.*``, ``user.*``); * End names with a verb that indicates what action has been taken (e.g. ``order.placed``). From a2a39b2ef98e85c5b3378bce79d0362f3f785eca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 20 Jun 2019 15:16:20 +0200 Subject: [PATCH 0053/8077] Minor fixes --- reference/configuration/twig.rst | 2 +- reference/forms/types/collection.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 2788446e575..aa5f1a032fb 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -112,7 +112,7 @@ called to determine the default escaping applied to the template. base_template_class ~~~~~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``Twig\Template`` +**type**: ``string`` **default**: ``'Twig\Template'`` Twig templates are compiled into PHP classes before using them to render contents. This option defines the base class from which all the template classes diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 8b873c207d3..ba61e8ca3c3 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -324,7 +324,7 @@ type:: entry_type ~~~~~~~~~~ -**type**: ``string`` **default**: ``Symfony\Component\Form\Extension\Core\Type\TextType`` +**type**: ``string`` **default**: ``'Symfony\Component\Form\Extension\Core\Type\TextType'`` This is the field type for each item in this collection (e.g. ``TextType``, ``ChoiceType``, etc). For example, if you have an array of email addresses, From ecd9e615a03b745df194a52972d7deacb18934ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4dlich?= Date: Thu, 20 Jun 2019 09:08:37 +0200 Subject: [PATCH 0054/8077] Add example for env var processor: trim --- configuration/env_var_processors.rst | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst index 1c4eeb758df..98bffbe3d7d 100644 --- a/configuration/env_var_processors.rst +++ b/configuration/env_var_processors.rst @@ -376,6 +376,43 @@ Symfony provides the following env var processors: and end of the string. This is especially useful in combination with the ``file`` processor, as it'll remove newlines at the end of a file. + .. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + parameters: + env(AUTH_FILE): '../config/auth.json' + google: + auth: '%env(trim:file:AUTH_FILE)%' + + .. code-block:: xml + + + + + + + ../config/auth.json + + + + + + .. code-block:: php + + // config/packages/framework.php + $container->setParameter('env(AUTH_FILE)', '../config/auth.json'); + $container->loadFromExtension('google', [ + 'auth' => '%env(trim:file:AUTH_FILE)%', + ]); + .. versionadded:: 4.3 The ``trim`` processor was introduced in Symfony 4.3. From f0a565d06449d129abddaf1b0d9eeb69f51e8292 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Jun 2019 09:29:46 +0200 Subject: [PATCH 0055/8077] Added a missing redirection for a deleted article --- _build/redirection_map | 1 + 1 file changed, 1 insertion(+) diff --git a/_build/redirection_map b/_build/redirection_map index 46edcee6e1d..b45783b612f 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -422,3 +422,4 @@ /configuration/environments /configuration /configuration/configuration_organization /configuration /contributing/community/other /contributing/community +/profiler/storage /profiler From 7a026dab128f770d8c3e8c37a3957ea6326dc541 Mon Sep 17 00:00:00 2001 From: Mynyx Date: Thu, 20 Jun 2019 18:28:02 +0200 Subject: [PATCH 0056/8077] Add Symfony version to symfony new command --- best_practices/creating-the-project.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 6c860552abc..a8c58686ac8 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -28,11 +28,11 @@ to create files and execute the following commands: .. code-block:: terminal $ cd projects/ - $ symfony new blog + $ symfony new blog 3.4 # Windows c:\> cd projects/ - c:\projects\> php symfony new blog + c:\projects\> php symfony new blog 3.4 .. note:: From 5c20204c6aa7417deccb4ec8b901d7a097cd6a81 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2019 17:03:21 +0200 Subject: [PATCH 0057/8077] [Form] Documented the setInvalidMessage() method for data transformers --- form/data_transformers.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/form/data_transformers.rst b/form/data_transformers.rst index 8775744bdb7..cea8bda1569 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -291,6 +291,44 @@ and type-hint the new class:: // ... } +Instead of defining the same ``invalid_message`` every time the form type is +used, you can set the end-user error message in the data transformer using the +``setInvalidMessage()`` method:: + + // src/Form/DataTransformer/IssueToNumberTransformer.php + namespace App\Form\DataTransformer; + + use Symfony\Component\Form\DataTransformerInterface; + use Symfony\Component\Form\Exception\TransformationFailedException; + + class IssueToNumberTransformer implements DataTransformerInterface + { + // ... + + public function reverseTransform($issueNumber) + { + // ... + + if (null === $issue) { + $privateErrorMessage = sprintf('An issue with number "%s" does not exist!', $issueNumber); + $publicErrorMessage = 'The given "{{ value }}" value is not a valid issue number.'; + + $failure = new TransformationFailedException($privateErrorMessage); + $failure->setInvalidMessage($publicErrorMessage, [ + '{{ value }}' => $issueNumber, + ]); + + throw $failure; + } + + return $issue; + } + } + +.. versionadded:: 4.3 + + The ``setInvalidMessage()`` method was introduced in Symfony 4.3. + That's it! As long as you're using :ref:`autowire ` and :ref:`autoconfigure `, Symfony will automatically know to pass your ``TaskType`` an instance of the ``IssueToNumberTransformer``. From e33b5b2304d4ba0ea72545827f84f617dc13d761 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Fri, 21 Jun 2019 10:43:45 +0200 Subject: [PATCH 0058/8077] [#11756] Added a bit more info on transformation errors --- form/data_transformers.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/form/data_transformers.rst b/form/data_transformers.rst index cea8bda1569..012c25069ad 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -291,9 +291,10 @@ and type-hint the new class:: // ... } -Instead of defining the same ``invalid_message`` every time the form type is -used, you can set the end-user error message in the data transformer using the -``setInvalidMessage()`` method:: +Whenever the transformer throws an exception, the ``invalid_message`` is shown +to the user. Instead of showing the same message every time, you can set the +end-user error message in the data transformer using the +``setInvalidMessage()`` method. It also allows you to include user values:: // src/Form/DataTransformer/IssueToNumberTransformer.php namespace App\Form\DataTransformer; From 828ff371260eb7346e4e970e84934b067cc57c4c Mon Sep 17 00:00:00 2001 From: Ellis Benjamin Date: Fri, 21 Jun 2019 14:51:29 +0200 Subject: [PATCH 0059/8077] Add weeks in the options 'labels' --- reference/forms/types/dateinterval.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/reference/forms/types/dateinterval.rst b/reference/forms/types/dateinterval.rst index 4a79b03a776..fae19648370 100644 --- a/reference/forms/types/dateinterval.rst +++ b/reference/forms/types/dateinterval.rst @@ -146,6 +146,7 @@ are ``null``, so they display the "humanized version" of the child names (``Inve 'invert' => null, 'years' => null, 'months' => null, + 'weeks' => null, 'days' => null, 'hours' => null, 'minutes' => null, From 8830bd55dac76dc853081580c25c20716d9be1f5 Mon Sep 17 00:00:00 2001 From: Mitchel Date: Fri, 7 Jun 2019 09:19:02 +0200 Subject: [PATCH 0060/8077] Update multiple_buses.rst When you want to restrict handlers to a bus you have to `autoconfigure: false` otherwise they still are available for other buses. This change is not complete, the XML has to be updated to (dont know how its looks like in XML). --- messenger/multiple_buses.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 86b10116e82..60838de49a5 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -213,12 +213,14 @@ the correct tag: command_handlers: namespace: App\MessageHandler\ resource: '%kernel.project_dir%/src/MessageHandler/*CommandHandler.php' + autoconfigure: false tags: - { name: messenger.message_handler, bus: messenger.bus.commands } query_handlers: namespace: App\MessageHandler\ resource: '%kernel.project_dir%/src/MessageHandler/*QueryHandler.php' + autoconfigure: false tags: - { name: messenger.message_handler, bus: messenger.bus.queries } @@ -250,11 +252,13 @@ the correct tag: // Command handlers $container->services() ->load('App\MessageHandler\\', '%kernel.project_dir%/src/MessageHandler/*CommandHandler.php') + ->autoconfigure(false) ->tag('messenger.message_handler', ['bus' => 'messenger.bus.commands']); // Query handlers $container->services() ->load('App\MessageHandler\\', '%kernel.project_dir%/src/MessageHandler/*QueryHandler.php') + ->autoconfigure(false) ->tag('messenger.message_handler', ['bus' => 'messenger.bus.queries']); Debugging the Buses From 690473a5f5b80923e070aa59db2ac08a235147b9 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 16 Jun 2019 15:16:46 -0400 Subject: [PATCH 0061/8077] updating xml --- messenger/multiple_buses.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 60838de49a5..9f2fe54424f 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -235,11 +235,11 @@ the correct tag: - + - + From 51a1644894d55ef1331d782ad5781cab03a3046d Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Fri, 31 May 2019 09:21:57 +0200 Subject: [PATCH 0062/8077] Tell about request_matcher --- security/firewall_restriction.rst | 80 ++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/security/firewall_restriction.rst b/security/firewall_restriction.rst index 355b32dea79..3a0278e2592 100644 --- a/security/firewall_restriction.rst +++ b/security/firewall_restriction.rst @@ -4,19 +4,26 @@ How to Restrict Firewalls to a Specific Request =============================================== -When using the Security component, you can create firewalls that match certain request options. -In most cases, matching against the URL is sufficient, but in special cases you can further -restrict the initialization of a firewall against other options of the request. +When using the Security component, firewalls will decide whether they handle a request based on the +result of a request matcher: the first firewall matching the request will handle it. + +The last firewall can be configured without any matcher to handle every incoming request. + +Restricting by Configuration +---------------------------- + +Most of the time you don't need to create matchers yourself as Symfony can do it for you based on the +firewall configuration. .. note:: - You can use any of these restrictions individually or mix them together to get + You can use any of the following restrictions individually or mix them together to get your desired firewall configuration. -Restricting by Pattern ----------------------- +Restricting by Path +~~~~~~~~~~~~~~~~~~~ -This is the default restriction and restricts a firewall to only be initialized if the request URL +This is the default restriction and restricts a firewall to only be initialized if the request path matches the configured ``pattern``. .. configuration-block:: @@ -65,12 +72,12 @@ matches the configured ``pattern``. ]); The ``pattern`` is a regular expression. In this example, the firewall will only be -activated if the URL starts (due to the ``^`` regex character) with ``/admin``. If -the URL does not match this pattern, the firewall will not be activated and subsequent +activated if the path starts (due to the ``^`` regex character) with ``/admin``. If +the path does not match this pattern, the firewall will not be activated and subsequent firewalls will have the opportunity to be matched for this request. Restricting by Host -------------------- +~~~~~~~~~~~~~~~~~~~ If matching against the ``pattern`` only is not enough, the request can also be matched against ``host``. When the configuration option ``host`` is set, the firewall will be restricted to @@ -129,7 +136,7 @@ and subsequent firewalls will have the opportunity to be matched for this request. Restricting by HTTP Methods ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~ The configuration option ``methods`` restricts the initialization of the firewall to the provided HTTP methods. @@ -183,3 +190,54 @@ In this example, the firewall will only be activated if the HTTP method of the request is either ``GET`` or ``POST``. If the method is not in the array of the allowed methods, the firewall will not be activated and subsequent firewalls will again have the opportunity to be matched for this request. + +Restricting by Service +---------------------- + +If the above options don't fit your needs you can configure any service implementing +:class:`Symfony\\Component\\HttpFoundation\\RequestMatcherInterface` as ``request_matcher``. + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + + # ... + security: + firewalls: + secured_area: + request_matcher: app.firewall.secured_area.request_matcher + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + + // ... + $container->loadFromExtension('security', [ + 'firewalls' => [ + 'secured_area' => [ + 'request_matcher' => 'app.firewall.secured_area.request_matcher', + // ... + ], + ], + ]); From 06b7cb07cc2ffe8339fdf0d245ac9001908fc94c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Jun 2019 17:07:38 +0200 Subject: [PATCH 0063/8077] Minor tweaks for previous merge --- security/firewall_restriction.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/security/firewall_restriction.rst b/security/firewall_restriction.rst index 3a0278e2592..c41a66e4e22 100644 --- a/security/firewall_restriction.rst +++ b/security/firewall_restriction.rst @@ -4,27 +4,29 @@ How to Restrict Firewalls to a Specific Request =============================================== -When using the Security component, firewalls will decide whether they handle a request based on the -result of a request matcher: the first firewall matching the request will handle it. +When using the Security component, firewalls will decide whether they handle a +request based on the result of a request matcher: the first firewall matching +the request will handle it. -The last firewall can be configured without any matcher to handle every incoming request. +The last firewall can be configured without any matcher to handle every incoming +request. Restricting by Configuration ---------------------------- -Most of the time you don't need to create matchers yourself as Symfony can do it for you based on the -firewall configuration. +Most of the time you don't need to create matchers yourself as Symfony can do it +for you based on the firewall configuration. .. note:: - You can use any of the following restrictions individually or mix them together to get - your desired firewall configuration. + You can use any of the following restrictions individually or mix them + together to get your desired firewall configuration. Restricting by Path ~~~~~~~~~~~~~~~~~~~ -This is the default restriction and restricts a firewall to only be initialized if the request path -matches the configured ``pattern``. +This is the default restriction and restricts a firewall to only be initialized +if the request path matches the configured ``pattern``. .. configuration-block:: @@ -201,7 +203,7 @@ If the above options don't fit your needs you can configure any service implemen .. code-block:: yaml - # config/packages/security.yaml + # app/config/security.yaml # ... security: @@ -212,7 +214,7 @@ If the above options don't fit your needs you can configure any service implemen .. code-block:: xml - + loadFromExtension('security', [ From 9159ade0e3b94941083e6229341f9d9a2257b1d8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Jun 2019 17:25:42 +0200 Subject: [PATCH 0064/8077] [Messenger] Fixed a reference --- messenger.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/messenger.rst b/messenger.rst index 7f8eed31192..53c4dddc0bf 100644 --- a/messenger.rst +++ b/messenger.rst @@ -623,6 +623,8 @@ config and start your workers: See the `Supervisor docs`_ for more details. +.. _messenger-retries-failures: + Retries & Failures ------------------ @@ -712,8 +714,8 @@ to retry them: $ php bin/console messenger:failed:remove 20 If the message fails again, it will be re-sent back to the failure transport -due to the normal `retry rules `_. Once the max retry has -been hit, the message will be discarded permanently. +due to the normal :ref:`retry rules `. Once the max +retry has been hit, the message will be discarded permanently. .. _messenger-transports-config: From b1639c7a45eed23b3e0659b9d4746f4d5a34b561 Mon Sep 17 00:00:00 2001 From: Mynyx Date: Sat, 22 Jun 2019 23:02:49 +0200 Subject: [PATCH 0065/8077] Update channels_handlers.rst --- logging/channels_handlers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logging/channels_handlers.rst b/logging/channels_handlers.rst index f63a836c0c8..b2b191828ea 100644 --- a/logging/channels_handlers.rst +++ b/logging/channels_handlers.rst @@ -15,8 +15,8 @@ the channel). .. note:: Each channel corresponds to a logger service (``monolog.logger.XXX``) - in the container (use the ``debug:container`` command to see a full list) - and those are injected into different services. + in the container (use the ``php bin/console debug:container monolog`` command + to see a full list) and those are injected into different services. .. _logging-channel-handler: From 8774d43a5dd4d7e5ef7970288869fb6997581a4a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Jun 2019 10:12:53 +0200 Subject: [PATCH 0066/8077] remove outdated deprecation warnings --- configuration.rst | 5 ----- configuration/front_controllers_and_kernel.rst | 5 ----- 2 files changed, 10 deletions(-) diff --git a/configuration.rst b/configuration.rst index 39369c0cab2..2bda9d4e394 100644 --- a/configuration.rst +++ b/configuration.rst @@ -325,11 +325,6 @@ can override it for commands by setting the ``APP_ENV`` value before running the # Ignore the .env file and run this command in production $ APP_ENV=prod php bin/console command_name -.. deprecated:: 4.2 - - In previous Symfony versions you could configure the environment with the - ``--env`` command option, which was deprecated in Symfony 4.2. - Creating a New Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/configuration/front_controllers_and_kernel.rst b/configuration/front_controllers_and_kernel.rst index 865a4077157..d986d7471b7 100644 --- a/configuration/front_controllers_and_kernel.rst +++ b/configuration/front_controllers_and_kernel.rst @@ -156,11 +156,6 @@ before running them: # Ignore the .env file and enable the debug mode for this command $ APP_DEBUG=1 php bin/console command_name -.. deprecated:: 4.2 - - In previous Symfony versions you could configure the debug mode with the - ``--no-debug`` command option, which was deprecated in Symfony 4.2. - Internally, the value of the debug mode becomes the ``kernel.debug`` parameter used inside the :doc:`service container `. If you look inside the application configuration file, you'll see the From f93d963da4d51311e1e82f8ef771a446b0e49659 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Jun 2019 17:49:55 +0200 Subject: [PATCH 0067/8077] Remove an outdated article about Composer --- _build/redirection_map | 1 + contributing/code/tests.rst | 3 ++- create_framework/introduction.rst | 3 ++- setup.rst | 7 +++---- setup/composer.rst | 16 ---------------- setup/unstable_versions.rst | 6 ++++-- 6 files changed, 12 insertions(+), 24 deletions(-) delete mode 100644 setup/composer.rst diff --git a/_build/redirection_map b/_build/redirection_map index 96f93cc858b..80d9086bb0c 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -366,3 +366,4 @@ /workflow/usage /workflow /introduction/from_flat_php_to_symfony2 /introduction/from_flat_php_to_symfony /contributing/community/other /contributing/community +/setup/composer /setup diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 503f66aa5f0..75c72e0128d 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -18,7 +18,7 @@ Before Running the Tests To run the Symfony test suite, install the external dependencies used during the tests, such as Doctrine, Twig and Monolog. To do so, -:doc:`install Composer ` and execute the following: +`install Composer`_ and execute the following: .. code-block:: terminal @@ -54,6 +54,7 @@ what's going on and if the tests are broken because of the new code. On Windows, install the `Cmder`_, `ConEmu`_, `ANSICON`_ or `Mintty`_ free applications to see colored test results. +.. _`install Composer`: https://getcomposer.org/download/ .. _Cmder: http://cmder.net/ .. _ConEmu: https://conemu.github.io/ .. _ANSICON: https://github.com/adoxa/ansicon/releases diff --git a/create_framework/introduction.rst b/create_framework/introduction.rst index 90f2987db2e..98ae23d3252 100644 --- a/create_framework/introduction.rst +++ b/create_framework/introduction.rst @@ -86,7 +86,7 @@ Dependency Management To install the Symfony Components that you need for your framework, you are going to use `Composer`_, a project dependency manager for PHP. If you don't have it -yet, :doc:`download and install Composer ` now. +yet, `download and install Composer`_ now. Our Project ----------- @@ -113,3 +113,4 @@ introduce the HttpFoundation Component and see what it brings us. .. _`Symfony`: https://symfony.com/ .. _`Composer`: http://packagist.org/about-composer +.. _`download and install Composer`: https://getcomposer.org/download/ diff --git a/setup.rst b/setup.rst index 4f0a7d4e4c2..b797397facb 100644 --- a/setup.rst +++ b/setup.rst @@ -135,9 +135,8 @@ applications with `Composer`_, the dependency manager used by modern PHP applications. If you don't have Composer installed in your computer, start by -:doc:`installing Composer globally `. Then, execute the -``create-project`` command to create a new Symfony application based on its -latest stable version: +`installing Composer`_. Then, execute the ``create-project`` command to create a +new Symfony application based on its latest stable version: .. code-block:: terminal @@ -315,11 +314,11 @@ Go Deeper with Setup setup/new_project_git setup/built_in_web_server setup/web_server_configuration - setup/composer setup/* .. _`Joyful Development with Symfony`: https://symfonycasts.com/screencast/symfony3 .. _`Composer`: https://getcomposer.org/ +.. _`installing Composer`: https://getcomposer.org/download/ .. _`Phar extension`: https://php.net/manual/en/intro.phar.php .. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard .. _`The Symfony Demo application`: https://github.com/symfony/demo diff --git a/setup/composer.rst b/setup/composer.rst deleted file mode 100644 index c50216b9520..00000000000 --- a/setup/composer.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. index:: - double: Composer; Installation - -Installing Composer -=================== - -`Composer`_ is the package manager used by modern PHP applications. Use Composer -to manage dependencies in your Symfony applications and to install Symfony -Components in your PHP projects. - -Since this article was first published, Composer installation has improved a lot. -Therefore, the original contents of this article have been removed and you are -encouraged to `install Composer`_ as explained in the official Composer documentation. - -.. _`Composer`: https://getcomposer.org/ -.. _`install Composer`: https://getcomposer.org/download/ diff --git a/setup/unstable_versions.rst b/setup/unstable_versions.rst index 9616ff7685e..0e6c32be47e 100644 --- a/setup/unstable_versions.rst +++ b/setup/unstable_versions.rst @@ -8,8 +8,8 @@ Creating a New Project Based on an Unstable Symfony Version ----------------------------------------------------------- Suppose that Symfony 2.7 version hasn't been released yet and you want to create -a new project to test its features. First, :doc:`install the Composer ` -package manager. Then, open a command console, enter your project's directory and +a new project to test its features. First, `install the Composer package manager`_. +Then, open a command console, enter your project's directory and execute the following command: .. code-block:: terminal @@ -77,3 +77,5 @@ Symfony version has deprecated some of its features. # ... after testing the new Symfony version $ git checkout master $ git branch -D testing_new_symfony + +.. _`install the Composer package manager`: https://getcomposer.org/download/ From d847b0c36e8713ad24fd522952502172ea805eae Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Jun 2019 15:14:36 +0200 Subject: [PATCH 0068/8077] [HttpClient] Fixed the indenting of config reference --- reference/configuration/framework.rst | 78 +++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index b59b5566659..6ee002e5c77 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -92,49 +92,49 @@ Configuration * :ref:`default_options ` - * `bindto`_ - * `cafile`_ - * `capath`_ - * `ciphers`_ - * `headers`_ - * `http_version`_ - * `local_cert`_ - * `local_pk`_ - * `max_redirects`_ - * `no_proxy`_ - * `passphrase`_ - * `peer_fingerprint`_ - * `proxy`_ - * `resolve`_ - * `timeout`_ - * `verify_host`_ - * `verify_peer`_ + * `bindto`_ + * `cafile`_ + * `capath`_ + * `ciphers`_ + * `headers`_ + * `http_version`_ + * `local_cert`_ + * `local_pk`_ + * `max_redirects`_ + * `no_proxy`_ + * `passphrase`_ + * `peer_fingerprint`_ + * `proxy`_ + * `resolve`_ + * `timeout`_ + * `verify_host`_ + * `verify_peer`_ * `max_host_connections`_ * :ref:`scoped_clients ` - * `scope`_ - * `auth_basic`_ - * `auth_bearer`_ - * `base_uri`_ - * `bindto`_ - * `cafile`_ - * `capath`_ - * `ciphers`_ - * `headers`_ - * `http_version`_ - * `local_cert`_ - * `local_pk`_ - * `max_redirects`_ - * `no_proxy`_ - * `passphrase`_ - * `peer_fingerprint`_ - * `proxy`_ - * `query`_ - * `resolve`_ - * `timeout`_ - * `verify_host`_ - * `verify_peer`_ + * `scope`_ + * `auth_basic`_ + * `auth_bearer`_ + * `base_uri`_ + * `bindto`_ + * `cafile`_ + * `capath`_ + * `ciphers`_ + * `headers`_ + * `http_version`_ + * `local_cert`_ + * `local_pk`_ + * `max_redirects`_ + * `no_proxy`_ + * `passphrase`_ + * `peer_fingerprint`_ + * `proxy`_ + * `query`_ + * `resolve`_ + * `timeout`_ + * `verify_host`_ + * `verify_peer`_ * `http_method_override`_ * `ide`_ From 790446dea2c75c7a2ece4d1f703537ef1d78ba9a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 24 Jun 2019 19:31:01 +0200 Subject: [PATCH 0069/8077] [HttpClient] add words about HTTPlug --- components/http_client.rst | 124 +++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 12 deletions(-) diff --git a/components/http_client.rst b/components/http_client.rst index c8405954bba..9bf24ed9657 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -609,14 +609,57 @@ regular expression applied to relative URLs:: 'https://api\.github\.com/' ); -PSR-18 Compatibility --------------------- +Interoperability +---------------- + +The component is interoperable with 3 different abstractions for HTTP clients: +`Symfony Contracts`_, `PSR-18`_ and `HTTPlug`_ v1 and v2. If your app uses +libraries that need any of them, the component is compatible with them. +They also benefit from autowiring aliases when the +:ref:`framework bundle ` is used. + +If you are writing or maintaining a library that makes HTTP requests, you can +decouple it from any specific HTTP client implementations by coding against +either Symfony Contracts (recommended) or PSR-18 (which superseded HTTPlug). + +Symfony Contracts +~~~~~~~~~~~~~~~~~ + +The interfaces found in the ``symfony/http-client-contracts`` package define +the primary abstractions implemented by the component. Its entry point is the +:class:`Symfony\\Contracts\\HttpClient\\HttpClientInterface`. That's the +interface you need to code against when a client is needed:: -This component uses and implements abstractions defined by the -``symfony/http-client-contracts``. It also implements the `PSR-18`_ (HTTP Client) -specifications via the :class:`Symfony\\Component\\HttpClient\\Psr18Client` -class, which is an adapter to turn a Symfony ``HttpClientInterface`` into a -PSR-18 ``ClientInterface``. + use Symfony\Contracts\HttpClient\HttpClientInterface; + + class MyApiLayer + { + private $client; + + public function __construct(HttpClientInterface $client) + { + $this->client = $client + } + + // [...] + } + +All request options mentionned above (e.g. timeout management) are also defined +in the wordings of the interface, so that any compliant implementations (like this +component) is guaranteed to provide them. That's a major difference with the +PSR-18 abstraction, which provides none related to the transport itself. + +Another major feature covered by the Symfony Contracts is async/multiplexing, +as described in the previous sections. + +PSR-18 and PSR-17 +~~~~~~~~~~~~~~~~~ + +This component implements the `PSR-18`_ (HTTP Client) specifications via the +:class:`Symfony\\Component\\HttpClient\\Psr18Client` class, which is an adapter +to turn a Symfony ``HttpClientInterface`` into a PSR-18 ``ClientInterface``. +This class also implements the relevant methods of `PSR-17`_ to ease creating +request objects. To use it, you need the ``psr/http-client`` package and a `PSR-17`_ implementation: @@ -631,18 +674,73 @@ To use it, you need the ``psr/http-client`` package and a `PSR-17`_ implementati Now you can make HTTP requests with the PSR-18 client as follows:: - use Nyholm\Psr7\Factory\Psr17Factory; use Symfony\Component\HttpClient\Psr18Client; - $psr17Factory = new Psr17Factory(); - $psr18Client = new Psr18Client(); + $client = new Psr18Client(); $url = 'https://symfony.com/versions.json'; - $request = $psr17Factory->createRequest('GET', $url); - $response = $psr18Client->sendRequest($request); + $request = $client->createRequest('GET', $url); + $response = $client->sendRequest($request); $content = json_decode($response->getBody()->getContents(), true); +.. versionadded:: 4.4 + + The PSR-17 factory methods have been added to ``Psr18Client`` in Symfony 4.4. + +HTTPlug +~~~~~~~ + +.. versionadded:: 4.4 + + Support for HTTPlug was added in Symfony 4.4. + +The `HTTPlug`_ specification pre-dates and is superseded by PSR-18. As such, you +should not use it in newly written code. Yet, many libraries still require v1 or +v2 of it. The component is interoperable with them thanks to the ``HttplugClient`` +adapter class that it provides. Similarly to ``Psr18Client`` implementing +relevant parts of PSR-17, ``HttplugClient`` also implements the factory methods +defined in the related ``php-http/message-factory`` package. + +Internally, the implementation relies on the ``Psr18Client``, so that the +``psr/http-client`` package is needed to use this class:: + +.. code-block:: terminal + + # Let's suppose php-http/httplug is already required by the lib you want to use + + # installs the PSR-18 ClientInterface + $ composer require psr/http-client + + # installs an efficient implementation of response and stream factories + # with autowiring aliases provided by Symfony Flex + $ composer require nyholm/psr7 + +Let's say you want to instantiate a class with the following constructor, +that requires HTTPlug dependencies:: + + use Http\Client\HttpClient; + use Http\Message\RequestFactory; + use Http\Message\StreamFactory; + + class SomeSdk + { + public function __construct( + HttpClient $httpClient, + RequestFactory $requestFactory, + StreamFactory $streamFactory + ) + // [...] + } + +Because ``HttplugClient`` implements the 3 interfaces, you can use it this way:: + + use Symfony\Component\HttpClient\HttplugClient; + + $httpClient = new HttplugClient(); + + $apiClient = new SomeSdk($httpClient, $httpClient, $httpClient); + Symfony Framework Integration ----------------------------- @@ -765,3 +863,5 @@ However, using ``MockResponse`` allows simulating chunked responses and timeouts .. _`cURL PHP extension`: https://php.net/curl .. _`PSR-17`: https://www.php-fig.org/psr/psr-17/ .. _`PSR-18`: https://www.php-fig.org/psr/psr-18/ +.. _`HTTPlug`: https://github.com/php-http/httplug/#readme +.. _`Symfony Contracts`: https://github.com/symfony/contracts From b6a8e9609e03b93a5f7964cbb1a60f80f8279504 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 24 Jun 2019 19:31:19 +0200 Subject: [PATCH 0070/8077] Use single quotes for example code --- service_container/tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 0e24af1f57c..1b60653fd56 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -425,7 +425,7 @@ use this, update the compiler:: foreach ($tags as $attributes) { $definition->addMethodCall('addTransport', [ new Reference($id), - $attributes["alias"] + $attributes['alias'] ]); } } From 0818acdb50eadb0a3bfa6e130accaca2bcfd3215 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 24 Jun 2019 19:31:01 +0200 Subject: [PATCH 0071/8077] [HttpClient] add section about interop + performance --- components/http_client.rst | 70 +++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/components/http_client.rst b/components/http_client.rst index c8405954bba..168697f6352 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -43,8 +43,18 @@ low-level HTTP client that makes requests, like the following ``GET`` request:: $content = $response->toArray(); // $content = ['id' => 521583, 'name' => 'symfony-docs', ...] +Performance +----------- + +The component is built for maximum HTTP performance. By design, it is compatible +with HTTP/2 and with doing concurrent asynchronous streamed and multiplexed +requests/responses. Even when doing regular synchronous calls, this design +allows keeping connections to remote hosts open between requests, improving +performance by saving repetitive DNS resolution, SSL negociation, etc. +To leverage all these design benefits, the cURL extension is needed. + Enabling cURL Support ---------------------- +~~~~~~~~~~~~~~~~~~~~~ This component supports both the native PHP streams and cURL to make the HTTP requests. Although both are interchangeable and provide the same features, @@ -68,7 +78,7 @@ not configurable and cURL will be used automatically if the cURL PHP extension is installed and enabled. Otherwise, the native PHP streams will be used. HTTP/2 Support --------------- +~~~~~~~~~~~~~~ When requesting an ``https`` URL, HTTP/2 is enabled by default if libcurl >= 7.36 is used. To force HTTP/2 for ``http`` URLs, you need to enable it explicitly via @@ -609,14 +619,55 @@ regular expression applied to relative URLs:: 'https://api\.github\.com/' ); -PSR-18 Compatibility --------------------- +Interoperability +---------------- + +The component is interoperable with 2 different abstractions for HTTP clients: +`Symfony Contracts`_ and `PSR-18`_. If your app uses +libraries that need any of them, the component is compatible with them. +They also benefit from autowiring aliases when the +:ref:`framework bundle ` is used. + +If you are writing or maintaining a library that makes HTTP requests, you can +decouple it from any specific HTTP client implementations by coding against +either Symfony Contracts (recommended) or PSR-18. + +Symfony Contracts +~~~~~~~~~~~~~~~~~ + +The interfaces found in the ``symfony/http-client-contracts`` package define +the primary abstractions implemented by the component. Its entry point is the +:class:`Symfony\\Contracts\\HttpClient\\HttpClientInterface`. That's the +interface you need to code against when a client is needed:: + + use Symfony\Contracts\HttpClient\HttpClientInterface; + + class MyApiLayer + { + private $client; + + public function __construct(HttpClientInterface $client) + { + $this->client = $client + } + + // [...] + } + +All request options mentionned above (e.g. timeout management) are also defined +in the wordings of the interface, so that any compliant implementations (like this +component) is guaranteed to provide them. That's a major difference with the +PSR-18 abstraction, which provides none related to the transport itself. + +Another major feature covered by the Symfony Contracts is async/multiplexing, +as described in the previous sections. + +PSR-18 +~~~~~~ -This component uses and implements abstractions defined by the -``symfony/http-client-contracts``. It also implements the `PSR-18`_ (HTTP Client) -specifications via the :class:`Symfony\\Component\\HttpClient\\Psr18Client` -class, which is an adapter to turn a Symfony ``HttpClientInterface`` into a -PSR-18 ``ClientInterface``. +This component implements the `PSR-18`_ (HTTP Client) specifications via the +:class:`Symfony\\Component\\HttpClient\\Psr18Client` class, which is an adapter +to turn a Symfony ``HttpClientInterface`` into a PSR-18 ``ClientInterface``. To use it, you need the ``psr/http-client`` package and a `PSR-17`_ implementation: @@ -765,3 +816,4 @@ However, using ``MockResponse`` allows simulating chunked responses and timeouts .. _`cURL PHP extension`: https://php.net/curl .. _`PSR-17`: https://www.php-fig.org/psr/psr-17/ .. _`PSR-18`: https://www.php-fig.org/psr/psr-18/ +.. _`Symfony Contracts`: https://github.com/symfony/contracts From 773d015b7910a96f238f89f943ad2dd97992bfdc Mon Sep 17 00:00:00 2001 From: Michael Kopinsky Date: Mon, 24 Jun 2019 20:36:45 -0400 Subject: [PATCH 0072/8077] Fix broken link to doctrine documentation --- form/form_collections.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/form/form_collections.rst b/form/form_collections.rst index 8f62139f8ec..38700725174 100644 --- a/form/form_collections.rst +++ b/form/form_collections.rst @@ -715,6 +715,6 @@ the relationship between the removed ``Tag`` and ``Task`` object. elements of the collection. More advanced functionality like moving or duplicating an element in the collection and customizing the buttons is also possible. -.. _`Owning Side and Inverse Side`: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html +.. _`Owning Side and Inverse Side`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/unitofwork-associations.html .. _`JSFiddle`: http://jsfiddle.net/847Kf/4/ .. _`symfony-collection`: https://github.com/ninsuo/symfony-collection From 9c2a90bc4ce3b7403f605efbf6f4c6a5a03f2d7e Mon Sep 17 00:00:00 2001 From: makmaoui <37940572+makmaoui@users.noreply.github.com> Date: Mon, 24 Jun 2019 22:50:32 +0200 Subject: [PATCH 0073/8077] Missed code format --- components/browser_kit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/browser_kit.rst b/components/browser_kit.rst index 522dc035b9f..7eeec4ace52 100644 --- a/components/browser_kit.rst +++ b/components/browser_kit.rst @@ -105,7 +105,7 @@ simulate the link click:: If you need the :class:`Symfony\\Component\\DomCrawler\\Link` object that provides access to the link properties (e.g. ``$link->getMethod()``, -``$link->getUri()``), use this other method: +``$link->getUri()``), use this other method:: // ... $crawler = $client->request('GET', '/product/123'); From 4ff6247217c7b0712b84a168712c8399c18dcd93 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 25 Jun 2019 10:01:21 +0200 Subject: [PATCH 0074/8077] Minor tweaks --- components/http_client.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/http_client.rst b/components/http_client.rst index 168697f6352..226e7482916 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -50,7 +50,7 @@ The component is built for maximum HTTP performance. By design, it is compatible with HTTP/2 and with doing concurrent asynchronous streamed and multiplexed requests/responses. Even when doing regular synchronous calls, this design allows keeping connections to remote hosts open between requests, improving -performance by saving repetitive DNS resolution, SSL negociation, etc. +performance by saving repetitive DNS resolution, SSL negotiation, etc. To leverage all these design benefits, the cURL extension is needed. Enabling cURL Support @@ -622,10 +622,10 @@ regular expression applied to relative URLs:: Interoperability ---------------- -The component is interoperable with 2 different abstractions for HTTP clients: -`Symfony Contracts`_ and `PSR-18`_. If your app uses -libraries that need any of them, the component is compatible with them. -They also benefit from autowiring aliases when the +The component is interoperable with two different abstractions for HTTP clients: +`Symfony Contracts`_ and `PSR-18`_. If your application uses libraries that need +any of them, the component is compatible with both. They also benefit from +:ref:`autowiring aliases ` when the :ref:`framework bundle ` is used. If you are writing or maintaining a library that makes HTTP requests, you can @@ -654,10 +654,10 @@ interface you need to code against when a client is needed:: // [...] } -All request options mentionned above (e.g. timeout management) are also defined -in the wordings of the interface, so that any compliant implementations (like this -component) is guaranteed to provide them. That's a major difference with the -PSR-18 abstraction, which provides none related to the transport itself. +All request options mentioned above (e.g. timeout management) are also defined +in the wordings of the interface, so that any compliant implementations (like +this component) is guaranteed to provide them. That's a major difference with +the PSR-18 abstraction, which provides none related to the transport itself. Another major feature covered by the Symfony Contracts is async/multiplexing, as described in the previous sections. From 9fbebaf1adc6c0ffe3bc911311317db41e02a53e Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Sun, 23 Jun 2019 11:07:47 +0200 Subject: [PATCH 0075/8077] [Cache] Make sure the chain cache configuration works --- cache.rst | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/cache.rst b/cache.rst index a6435e2e2dd..4044b70f8ef 100644 --- a/cache.rst +++ b/cache.rst @@ -413,16 +413,22 @@ case the value needs to be recalculated. cache: pools: my_cache_pool: - adapter: app.my_cache_chain_adapter + adapter: cache.adapter.psr6 + provider: app.my_cache_chain_adapter cache.my_redis: adapter: cache.adapter.redis provider: 'redis://user:password@example.com' + cache.apcu: + adapter: cache.adapter.apcu + cache.array: + adapter: cache.adapter.array + services: app.my_cache_chain_adapter: class: Symfony\Component\Cache\Adapter\ChainAdapter arguments: - - ['cache.adapter.array', 'cache.my_redis', 'cache.adapter.file'] + - ['@cache.array', '@cache.apcu', '@cache.my_redis'] - 31536000 # One year .. code-block:: xml @@ -436,18 +442,20 @@ case the value needs to be recalculated. https://symfony.com/schema/dic/services/services-1.0.xsd"> - - + + + + - + + - 31536000 @@ -461,28 +469,37 @@ case the value needs to be recalculated. 'cache' => [ 'pools' => [ 'my_cache_pool' => [ - 'adapter' => 'app.my_cache_chain_adapter', + 'adapter' => 'cache.adapter.psr6', + 'provider' => 'app.my_cache_chain_adapter', ], 'cache.my_redis' => [ 'adapter' => 'cache.adapter.redis', 'provider' => 'redis://user:password@example.com', ], + 'cache.apcu' => [ + 'adapter' => 'cache.adapter.apcu', + ], + 'cache.array' => [ + 'adapter' => 'cache.adapter.array', + ], ], ], ]); $container->getDefinition('app.my_cache_chain_adapter', \Symfony\Component\Cache\Adapter\ChainAdapter::class) ->addArgument([ - new Reference('cache.adapter.array'), + new Reference('cache.array'), + new Reference('cache.apcu'), new Reference('cache.my_redis'), - new Reference('cache.adapter.file'), ]) ->addArgument(31536000); .. note:: - In this configuration there is a ``cache.my_redis`` pool that is used as an - adapter in the ``app.my_cache_chain_adapter`` + In this configuration the ``my_cache_pool`` pool is using the ``cache.adapter.psr6`` + adapter and the ``app.my_cache_chain_adapter`` service as a provider. That is + because ``ChainAdapter`` does not support the ``cache.pool`` tag. So it is decorated + with the ``ProxyAdapter``. Clearing the Cache From 66a474ff031c78b6e04a7ed510591a4b43bce07a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Jun 2019 17:22:36 +0200 Subject: [PATCH 0076/8077] [BrowserKit] Document the integration with HttpClient --- components/browser_kit.rst | 42 +++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/components/browser_kit.rst b/components/browser_kit.rst index 522dc035b9f..532efbabe60 100644 --- a/components/browser_kit.rst +++ b/components/browser_kit.rst @@ -10,9 +10,10 @@ The BrowserKit Component .. note:: - The BrowserKit component can only make internal requests to your application. - If you need to make requests to external sites and applications, consider - using `Goutte`_, a simple web scraper based on Symfony Components. + In Symfony versions prior to 4.3, the BrowserKit component could only make + internal requests to your application. Starting from Symfony 4.3, this + component can also :ref:`make HTTP requests to any public site ` + when using it in combination with the :doc:`HttpClient component `. Installation ------------ @@ -279,6 +280,41 @@ also delete all the cookies:: // reset the client (history and cookies are cleared too) $client->restart(); +.. _component-browserkit-external-requests: + +Making External HTTP Requests +----------------------------- + +So far, all the examples in this article have assumed that you are making +internal requests to your own application. However, you can run the exact same +examples when making HTTP requests to external web sites and applications. + +First, install and configure the :doc:`HttpClient component `. +Then, use the :class:`Symfony\\Component\\BrowserKit\\HttpBrowser` to create +the client that will make the external HTTP requests:: + + use Symfony\Component\BrowserKit\HttpBrowser; + use Symfony\Component\HttpClient\HttpClient; + + $browser = new HttpBrowser(HttpClient::create()); + +You can now use any of the methods shown in this article to extract information, +click links, submit forms, etc. This means that you no longer need to use a +dedicated web crawler or scraper such as `Goutte`_:: + + $browser = new HttpBrowser(HttpClient::create()); + + $browser->request('GET', 'https://github.com'); + $browser->clickLink('Sign in'); + $browser->submitForm('Sign in', ['login' => '...', 'password' => '...']); + $openPullRequests = trim($browser->clickLink('Pull requests')->filter( + '.table-list-header-toggle a:nth-child(1)' + )->text()); + +.. versionadded:: 4.3 + + The feature to make external HTTP requests was introduced in Symfony 4.3. + Learn more ---------- From 300efbe5d2e0216208a4f76f698a8d2965672515 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 26 Jun 2019 17:26:29 +0200 Subject: [PATCH 0077/8077] Fixed a broken reference --- setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.rst b/setup.rst index 6d334f33869..96f0b0ec9d2 100644 --- a/setup.rst +++ b/setup.rst @@ -39,7 +39,7 @@ Running your Symfony Application On production, you should use a web server like Nginx or Apache (see :doc:`configuring a web server to run Symfony `). But for development, it's more convenient to use the -:doc:`Symfony Local Web Server `. +:doc:`Symfony Local Web Server `. This local server provides support for HTTP/2, TLS/SSL, automatic generation of security certificates and many other features. It works with any PHP application, From 882de65204a034b04418ab303d18fd1acb2ff2c1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 26 Jun 2019 17:46:27 +0200 Subject: [PATCH 0078/8077] Update the PHP requirements for Symfony 3.4 version --- reference/requirements.rst | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/reference/requirements.rst b/reference/requirements.rst index 6c1934cac31..a252ee07bab 100644 --- a/reference/requirements.rst +++ b/reference/requirements.rst @@ -7,11 +7,15 @@ Requirements for Running Symfony ================================ Symfony 3.4 requires **PHP 5.5.9** or higher to run, in addition to other minor -requirements. To make things simple, Symfony provides a tool to quickly check if -your system meets all those requirements. +requirements, when using the traditional installation based on the +`Symfony Standard Edition`_. If Symfony 3.4 is installed via the `skeleton`_ or +`website-skeleton`_ (which is the recommended way for modern Symfony +applications) the requirements are **PHP 7.0.8** or higher. -Beware that PHP can define a different configuration for the command console and -the web server, so you need to check requirements in both environments. +To make things simple, Symfony provides a tool to quickly check if your system +meets all those requirements. Beware that PHP can define a different +configuration for the command console and the web server, so you need to check +the requirements in both environments. Checking Requirements for the Web Server ---------------------------------------- @@ -32,3 +36,7 @@ command and fix the reported issues: $ cd my-project/ $ php bin/symfony_requirements + +.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard +.. _`skeleton`: https://github.com/symfony/skeleton +.. _`website-skeleton`: https://github.com/symfony/website-skeleton From c08a0e3f2acc8179d042def1f6424d1cf2aeb4f7 Mon Sep 17 00:00:00 2001 From: peaceant Date: Wed, 26 Jun 2019 14:23:51 +0200 Subject: [PATCH 0079/8077] Fix new instance of a class name --- components/cache/psr6_psr16_adapters.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cache/psr6_psr16_adapters.rst b/components/cache/psr6_psr16_adapters.rst index 55014b9faba..b390c62f604 100644 --- a/components/cache/psr6_psr16_adapters.rst +++ b/components/cache/psr6_psr16_adapters.rst @@ -80,7 +80,7 @@ this use-case:: $psr6Cache = new FilesystemAdapter(); // a PSR-16 cache that uses your cache internally! - $psr16Cache = new Psr6Cache($psr6Cache); + $psr16Cache = new Psr16Cache($psr6Cache); // now use this wherever you want $githubApiClient = new GitHubApiClient($psr16Cache); From cd5d6338546bf1f86f891de1049350ba0b18a978 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 27 Jun 2019 08:38:02 +0200 Subject: [PATCH 0080/8077] Minor tweak --- components/cache/psr6_psr16_adapters.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cache/psr6_psr16_adapters.rst b/components/cache/psr6_psr16_adapters.rst index b390c62f604..0019ce50df3 100644 --- a/components/cache/psr6_psr16_adapters.rst +++ b/components/cache/psr6_psr16_adapters.rst @@ -74,7 +74,7 @@ the class instead. No problem! The Cache component provides the this use-case:: use Symfony\Component\Cache\Adapter\FilesystemAdapter; - use Symfony\Component\Cache\Simple\Psr6Cache; + use Symfony\Component\Cache\Simple\Psr16Cache; // the PSR-6 cache object that you want to use $psr6Cache = new FilesystemAdapter(); From d51657f90903080af5cf40c09a70cef00e43c442 Mon Sep 17 00:00:00 2001 From: gdembowski Date: Thu, 20 Jun 2019 08:22:09 +0200 Subject: [PATCH 0081/8077] Update name of argument --- form/data_mappers.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/form/data_mappers.rst b/form/data_mappers.rst index 62d5d700b65..c0dce37281e 100644 --- a/form/data_mappers.rst +++ b/form/data_mappers.rst @@ -96,30 +96,30 @@ in your form type:: // ... /** - * @param Color|null $data + * @param Color|null $viewData */ - public function mapDataToForms($data, $forms) + public function mapDataToForms($viewData, $forms) { // there is no data yet, so nothing to prepopulate - if (null === $data) { + if (null === $viewData) { return; } // invalid data type - if (!$data instanceof Color) { - throw new UnexpectedTypeException($data, Color::class); + if (!$viewData instanceof Color) { + throw new UnexpectedTypeException($viewData, Color::class); } /** @var FormInterface[] $forms */ $forms = iterator_to_array($forms); // initialize form field values - $forms['red']->setData($data->getRed()); - $forms['green']->setData($data->getGreen()); - $forms['blue']->setData($data->getBlue()); + $forms['red']->setData($viewData->getRed()); + $forms['green']->setData($viewData->getGreen()); + $forms['blue']->setData($viewData->getBlue()); } - public function mapFormsToData($forms, &$data) + public function mapFormsToData($forms, &$viewData) { /** @var FormInterface[] $forms */ $forms = iterator_to_array($forms); @@ -127,7 +127,7 @@ in your form type:: // as data is passed by reference, overriding it will change it in // the form object as well // beware of type inconsistency, see caution below - $data = new Color( + $viewData = new Color( $forms['red']->getData(), $forms['green']->getData(), $forms['blue']->getData() From 22a3ea4d96c4d884f7e9c268187fd893666a1e73 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 27 Jun 2019 10:51:26 +0200 Subject: [PATCH 0082/8077] [Intl] Mention that all methods trigger exceptions --- components/intl.rst | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/components/intl.rst b/components/intl.rst index a10d8102a8e..4255de60a7f 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -88,7 +88,9 @@ which defaults to the current default locale:: $language = Languages::getName('fr', 'de'); // => 'Französisch' -You can also check if a given language code is valid:: +If the given locale doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given language code is valid:: $isValidLanguage = Languages::exists($languageCode); @@ -121,7 +123,9 @@ which defaults to the current default locale:: $language = Scripts::getName('Hans', 'de'); // => 'Vereinfacht' -You can also check if a given script code is valid:: +If the given script code doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given script code is valid:: $isValidScript = Scripts::exists($scriptCode); @@ -156,7 +160,9 @@ which defaults to the current default locale:: $country = Countries::getName('GB', 'de'); // => 'Vereinigtes Königreich' -You can also check if a given country code is valid:: +If the given country code doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given country code is valid:: $isValidCountry = Countries::exists($countryCode); @@ -192,7 +198,9 @@ which defaults to the current default locale:: $locale = Locales::getName('zh_Hans_MO', 'de'); // => 'Chinesisch (Vereinfacht, Sonderverwaltungsregion Macau)' -You can also check if a given locale code is valid:: +If the given locale code doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given locale code is valid:: $isValidLocale = Locales::exists($localeCode); @@ -236,7 +244,9 @@ the current default locale:: $currency = Currencies::getName('INR', 'de'); // => 'Indische Rupie' -You can also check if a given currency code is valid:: +If the given currency code doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given currency code is valid:: $isValidCurrency = Currencies::exists($currencyCode); @@ -314,7 +324,9 @@ you can pass the locale as the third optional argument:: $offset = Timezones::getGmtOffset('Europe/Madrid', strtotime('October 28, 2019'), 'ar')); // $offset = 'غرينتش+01:00' $offset = Timezones::getGmtOffset('Europe/Madrid', strtotime('October 28, 2019'), 'dz')); // $offset = 'ཇི་ཨེམ་ཏི་+01:00' -Finally, you can also check if a given timezone ID is valid:: +If the given timezone ID doesn't exist, the methods trigger a +:class:`Symfony\\Component\\Intl\\Exception\\MissingResourceException`. In addition +to catching the exception, you can also check if a given timezone ID is valid:: $isValidTimezone = Timezones::exists($timezoneId); From d2080805f69519289aea6b838d821b867a73e91d Mon Sep 17 00:00:00 2001 From: Dan Abrey Date: Wed, 26 Jun 2019 17:30:02 +0100 Subject: [PATCH 0083/8077] Provide a link to Symfony client download instructions Make it clearer how to install the Symfony web server directly from the Installing & Setting up the Symfony Framework page. --- setup.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.rst b/setup.rst index 96f0b0ec9d2..2e55fcedddc 100644 --- a/setup.rst +++ b/setup.rst @@ -153,6 +153,7 @@ Go Deeper with Setup .. _`Stellar Development with Symfony`: http://symfonycasts.com/screencast/symfony .. _`Composer`: https://getcomposer.org/ .. _`installing Composer`: https://getcomposer.org/download/ +.. _`Download the Symfony client`: https://symfony.com/download .. _`the Security Checker`: https://github.com/sensiolabs/security-checker#integration .. _`The Symfony Demo application`: https://github.com/symfony/demo .. _`symfony/symfony-demo`: https://github.com/symfony/demo From f71ab806eece35cf049497601d3aae698950196e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 27 Jun 2019 11:42:08 +0200 Subject: [PATCH 0084/8077] Minor reword --- setup.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.rst b/setup.rst index 2e55fcedddc..2d882e97332 100644 --- a/setup.rst +++ b/setup.rst @@ -45,11 +45,12 @@ This local server provides support for HTTP/2, TLS/SSL, automatic generation of security certificates and many other features. It works with any PHP application, not only Symfony projects, so it's a very useful development tool. -Once installed, move into your new project and start the local web server: +`Download the Symfony local web server`_, install it, move into your new project +directory and start the local web server as follows: .. code-block:: terminal - $ cd my-project + $ cd my-project/ $ symfony server:start Open your browser and navigate to ``http://localhost:8000/``. If everything is @@ -153,7 +154,7 @@ Go Deeper with Setup .. _`Stellar Development with Symfony`: http://symfonycasts.com/screencast/symfony .. _`Composer`: https://getcomposer.org/ .. _`installing Composer`: https://getcomposer.org/download/ -.. _`Download the Symfony client`: https://symfony.com/download +.. _`Download the Symfony local web server`: https://symfony.com/download .. _`the Security Checker`: https://github.com/sensiolabs/security-checker#integration .. _`The Symfony Demo application`: https://github.com/symfony/demo .. _`symfony/symfony-demo`: https://github.com/symfony/demo From e0d6c11e0d502221089201c057bd2fdf7c87a7b2 Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Thu, 27 Jun 2019 12:29:07 +0200 Subject: [PATCH 0085/8077] Update workflow.rst --- components/workflow.rst | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/components/workflow.rst b/components/workflow.rst index 54db794696a..029b0d7dfe5 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -32,28 +32,30 @@ a ``Definition`` and a way to write the states to the objects (i.e. an instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`). Consider the following example for a blog post. A post can have one of a number -of predefined statuses (`draft`, `review`, `rejected`, `published`). In a workflow, +of predefined statuses (`draft`, `reviewed`, `rejected`, `published`). In a workflow, these statuses are called **places**. You can define the workflow like this:: use Symfony\Component\Workflow\DefinitionBuilder; - use Symfony\Component\Workflow\MarkingStore\SingleStateMarkingStore; + use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore; use Symfony\Component\Workflow\Transition; use Symfony\Component\Workflow\Workflow; $definitionBuilder = new DefinitionBuilder(); - $definition = $definitionBuilder->addPlaces(['draft', 'review', 'rejected', 'published']) + $definition = $definitionBuilder->addPlaces(['draft', 'reviewed', 'rejected', 'published']) // Transitions are defined with a unique name, an origin place and a destination place - ->addTransition(new Transition('to_review', 'draft', 'review')) - ->addTransition(new Transition('publish', 'review', 'published')) - ->addTransition(new Transition('reject', 'review', 'rejected')) + ->addTransition(new Transition('to_review', 'draft', 'reviewed')) + ->addTransition(new Transition('publish', 'reviewed', 'published')) + ->addTransition(new Transition('reject', 'reviewed', 'rejected')) ->build() ; - $marking = new SingleStateMarkingStore('currentState'); + $singleState = true; // true if the subject can be in only one state at a given time + $property = 'currentState' // subject property name where the state is stored + $marking = new MethodMarkingStore($singleState, $property); $workflow = new Workflow($definition, $marking); -The ``Workflow`` can now help you to decide what actions are allowed -on a blog post depending on what *place* it is in. This will keep your domain +The ``Workflow`` can now help you to decide what *transitions* (actions) are allowed +on a blog post depending on what *place* (state) it is in. This will keep your domain logic in one place and not spread all over your application. When you define multiple workflows you should consider using a ``Registry``, @@ -66,11 +68,11 @@ are trying to use it with:: use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\SupportStrategy\InstanceOfSupportStrategy; - $blogWorkflow = ... + $blogPostWorkflow = ... $newsletterWorkflow = ... $registry = new Registry(); - $registry->addWorkflow($blogWorkflow, new InstanceOfSupportStrategy(BlogPost::class)); + $registry->addWorkflow($blogPostWorkflow, new InstanceOfSupportStrategy(BlogPost::class)); $registry->addWorkflow($newsletterWorkflow, new InstanceOfSupportStrategy(Newsletter::class)); Usage @@ -80,17 +82,17 @@ When you have configured a ``Registry`` with your workflows, you can retrieve a workflow from it and use it as follows:: // ... - // Consider that $post is in state "draft" by default - $post = new BlogPost(); - $workflow = $registry->get($post); + // Consider that $blogPost is in place "draft" by default + $blogPost = new BlogPost(); + $workflow = $registry->get($blogPost); - $workflow->can($post, 'publish'); // False - $workflow->can($post, 'to_review'); // True + $workflow->can($blogPost, 'publish'); // False + $workflow->can($blogPost, 'to_review'); // True - $workflow->apply($post, 'to_review'); // $post is now in state "review" + $workflow->apply($blogPost, 'to_review'); // $blogPost is now in place "reviewed" - $workflow->can($post, 'publish'); // True - $workflow->getEnabledTransitions($post); // ['publish', 'reject'] + $workflow->can($blogPost, 'publish'); // True + $workflow->getEnabledTransitions($blogPost); // $blogPost can perform transition "publish" or "reject" Learn more ---------- From d321c5ae241723d18acc6279cc49b1e3f9c734ab Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Jun 2019 11:59:13 +0200 Subject: [PATCH 0086/8077] Reworded the file upload article to use unmapped fields --- controller/upload_file.rst | 278 +++++++++++-------------------------- 1 file changed, 84 insertions(+), 194 deletions(-) diff --git a/controller/upload_file.rst b/controller/upload_file.rst index 84c7c5b93c9..2072c4756f7 100644 --- a/controller/upload_file.rst +++ b/controller/upload_file.rst @@ -12,14 +12,13 @@ How to Upload Files integrated with Doctrine ORM, MongoDB ODM, PHPCR ODM and Propel. Imagine that you have a ``Product`` entity in your application and you want to -add a PDF brochure for each product. To do so, add a new property called ``brochure`` -in the ``Product`` entity:: +add a PDF brochure for each product. To do so, add a new property called +``brochureFilename`` in the ``Product`` entity:: // src/AppBundle/Entity/Product.php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; - use Symfony\Component\Validator\Constraints as Assert; class Product { @@ -27,29 +26,30 @@ in the ``Product`` entity:: /** * @ORM\Column(type="string") - * - * @Assert\NotBlank(message="Please, upload the product brochure as a PDF file.") - * @Assert\File(mimeTypes={ "application/pdf" }) */ - private $brochure; + private $brochureFilename; - public function getBrochure() + public function getBrochureFilename() { - return $this->brochure; + return $this->brochureFilename; } - public function setBrochure($brochure) + public function setBrochureFilename($brochureFilename) { - $this->brochure = $brochure; + $this->brochureFilename = $brochureFilename; return $this; } } -Note that the type of the ``brochure`` column is ``string`` instead of ``binary`` -or ``blob`` because it just stores the PDF file name instead of the file contents. +Note that the type of the ``brochureFilename`` column is ``string`` instead of +``binary`` or ``blob`` because it only stores the PDF file name instead of the +file contents. -Then, add a new ``brochure`` field to the form that manages the ``Product`` entity:: +The next step is to add a new field to the form that manages the ``Product`` +entity. This must be a ``FileType`` field so the browsers can display the file +upload widget. The trick to make it work is to add the form field as "unmapped", +so Symfony doesn't try to get/set its value from the related entity:: // src/AppBundle/Form/ProductType.php namespace AppBundle\Form; @@ -59,6 +59,7 @@ Then, add a new ``brochure`` field to the form that manages the ``Product`` enti use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; + use Symfony\Component\Validator\Constraints\File; class ProductType extends AbstractType { @@ -66,7 +67,29 @@ Then, add a new ``brochure`` field to the form that manages the ``Product`` enti { $builder // ... - ->add('brochure', FileType::class, ['label' => 'Brochure (PDF file)']) + ->add('brochure', FileType::class, [ + 'label' => 'Brochure (PDF file)', + + // unmapped means that this field is not associated to any entity property + 'mapped' => false, + + // make it optional so you don't have to re-upload the PDF file + // everytime you edit the Product details + 'required' => false, + + // unmapped fields can't define their validation using annotations + // in the associated entity, so you can use the PHP constraint classes + 'constraints' => [ + new File([ + 'maxSize' => '1024k', + 'mimeTypes' => [ + 'application/pdf', + 'application/x-pdf', + ], + 'mimeTypesMessage' => 'Please upload a valid PDF document', + ]) + ], + ]) // ... ; } @@ -103,6 +126,7 @@ Finally, you need to update the code of the controller that handles the form:: use AppBundle\Form\ProductType; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\File\Exception\FileException; + use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; @@ -118,26 +142,32 @@ Finally, you need to update the code of the controller that handles the form:: $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - // $file stores the uploaded PDF file - /** @var Symfony\Component\HttpFoundation\File\UploadedFile $file */ - $file = $product->getBrochure(); - - $fileName = $this->generateUniqueFileName().'.'.$file->guessExtension(); - - // Move the file to the directory where brochures are stored - try { - $file->move( - $this->getParameter('brochures_directory'), - $fileName - ); - } catch (FileException $e) { - // ... handle exception if something happens during file upload + /** @var UploadedFile $brochureFile */ + $brochureFile = $form['brochure']->getData(); + + // this condition is needed because the 'brochure' field is not required + // so the PDF file must be processed only when a file is uploaded + if ($brochureFile) { + $originalFilename = pathinfo($brochureFile->getClientOriginalName(), PATHINFO_FILENAME); + // this is needed to safely include the file name as part of the URL + $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename); + $newFilename = $safeFilename.'-'.uniqid().'.'.$brochureFile->guessExtension(); + + // Move the file to the directory where brochures are stored + try { + $brochureFile->move( + $this->getParameter('brochures_directory'), + $newFilename + ); + } catch (FileException $e) { + // ... handle exception if something happens during file upload + } + + // updates the 'brochureFilename' property to store the PDF file name + // instead of its contents + $product->setBrochureFilename($newFilename); } - // updates the 'brochure' property to store the PDF file name - // instead of its contents - $product->setBrochure($fileName); - // ... persist the $product variable or any other work return $this->redirect($this->generateUrl('app_product_list')); @@ -147,16 +177,6 @@ Finally, you need to update the code of the controller that handles the form:: 'form' => $form->createView(), ]); } - - /** - * @return string - */ - private function generateUniqueFileName() - { - // md5() reduces the similarity of the file names generated by - // uniqid(), which is based on timestamps - return md5(uniqid()); - } } Now, create the ``brochures_directory`` parameter that was used in the @@ -172,9 +192,6 @@ controller to specify the directory in which the brochures should be stored: There are some important things to consider in the code of the above controller: -#. When the form is uploaded, the ``brochure`` property contains the whole PDF - file contents. Since this property stores just the file name, you must set - its new value before persisting the changes of the entity; #. In Symfony applications, uploaded files are objects of the :class:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile` class. This class provides methods for the most common operations when dealing with uploaded files; @@ -193,7 +210,7 @@ You can use the following code to link to the PDF brochure of a product: .. code-block:: html+twig - View brochure (PDF) + View brochure (PDF) .. tip:: @@ -206,8 +223,8 @@ You can use the following code to link to the PDF brochure of a product: use Symfony\Component\HttpFoundation\File\File; // ... - $product->setBrochure( - new File($this->getParameter('brochures_directory').'/'.$product->getBrochure()) + $product->setBrochureFilename( + new File($this->getParameter('brochures_directory').'/'.$product->getBrochureFilename()) ); Creating an Uploader Service @@ -233,7 +250,9 @@ logic to a separate service:: public function upload(UploadedFile $file) { - $fileName = md5(uniqid()).'.'.$file->guessExtension(); + $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); + $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename); + $fileName = $safeFilename.'-'.uniqid().'.'.$file->guessExtension(); try { $file->move($this->getTargetDirectory(), $fileName); @@ -299,10 +318,12 @@ Now you're ready to use this service in the controller:: // ... if ($form->isSubmitted() && $form->isValid()) { - $file = $product->getBrochure(); - $fileName = $fileUploader->upload($file); - - $product->setBrochure($fileName); + /** @var UploadedFile $brochureFile */ + $brochureFile = $form['brochure']->getData(); + if ($brochureFile) { + $brochureFileName = $fileUploader->upload($brochureFile); + $product->setBrochureFilename($brochureFileName); + } // ... } @@ -313,147 +334,16 @@ Now you're ready to use this service in the controller:: Using a Doctrine Listener ------------------------- -If you are using Doctrine to store the Product entity, you can create a -:doc:`Doctrine listener ` to -automatically move the file when persisting the entity:: - - // src/AppBundle/EventListener/BrochureUploadListener.php - namespace AppBundle\EventListener; - - use AppBundle\Entity\Product; - use AppBundle\Service\FileUploader; - use Doctrine\ORM\Event\LifecycleEventArgs; - use Doctrine\ORM\Event\PreUpdateEventArgs; - use Symfony\Component\HttpFoundation\File\File; - use Symfony\Component\HttpFoundation\File\UploadedFile; - - class BrochureUploadListener - { - private $uploader; - - public function __construct(FileUploader $uploader) - { - $this->uploader = $uploader; - } - - public function prePersist(LifecycleEventArgs $args) - { - $entity = $args->getEntity(); - - $this->uploadFile($entity); - } - - public function preUpdate(PreUpdateEventArgs $args) - { - $entity = $args->getEntity(); - - $this->uploadFile($entity); - } - - private function uploadFile($entity) - { - // upload only works for Product entities - if (!$entity instanceof Product) { - return; - } - - $file = $entity->getBrochure(); - - // only upload new files - if ($file instanceof UploadedFile) { - $fileName = $this->uploader->upload($file); - $entity->setBrochure($fileName); - } elseif ($file instanceof File) { - // prevents the full file path being saved on updates - // as the path is set on the postLoad listener - $entity->setBrochure($file->getFilename()); - } - } - } - -Now, register this class as a Doctrine listener: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - _defaults: - # ... be sure autowiring is enabled - autowire: true - # ... - - AppBundle\EventListener\BrochureUploadListener: - tags: - - { name: doctrine.event_listener, event: prePersist } - - { name: doctrine.event_listener, event: preUpdate } - - .. code-block:: xml - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/services.php - use AppBundle\EventListener\BrochureUploaderListener; - - $container->autowire(BrochureUploaderListener::class) - ->addTag('doctrine.event_listener', [ - 'event' => 'prePersist', - ]) - ->addTag('doctrine.event_listener', [ - 'event' => 'preUpdate', - ]) - ; - -This listener is now automatically executed when persisting a new Product -entity. This way, you can remove everything related to uploading from the -controller. - -.. tip:: - - This listener can also create the ``File`` instance based on the path when - fetching entities from the database:: - - // ... - use Symfony\Component\HttpFoundation\File\File; - - // ... - class BrochureUploadListener - { - // ... - - public function postLoad(LifecycleEventArgs $args) - { - $entity = $args->getEntity(); +The previous versions of this article explained how to handle file uploads using +:doc:`Doctrine listeners `. However, this +is no longer recommended, because Doctrine events shouldn't be used for your +domain logic. - if (!$entity instanceof Product) { - return; - } - - if ($fileName = $entity->getBrochure()) { - $entity->setBrochure(new File($this->uploader->getTargetDirectory().'/'.$fileName)); - } - } - } +Moreover, Doctrine listeners are often dependent on internal Doctrine behaviour +which may change in future versions. Also, they can introduce performance issues +unawarely (because your listener persists entities which cause other entities to +be changed and persisted). - After adding these lines, configure the listener to also listen for the - ``postLoad`` event. +As an alternative, you can use :doc:`Symfony events, listeners and subscribers `. .. _`VichUploaderBundle`: https://github.com/dustin10/VichUploaderBundle From e1521a00e6bba542191db38e3ce0ddfcc60f2563 Mon Sep 17 00:00:00 2001 From: Edouard Lescot Date: Thu, 27 Jun 2019 15:47:28 +0200 Subject: [PATCH 0087/8077] Update controllers.rst Do not add annotation routing configuration if your project uses Symfony Flex since the recipe already created the annotations.yaml file with the config inside. --- best_practices/controllers.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index c2e9827b32e..6288a303db7 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -47,14 +47,14 @@ this suffix is neither required nor recommended, so you can safely remove it. Routing Configuration --------------------- -To load routes defined as annotations in your controllers, add the following -configuration to the main routing configuration file: +To load routes defined as annotations in your controllers and if you are not using Symfony Flex, add the following +configuration to the annotation routing configuration file: .. code-block:: yaml - # config/routes.yaml + # config/routes/annotations.yaml controllers: - resource: '../src/Controller/' + resource: '../../src/Controller/' type: annotation This configuration will load annotations from any controller stored inside the From 07da28b0f58a29c5db137119d7f0a88d1e8b3454 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 27 Jun 2019 17:08:39 +0200 Subject: [PATCH 0088/8077] added Yonel as a core team member --- contributing/code/core_team.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index b547218753c..21167464b7e 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -92,6 +92,9 @@ Active Core Members * **Samuel Rozé** (`sroze`_) can merge into the Messenger_ component. + * **Yonel Ceruto** (`yceruto`_) can merge into the ErrorHandler_, + OptionsResolver_, and Form_ components and TwigBundle_. + * **Deciders Team** (``@symfony/deciders`` on GitHub): * **Jordi Boggiano** (`seldaek`_); @@ -207,6 +210,7 @@ discretion of the **Project Leader**. .. _DoctrineBridge: https://github.com/symfony/doctrine-bridge .. _EventDispatcher: https://github.com/symfony/event-dispatcher .. _DomCrawler: https://github.com/symfony/dom-crawler +.. _ErrorHandler: https://github.com/symfony/error-handler .. _Form: https://github.com/symfony/form .. _HttpFoundation: https://github.com/symfony/http-foundation .. _HttpKernel: https://github.com/symfony/http-kernel @@ -227,6 +231,7 @@ discretion of the **Project Leader**. .. _SecurityBundle: https://github.com/symfony/security-bundle .. _Stopwatch: https://github.com/symfony/stopwatch .. _TwigBridge: https://github.com/symfony/twig-bridge +.. _TwigBundle: https://github.com/symfony/twig-bundle .. _Validator: https://github.com/symfony/validator .. _VarDumper: https://github.com/symfony/var-dumper .. _Workflow: https://github.com/symfony/workflow @@ -252,6 +257,7 @@ discretion of the **Project Leader**. .. _`ogizanagi`: https://github.com/ogizanagi/ .. _`Nyholm`: https://github.com/Nyholm .. _`sroze`: https://github.com/sroze +.. _`yceruto`: https://github.com/yceruto .. _`michaelcullum`: https://github.com/michaelcullum .. _`wouterj`: https://github.com/wouterj .. _`HeahDude`: https://github.com/HeahDude From dcee1d3c76e2665e75f0651e1f1e29fc2bab4cd6 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 27 Jun 2019 14:35:05 -0400 Subject: [PATCH 0089/8077] Rename ErrorHandler to ErrorCatcher --- contributing/code/core_team.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index 21167464b7e..7c7231b4e05 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -92,7 +92,7 @@ Active Core Members * **Samuel Rozé** (`sroze`_) can merge into the Messenger_ component. - * **Yonel Ceruto** (`yceruto`_) can merge into the ErrorHandler_, + * **Yonel Ceruto** (`yceruto`_) can merge into the ErrorCatcher_, OptionsResolver_, and Form_ components and TwigBundle_. * **Deciders Team** (``@symfony/deciders`` on GitHub): @@ -210,7 +210,7 @@ discretion of the **Project Leader**. .. _DoctrineBridge: https://github.com/symfony/doctrine-bridge .. _EventDispatcher: https://github.com/symfony/event-dispatcher .. _DomCrawler: https://github.com/symfony/dom-crawler -.. _ErrorHandler: https://github.com/symfony/error-handler +.. _ErrorCatcher: https://github.com/symfony/error-catcher .. _Form: https://github.com/symfony/form .. _HttpFoundation: https://github.com/symfony/http-foundation .. _HttpKernel: https://github.com/symfony/http-kernel From 7d88725acfd5d7bbb041da668290d7a26df0917d Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Fri, 28 Jun 2019 08:49:08 +0200 Subject: [PATCH 0090/8077] Update introduction.rst --- workflow/introduction.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/workflow/introduction.rst b/workflow/introduction.rst index 58322183201..c8302870ac1 100644 --- a/workflow/introduction.rst +++ b/workflow/introduction.rst @@ -239,6 +239,7 @@ In a Symfony application using the you can get this state machine by injecting the Workflow registry service:: // ... + use App\Entity\PullRequest; use Symfony\Component\Workflow\Registry; class SomeService @@ -250,10 +251,10 @@ you can get this state machine by injecting the Workflow registry service:: $this->workflows = $workflows; } - public function someMethod($subject) + public function someMethod(PullRequest $pullRequest) { - $stateMachine = $this->workflows->get($subject, 'pull_request'); - $stateMachine->apply($subject, 'wait_for_review'); + $stateMachine = $this->workflows->get($pullRequest, 'pull_request'); + $stateMachine->apply($pullRequest, 'wait_for_review'); // ... } @@ -267,6 +268,7 @@ or ``state_machine.pull_request`` respectively in your service definitions to access the proper service:: // ... + use App\Entity\PullRequest; use Symfony\Component\Workflow\StateMachine; class SomeService @@ -278,9 +280,9 @@ to access the proper service:: $this->stateMachine = $stateMachine; } - public function someMethod($subject) + public function someMethod(PullRequest $pullRequest) { - $this->stateMachine->apply($subject, 'wait_for_review', [ + $this->stateMachine->apply($pullRequest, 'wait_for_review', [ 'log_comment' => 'My logging comment for the wait for review transition.', ]); // ... From 9878335eef9d1fdeb35d0043740ff5c23c5b355a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9ia=20Bohner?= Date: Thu, 27 Jun 2019 23:42:39 -0300 Subject: [PATCH 0091/8077] [HttpClient] Fix code block --- components/http_client.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_client.rst b/components/http_client.rst index 61356d2b0f3..61da8d14649 100644 --- a/components/http_client.rst +++ b/components/http_client.rst @@ -713,7 +713,7 @@ relevant parts of PSR-17, ``HttplugClient`` also implements the factory methods defined in the related ``php-http/message-factory`` package. Internally, the implementation relies on the ``Psr18Client``, so that the -``psr/http-client`` package is needed to use this class:: +``psr/http-client`` package is needed to use this class: .. code-block:: terminal From ac08120d4985b9647e319f5b10b1cb2cb248391e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 17 Jun 2019 17:51:36 +0200 Subject: [PATCH 0092/8077] [Forms] Reworded the article about creating custom form types --- ...tom-type-postal-address-fragment-names.svg | 1 + .../form/form-custom-type-postal-address.svg | 1 + ...tom-type-postal-address-fragment-names.dia | Bin 0 -> 2578 bytes .../form/form-custom-type-postal-address.dia | Bin 0 -> 2075 bytes form/create_custom_field_type.rst | 599 +++++++++++------- form/create_form_type_extension.rst | 5 +- form/data_transformers.rst | 15 +- form/dynamic_form_modification.rst | 9 +- form/form_dependencies.rst | 150 +---- form/use_empty_data.rst | 6 +- 10 files changed, 417 insertions(+), 369 deletions(-) create mode 100644 _images/form/form-custom-type-postal-address-fragment-names.svg create mode 100644 _images/form/form-custom-type-postal-address.svg create mode 100644 _images/sources/form/form-custom-type-postal-address-fragment-names.dia create mode 100644 _images/sources/form/form-custom-type-postal-address.dia diff --git a/_images/form/form-custom-type-postal-address-fragment-names.svg b/_images/form/form-custom-type-postal-address-fragment-names.svg new file mode 100644 index 00000000000..9b6092c9808 --- /dev/null +++ b/_images/form/form-custom-type-postal-address-fragment-names.svg @@ -0,0 +1 @@ + diff --git a/_images/form/form-custom-type-postal-address.svg b/_images/form/form-custom-type-postal-address.svg new file mode 100644 index 00000000000..ab0fde8af3a --- /dev/null +++ b/_images/form/form-custom-type-postal-address.svg @@ -0,0 +1 @@ + diff --git a/_images/sources/form/form-custom-type-postal-address-fragment-names.dia b/_images/sources/form/form-custom-type-postal-address-fragment-names.dia new file mode 100644 index 0000000000000000000000000000000000000000..aebdadb41701a5dbfc9f99f809b3ed9135447dc8 GIT binary patch literal 2578 zcmV+t3hnhDiwFP!000021MOW~Z{kQ6e(zsl(XULU?w4VbQBO-Ft+d){se51M1sQON zXN--E=`{N=zkRE0hY*4Zi7#AoKyKIFeGk$_n8epTf^g3Z z;&CzwnkLJ5(koh( z?Z!!zq~3Qwy6%0v&0oE%vYV?X{p{&`?oWeJ8u))#ca81Kn74F257O;!XY*tc+9ug* z{;WwwKkna+Hf5`YZ4^&$K7QgKi$f~Ecf>mTR&O^ZwFe8{m9zbzH!wU z#%-8oNkaoDzFxcAYh8DmhLioC`=muhFZbajyBq#!>2wvnuUhK;d$JAV=!q%5 z+M2C%h{#_BY1#d6kMp!wUSK=1q|2b^`Qt47{_ttzoVe!mCublT{S}O}a`;~}Kc4vM z#QVwnE&0)V3`=ZK5>Bprf8du+jqT2o+`C40 z5XT1+33czTh@@sJQh32I^(WzSvHz-3v-NsA>fE19UPyrO+k4M~AK9);^fV-V)~f1q z)w}d-d8_fc>nR!G`a72i)`iMMBOX9 z`J61%Fi1B~pDD^>~d-zT-=M(p? z59deWG@b=jmpq7%Dh zesIqn>+zsi9q}}Z-hH;n8lrI}h<3EfB{r?oxWmIG9>HcHxTSOVStCKPX+K+RY`}+Q zixi`$;W%qw4Z7Im@OdK*cIRn|@U_S;C?cAfa~0Vsi0sD~*>~75 zi0lwBow&%Zn#_kStqQ7SXCk}B<4Vc3$Zly|C?&fS*=rB@K#`q*s1Bk!i0UA!gQ&hG zs=KI*3Ia0KF-3LVR1iU>c1!3)SC#r4k0QyPsLqj5B*)j}yhQa+a~tA0W9oa0rG@Bw z%Q&9rq0frtK8M)~%C*NW*H)M=lxraqKlM9k&_;t) zjufUH)@wPEg31ABWxZC~UzKvrPTF^4^kIMU55+$RaOg9*JVjvyiuvkxMd=&{VfUxS)X8ip^?N& z4>s@(t%Kh^%l-MUFW%>55`f%w0&>@L^98vJym?`8g zi4h~5bSQV#9q?gtmjty7)GkoFAjt%^Yg_HIQI{leJJ=T0t^DPe_(EZmKh=QqZk%n;n0Ghk`SEeZz}MdAJ=JL= zyE_F&L=fJW95KaI>%O+eV~7i;20|mFWsWat9sKUYciVC1N5k^zG{c3{oSl(OgA5}3 zqeb?63>qSPP@Yd*d8UnQwiCiSAG6s4L7bWPA4k2$vojzubg+RhwV|NN0COM0+|^bK z%zaVHGi_wEo!cS7+~zl%4QbK-PK`C5KuiiU*uWR84nDBlJ7T##Rtqe5SuEGcel#lW zZ0M9z|&OLN8vx@8^Uy`%Bo9yYJchKvbigFSc^OS0EHOP}X8&I}ceeo=M*b*>fYpx~uUA<# zusX2%sjzz0LZn7^aEN>{j&dw3F>XIzcMp8SO65E5j1+DgKjDVt`1Ti_EJWHf=G~2{ z$;C`A6@`^Z?{g(mGaCu8wIwXvgoCkeektHB076ZngTU=MIvhow zAC9kke}Dh-PV_$9ym{~W>NoQ>j@865KM^L`*>&$eO{c$IT|GQJfMA}eG>Sps&p@KD z{!>Aqu1uq=-pw1wS?$16smiXES5=zE{&1FRCsY%C-5aXWk8vE$La$e}D%*{sAc~!z zD!A^wy~~f@RoTteT0eXGo~p4P##;TT?%Fknl!tUZ)$wMxlWCOrrb#-VZZ)asC;NA$ zP1!0jjl%KG+mGaJaZBZeWna5)v=StpsCev$+c;)!f?^^N5C|bN8B_}Ca>C(aPF@Z- z>=rKQ7B21<&QETqQJlugPq!h5Q50wu7N~bBNc7=A$w&pp#`cY?!7%RpG>uvsKymqL zxxIAhj$_~3zjD`FRP^%Td+Gh{f#)qvnKdyHc2~-VclVD zwW@mXdA6|8awE0)8YQU;K6+lPljOVUz9Q4uJC^EjFACG$_}O=yJ2mlx`E~E>cjuc5 z6X#DA$I(O2Nz!@dR*VBEy7?T5&G^~iNNo0LTQ0<4|k%K^b?eskMvYk5<+NmdgCzdVzpHkh1J z;0l-rE|4~HuQi$c36B7TAsVdU6SoO|_e^ey+YmQIB0C5r1am}LH^jZiYAtb}1##CS zxi3NVFuGT9TA7RDR>D;Vu$!5S07$aqlbpdrw8SIKkerDxjJ@`;xo-aNARBFKz%|oR z2wciIW+oM7l)w(sQ4_KW1_c+wM2nbWA}%%^9pdcMK*0P+=R#tbwsI-bMwo#S zkP?|E3IQdugD}H*cf<{z3n*lKu!1j;d{wzrp@%tvA8H4l6Dj>_lGq*_zO2j&SxM}* zNn+1ui?kIQ$AG(%X0!@9l1Ha;gU4uzhmagl0(gFo480?H{A{tc0oThG4a|Btj*nlw zyx8P$JDR(QeM@7lQ%4nBCi>1-@E34~(PQ7qP*ph3`?A%zlU3vP*tTUWn=_uvHi|#| zQ18eYc^mXj1b~pDJ|aL6NRLkMga9gXI12e7LuVBCQC1EYq)Ff$hPOKO@{t`p;vJ2N ziDwYc8)M$!294E^rpMVmZ-#wa@c5p^CR*tKHqhbtxnz>I0aPPtr5$cq60M5t6fk4# z-HGhkios02GofMRo>| zXoCM@k-h1FYl`g9it1KWx1zcg)vc($DXN>O3$Oob04q~iC|nW|2JL^8*x=yj>;AEze9%dDyIBr^licV?k)PKGl-$QZZp+yzk-TUq^-vU-(q zTfVYc;<>+P-pY&S*aQo1;j_Cbn=bH3Q8rz{e{E$`%iO19?rN*GuP45u*Ar{79BNez zV}N(MIfMd{v)mG2T9&gc_rh%-+g58??y^`8w^g~#^%n`abVV1j92FU!0Gx`Va+~2} z7r5MgRh8S%emb`W+tU_oD@@lGY;7uj<^@|wS`AVKAdqxeu@wL_!UhBv6Q zwoM0IuVM>bTd}njTU)WU8K$k+ZdPn15D?jl?P)8vv?W2maH_H+sS7?iaZw8T3w$ee zS*feOpkHORR_eMesf)EKbr}bSDZw?TvrB5V+0 zg9sZ$q#GW~4qp^}i~swYwcIYPSP|ZLu6-`JYji4AV|nuEcW* zrh}(*0T670Z&~gaW4Q{ewJdk0ELU8~;>Brpp*RiHT*o(WiWBoSj@9Jm&Hqw%7h`jc F002)30rmg@ literal 0 HcmV?d00001 diff --git a/form/create_custom_field_type.rst b/form/create_custom_field_type.rst index ec8c1773afb..e17c10013de 100644 --- a/form/create_custom_field_type.rst +++ b/form/create_custom_field_type.rst @@ -4,23 +4,30 @@ How to Create a Custom Form Field Type ====================================== -Symfony comes with a bunch of core field types available for building forms. -However there are situations where you may want to create a custom form field -type for a specific purpose. This article assumes you need a field definition -that holds a shipping option, based on the existing choice field. This section -explains how the field is defined and how you can customize its layout. - -Defining the Field Type ------------------------ - -In order to create the custom field type, first you have to create the class -representing the field. In this situation the class holding the field type -will be called ``ShippingType`` and the file will be stored in the default location -for form fields, which is ``App\Form\Type``. - -All field types must implement the :class:`Symfony\\Component\\Form\\FormTypeInterface`, +Symfony comes with :doc:`tens of form types ` (called +"form fields" in other projects) ready to use in your applications. However, +it's common to create custom form types to solve specific purposes in your +projects. + +Creating Form Types Based on Symfony Built-in Types +--------------------------------------------------- + +The easiest way to create a form type is to base it on one of the +:doc:`existing form types `. Imagine that your project +displays a list of "shipping options" as a `` + + + Pass an array of values:: @@ -495,6 +498,11 @@ Pass an array of values:: 'dimensional' => 'an other value', ]]); + // tick multiple checkboxes at once + $form->setValues(['multi' => [ + 'dimensional' => [1, 3] // it uses the input value to determine which checkbox to tick + ]]); + This is great, but it gets better! The ``Form`` object allows you to interact with your form like a browser, selecting radio values, ticking checkboxes, and uploading files:: From 4b76554f55ec3166f1b0dc8ee9ba628f80842051 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 6 Aug 2019 19:24:16 +0200 Subject: [PATCH 0290/8077] fix voting on multiple roles behavior description --- reference/twig_reference.rst | 3 +-- security/access_control.rst | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index bf01340a061..6c455c8b306 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -294,8 +294,7 @@ is_granted Returns ``true`` if the current user has the given role. If several roles are passed in an array, ``true`` is returned if the user has at least one of -them or all of them, depending on the -:ref:`Access Decision Manager strategy `. +them. Optionally, an object can be passed to be used by the voter. More information can be found in :ref:`security-template`. diff --git a/security/access_control.rst b/security/access_control.rst index 6d1db76f0c2..2d6d6fd37bc 100644 --- a/security/access_control.rst +++ b/security/access_control.rst @@ -134,9 +134,7 @@ options: * ``roles`` If the user does not have the given role, then access is denied (internally, an :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` is thrown). If this value is an array of multiple roles, the user must have - at least one of them (when using the default ``affirmative`` strategy in the - :ref:`Access Decision Manager `) - or all of them when using the ``unanimous`` strategy; + at least one of them. * ``allow_if`` If the expression returns false, then access is denied; From 4946c67ed7c0c0555e9c3aaafb1014b453e90db9 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Fri, 9 Aug 2019 11:29:06 +0200 Subject: [PATCH 0291/8077] Adding missing semicolon So I'm now a Symfony contributor right? :smirk: --- components/workflow.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/workflow.rst b/components/workflow.rst index 029b0d7dfe5..137695ddb0f 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -50,7 +50,7 @@ these statuses are called **places**. You can define the workflow like this:: ; $singleState = true; // true if the subject can be in only one state at a given time - $property = 'currentState' // subject property name where the state is stored + $property = 'currentState'; // subject property name where the state is stored $marking = new MethodMarkingStore($singleState, $property); $workflow = new Workflow($definition, $marking); From dcb0eaaae4676ce4cecb8fe4b2b247050e489ca3 Mon Sep 17 00:00:00 2001 From: Amrouche Hamza Date: Sun, 26 May 2019 18:20:28 +0200 Subject: [PATCH 0292/8077] [Console] allow the answer to not be trimmed --- components/console/helpers/questionhelper.rst | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index 997e56daf33..ee412d69527 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -216,6 +216,30 @@ provide a callback function to dynamically generate suggestions:: The ``setAutocompleterCallback()`` method was introduced in Symfony 4.3. +Do not Trim the Answer +~~~~~~~~~~~~~~~~~~~~~~ + +You can also specify if you want to not trim the answer by setting it directly with +:method:`Symfony\\Component\\Console\\Question\\Question::setTrimmable`:: + + use Symfony\Component\Console\Question\Question; + + // ... + public function execute(InputInterface $input, OutputInterface $output) + { + // ... + $helper = $this->getHelper('question'); + + $question = new Question('What is the name of the child?'); + $question->setTrimmable(false); + // if the users inputs 'elsa ' it will not be trimmed and you will get 'elsa ' as value + $name = $helper->ask($input, $output, $question); + } + +.. versionadded:: 4.4 + + The ``setTrimmable()`` method was introduced in Symfony 4.4. + Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 9116f6b99f97bcd316a5cf8d4d2ac93809a7961b Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Fri, 9 Aug 2019 13:20:44 +0200 Subject: [PATCH 0293/8077] Remove old versionadded directives --- components/console/helpers/questionhelper.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index f2d752291d5..af5d4ae32c9 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -214,10 +214,6 @@ provide a callback function to dynamically generate suggestions:: $filePath = $helper->ask($input, $output, $question); } -.. versionadded:: 4.3 - - The ``setAutocompleterCallback()`` method was introduced in Symfony 4.3. - Do not Trim the Answer ~~~~~~~~~~~~~~~~~~~~~~ @@ -238,10 +234,6 @@ You can also specify if you want to not trim the answer by setting it directly w $name = $helper->ask($input, $output, $question); } -.. versionadded:: 4.4 - - The ``setTrimmable()`` method was introduced in Symfony 4.4. - Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ From df9ec7a4154eee4f50cec1cd79b004148ced71d6 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Sat, 10 Aug 2019 11:35:20 +0200 Subject: [PATCH 0294/8077] Update security.yml use auto for encoding --- security.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security.rst b/security.rst index c35ab9fa6b9..f9d4ab90f82 100644 --- a/security.rst +++ b/security.rst @@ -124,10 +124,9 @@ command will pre-configure this for you: encoders: # use your user class name here App\Entity\User: - # bcrypt or sodium are recommended - # sodium is more secure, but requires PHP 7.2 or the Sodium extension - algorithm: bcrypt - cost: 12 + # Use native password encoder + # This value auto-selects the best possible hashing algorithm. + algorithm: auto .. code-block:: xml From a72a8abb0716f0413e3f240e6f1a975615a4fd98 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Sat, 10 Aug 2019 12:07:31 +0200 Subject: [PATCH 0295/8077] Update form_login_setup.rst The script ask for the generation of logout, so add this information to the doc too --- security/form_login_setup.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/form_login_setup.rst b/security/form_login_setup.rst index bd6a8c65bb5..3527c2a3fda 100644 --- a/security/form_login_setup.rst +++ b/security/form_login_setup.rst @@ -31,6 +31,9 @@ and your generated code may be slightly different: Choose a name for the controller class (e.g. SecurityController) [SecurityController]: > SecurityController + + Do you want to generate a '/logout' URL? (yes/no) [yes]: + > yes created: src/Security/LoginFormAuthenticator.php updated: config/packages/security.yaml From e614ed1d61941804aced0a5f9b3bdb3d48f17f9f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 8 Aug 2019 09:57:33 +0200 Subject: [PATCH 0296/8077] Updated related to Symfony installation and Symfony Flex --- bundles.rst | 2 +- bundles/best_practices.rst | 2 +- configuration.rst | 6 +- contributing/code/security.rst | 3 +- controller/error_pages.rst | 2 +- deployment.rst | 2 +- email.rst | 2 +- forms.rst | 2 +- frontend/encore/installation.rst | 2 +- mailer.rst | 6 +- mercure.rst | 2 +- messenger.rst | 2 +- migration.rst | 2 +- page_creation.rst | 5 +- profiler.rst | 2 +- security.rst | 2 +- security/ldap.rst | 2 +- serializer.rst | 2 +- setup.rst | 221 ++++++++++++++++++++++--------- setup/flex.rst | 137 +------------------ setup/symfony_server.rst | 67 +--------- setup/upgrade_major.rst | 2 +- testing.rst | 2 +- validation.rst | 2 +- workflow.rst | 4 +- 25 files changed, 194 insertions(+), 289 deletions(-) diff --git a/bundles.rst b/bundles.rst index a16c2870ab6..cdef74b5c87 100644 --- a/bundles.rst +++ b/bundles.rst @@ -37,7 +37,7 @@ file:: .. tip:: - In a default Symfony application that uses :doc:`Symfony Flex `, + In a default Symfony application that uses :ref:`Symfony Flex `, bundles are enabled/disabled automatically for you when installing/removing them, so you don't need to look at or edit this ``bundles.php`` file. diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index b4d7d20adcb..fd6198a87d2 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -246,7 +246,7 @@ Installation ------------ Bundles should set ``"type": "symfony-bundle"`` in their ``composer.json`` file. -With this, :doc:`Symfony Flex ` will be able to automatically +With this, :ref:`Symfony Flex ` will be able to automatically enable your bundle when it's installed. If your bundle requires any setup (e.g. configuration, new files, changes to diff --git a/configuration.rst b/configuration.rst index fffcf9501a9..6314d0dbfb3 100644 --- a/configuration.rst +++ b/configuration.rst @@ -30,7 +30,7 @@ stores the configuration of every package installed in your application. Packages (also called "bundles" in Symfony and "plugins/modules" in other projects) add ready-to-use features to your projects. -When using :doc:`Symfony Flex `, which is enabled by default in +When using :ref:`Symfony Flex `, which is enabled by default in Symfony applications, packages update the ``bundles.php`` file and create new files in ``config/packages/`` automatically during their installation. For example, this is the default file created by the "API Platform" package: @@ -555,7 +555,7 @@ This is for example the content of the ``.env`` file to define the value of the In addition to your own env vars, this ``.env`` file also contains the env vars defined by the third-party packages installed in your application (they are -added automatically by :doc:`Symfony Flex ` when installing packages). +added automatically by :ref:`Symfony Flex ` when installing packages). .. _configuration-env-var-in-prod: @@ -565,7 +565,7 @@ Configuring Environment Variables in Production In production, the ``.env`` files are also parsed and loaded on each request so you can override the env vars already defined in the server. In order to improve performance, you can run the ``dump-env`` command (available when using -:doc:`Symfony Flex ` 1.2 or later). +:ref:`Symfony Flex ` 1.2 or later). This command parses all the ``.env`` files once and compiles their contents into a new PHP-optimized file called ``.env.local.php``. From that moment, Symfony diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 344661b4958..46f250230ba 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -169,8 +169,7 @@ Security Advisories .. tip:: You can check your Symfony application for known security vulnerabilities - using the ``security:check`` command provided by the - :ref:`Symfony security checker `. + using :ref:`the security:check command `. Check the `Security Advisories`_ blog category for a list of all security vulnerabilities that were fixed in Symfony releases, starting from Symfony diff --git a/controller/error_pages.rst b/controller/error_pages.rst index 7bfd8fd0932..e8aef8f59a4 100644 --- a/controller/error_pages.rst +++ b/controller/error_pages.rst @@ -150,7 +150,7 @@ Fortunately, the default ``ExceptionController`` allows you to preview your *error* pages during development. To use this feature, you need to load some special routes provided by TwigBundle -(if the application uses :doc:`Symfony Flex ` they are loaded +(if the application uses :ref:`Symfony Flex ` they are loaded automatically when installing Twig support): .. configuration-block:: diff --git a/deployment.rst b/deployment.rst index 27a76b78165..ebab725db8f 100644 --- a/deployment.rst +++ b/deployment.rst @@ -170,7 +170,7 @@ as you normally do: If you get a "class not found" error during this step, you may need to run ``export APP_ENV=prod`` (or ``export SYMFONY_ENV=prod`` if you're not - using :doc:`Symfony Flex `) before running this command so + using :ref:`Symfony Flex `) before running this command so that the ``post-install-cmd`` scripts run in the ``prod`` environment. D) Clear your Symfony Cache diff --git a/email.rst b/email.rst index 4de99f390f3..5cef4971914 100644 --- a/email.rst +++ b/email.rst @@ -17,7 +17,7 @@ own mail servers as well as using popular email providers like `Mandrill`_, Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the Swift Mailer based mailer before using it: .. code-block:: terminal diff --git a/forms.rst b/forms.rst index 4fccc340c43..c0e03f74419 100644 --- a/forms.rst +++ b/forms.rst @@ -17,7 +17,7 @@ learning the most important features of the form library along the way. Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the form feature before using it: .. code-block:: terminal diff --git a/frontend/encore/installation.rst b/frontend/encore/installation.rst index 341362d667a..9fab79dbcbd 100644 --- a/frontend/encore/installation.rst +++ b/frontend/encore/installation.rst @@ -16,7 +16,7 @@ project: $ composer require symfony/webpack-encore-bundle $ yarn install -If you are using :doc:`Symfony Flex `, this will install and enable +If you are using :ref:`Symfony Flex `, this will install and enable the `WebpackEncoreBundle`_, create the ``assets/`` directory, add a ``webpack.config.js`` file, and add ``node_modules/`` to ``.gitignore``. You can skip the rest of this article and go write your first JavaScript and CSS by diff --git a/mailer.rst b/mailer.rst index c7fded70b84..863b93cbd9d 100644 --- a/mailer.rst +++ b/mailer.rst @@ -50,9 +50,9 @@ Postmark ``composer require symfony/postmark-mailer`` SendGrid ``composer require symfony/sendgrid-mailer`` ================== ============================================= -Each library includes a :ref:`Flex recipe ` that will add example configuration -to your ``.env`` file. For example, suppose you want to use SendGrid. First, -install it: +Each library includes a :ref:`Symfony Flex recipe ` that will add +example configuration to your ``.env`` file. For example, suppose you want to +use SendGrid. First, install it: .. code-block:: terminal diff --git a/mercure.rst b/mercure.rst index bb1e973b64b..6c8ba2f8fed 100644 --- a/mercure.rst +++ b/mercure.rst @@ -47,7 +47,7 @@ Installation Installing the Symfony Component ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the Mercure support before using it: .. code-block:: terminal diff --git a/messenger.rst b/messenger.rst index fe0dbf6696c..cbf1676ee44 100644 --- a/messenger.rst +++ b/messenger.rst @@ -12,7 +12,7 @@ handle them immediately in your application or send them through transports Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install messenger: .. code-block:: terminal diff --git a/migration.rst b/migration.rst index 0415ac59ae9..4b6e6838c5f 100644 --- a/migration.rst +++ b/migration.rst @@ -52,7 +52,7 @@ faster on fixing deprecations to be able to upgrade. .. tip:: When upgrading to Symfony you might be tempted to also use - :doc:`Flex `. Please keep in mind that it primarily + :ref:`Flex `. Please keep in mind that it primarily focuses on bootstrapping a new Symfony application according to best practices regarding the directory structure. When you work in the constraints of an existing application you might not be able to diff --git a/page_creation.rst b/page_creation.rst index 5e71744b531..4dd1d0eb335 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -133,7 +133,7 @@ Auto-Installing Recipes with Symfony Flex You may not have noticed, but when you ran ``composer require annotations``, two special things happened, both thanks to a powerful Composer plugin called -:doc:`Flex `. +:ref:`Flex `. First, ``annotations`` isn't a real package name: it's an *alias* (i.e. shortcut) that Flex resolves to ``sensio/framework-extra-bundle``. @@ -145,9 +145,6 @@ to do a lot, like adding configuration files, creating directories, updating ``. and adding new config to your ``.env`` file. Flex *automates* the installation of packages so you can get back to coding. -You can learn more about Flex by reading ":doc:`/setup/flex`". But that's not necessary: -Flex works automatically in the background when you add packages. - The bin/console Command ----------------------- diff --git a/profiler.rst b/profiler.rst index cfc0bf61dd6..e37bdd8ba7d 100644 --- a/profiler.rst +++ b/profiler.rst @@ -8,7 +8,7 @@ environments as it will lead to major security vulnerabilities in your project. Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the profiler before using it: .. code-block:: terminal diff --git a/security.rst b/security.rst index c35ab9fa6b9..331bd921432 100644 --- a/security.rst +++ b/security.rst @@ -30,7 +30,7 @@ A few other important topics are discussed after. 1) Installation --------------- -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the security feature before using it: .. code-block:: terminal diff --git a/security/ldap.rst b/security/ldap.rst index 73a93f4e2be..2bb3eee95bd 100644 --- a/security/ldap.rst +++ b/security/ldap.rst @@ -37,7 +37,7 @@ This means that the following scenarios will work: Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the Ldap component before using it: .. code-block:: terminal diff --git a/serializer.rst b/serializer.rst index 3812b4d285d..43781767379 100644 --- a/serializer.rst +++ b/serializer.rst @@ -14,7 +14,7 @@ its philosophy and the normalizers and encoders terminology. Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the serializer before using it: .. code-block:: terminal diff --git a/setup.rst b/setup.rst index 17390760296..fdb54c448d0 100644 --- a/setup.rst +++ b/setup.rst @@ -10,57 +10,67 @@ Installing & Setting up the Symfony Framework Do you prefer video tutorials? Check out the `Stellar Development with Symfony`_ screencast series. -Installing Symfony ------------------- +Technical Requirements +---------------------- -Before creating your first Symfony application, make sure to meet the following -requirements: +Before creating your first Symfony application you must: -* Your server has PHP 7.1 or higher installed (and :doc:`these PHP extensions ` +* Make sure to have PHP 7.1 or higher installed (and :doc:`these PHP extensions ` which are installed and enabled by default by PHP); -* You have `installed Composer`_, which is used to install PHP packages; -* You have installed the :doc:`Symfony local web server `, - which provides all the tools you need to develop your application locally. +* `Install Composer`_, which is used to install PHP packages; +* `Install Symfony`_, which creates in your computer a binary called ``symfony`` + that provides all the tools you need to develop your application locally. -Once these requirements are installed, open your terminal and run any of these -commands to create the Symfony application: +.. _creating-symfony-applications: + +Creating Symfony Applications +----------------------------- + +Open your console terminal and run any of these commands to create a new Symfony +application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new --full my_project + $ symfony new --full my_project_name # run this if you are building a microservice, console application or API - $ symfony new my-project + $ symfony new my_project_name The only difference between these two commands is the number of packages -installed. The ``--full`` option installs all the packages that you usually -need to build web applications. Therefore, the installation size will be much bigger. +installed by default. The ``--full`` option installs all the packages that you +usually need to build web applications, so the installation size will be bigger. -Both commands will create a new ``my-project/`` directory, download some -dependencies into it and even generate the basic directories and files you'll -need to get started. In other words, your new app is ready! +If you can't or don't want to `install Symfony`_ for any reason, run these +commands to create the new Symfony application using Composer: + +.. code-block:: terminal -.. seealso:: + # run this if you are building a traditional web application + $ composer create-project symfony/website-skeleton my_project_name + + # run this if you are building a microservice, console application or API + $ composer create-project symfony/skeleton my_project_name - If you can't use the ``symfony`` command provided by the Symfony local web - server, use the alternative installation commands based on Composer and - displayed on the `Symfony download page`_. +No matter which command you run to create the Symfony application. All of them +will create a new ``my_project_name/`` directory, download some dependencies +into it and even generate the basic directories and files you'll need to get +started. In other words, your new application is ready! -Running your Symfony Application --------------------------------- +Running Symfony Applications +---------------------------- On production, you should use a web server like Nginx or Apache (see :doc:`configuring a web server to run Symfony `). But for development, it's more convenient to use the -:doc:`Symfony Local Web Server ` installed earlier. +:doc:`local web server ` provided by Symfony. This local server provides support for HTTP/2, TLS/SSL, automatic generation of security certificates and many other features. It works with any PHP application, not only Symfony projects, so it's a very useful development tool. -Open your terminal, move into your new project directory and start the local web -server as follows: +Open your console terminal, move into your new project directory and start the +local web server as follows: .. code-block:: terminal @@ -77,36 +87,15 @@ the server by pressing ``Ctrl+C`` from your terminal. some technical requirements. Use the :doc:`Symfony Requirements Checker ` tool to make sure your system is set up. -.. note:: - - If you want to use a virtual machine (VM) with Vagrant, check out - :doc:`Homestead `. - -Storing your Project in git ---------------------------- - -Storing your project in services like GitHub, GitLab and Bitbucket works like with -any other code project! Init a new repository with ``Git`` and you are ready to push -to your remote: - -.. code-block:: terminal - - $ git init - $ git add . - $ git commit -m "Initial commit" - -Your project already has a sensible ``.gitignore`` file. And as you install more -packages, a system called :ref:`Flex ` will add more lines to -that file when needed. - .. _install-existing-app: Setting up an Existing Symfony Project -------------------------------------- -If you're working on an existing Symfony application, you only need to get the -project code and install the dependencies with Composer. Assuming your team uses Git, -setup your project with the following commands: +In addition to creating new Symfony projects, you will also work on projects +already created by other developers. In that case, you only need to get the +project code and install the dependencies with Composer. Assuming your team uses +Git, setup your project with the following commands: .. code-block:: terminal @@ -118,15 +107,112 @@ setup your project with the following commands: $ cd my-project/ $ composer install -You'll probably also need to customize your :ref:`.env ` and do a -few other project-specific tasks (e.g. creating database schema). When working -on a existing Symfony app for the first time, it may be useful to run this -command which displays information about the app: +You'll probably also need to customize your :ref:`.env file ` +and do a few other project-specific tasks (e.g. creating a database). When +working on a existing Symfony application for the first time, it may be useful +to run this command which displays information about the project: .. code-block:: terminal $ php bin/console about +.. _symfony-flex: + +Installing Packages +------------------- + +A common practice when developing Symfony applications is to install packages +(Symfony calls them :doc:`bundles `) that provide ready-to-use +features. Packages usually require some setup before using them (editing some +file to enable the bundle, creating some file to add some initial config, etc.) + +Most of the time this setup can be automated and that's why Symfony includes +`Symfony Flex`_, a tool to simplify the installation/removal of packages in +Symfony applications. Technically speaking, Symfony Flex is a Composer plugin +that is installed by default when creating a new Symfony application and which +**automates the most common tasks of Symfony applications**. + +.. tip:: + + You can also :doc:`add Symfony Flex to an existing project `. + +Symfony Flex modifies the behavior of the ``require``, ``update``, and +``remove`` Composer commands to provide advanced features. Consider the +following example: + +.. code-block:: terminal + + $ cd my-project/ + $ composer require logger + +If you execute that command in a Symfony application which doesn't use Flex, +you'll see a Composer error explaining that ``logger`` is not a valid package +name. However, if the application has Symfony Flex installed, that command +installs and enables all the packages needed to use the official Symfony logger. + +This is possible because lots of Symfony packages/bundles define **"recipes"**, +which are a set of automated instructions to install and enable packages into +Symfony applications. Flex keeps tracks of the recipes it installed in a +``symfony.lock`` file, which must be committed to your code repository. + +Symfony Flex recipes are contributed by the community and they are stored in +two public repositories: + +* `Main recipe repository`_, is a curated list of recipes for high quality and + maintained packages. Symfony Flex only looks in this repository by default. + +* `Contrib recipe repository`_, contains all the recipes created by the + community. All of them are guaranteed to work, but their associated packages + could be unmaintained. Symfony Flex will ask your permission before installing + any of these recipes. + +Read the `Symfony Recipes documentation`_ to learn everything about how to +create recipes for your own packages. + +.. _security-checker: + +Checking Security Vulnerabilities +--------------------------------- + +The ``symfony`` binary created when you `install Symfony`_ provides a command to +check whether your project's dependencies contain any known security +vulnerability: + +.. code-block:: terminal + + $ symfony security:check + +A good security practice is to execute this command regularly to be able to +update or replace compromised dependencies as soon as possible. The security +check is done locally by cloning the public `PHP security advisories database`_, +so your ``composer.lock`` file is not sent on the network. + +.. tip:: + + The ``security:check`` command terminates with a non-zero exit code if + any of your dependencies is affected by a known security vulnerability. + This way you can add it to your project build process and your continuous + integration workflows to make them fail when there are vulnerabilities. + +Symfony LTS Versions +-------------------- + +According to the :doc:`Symfony release process `, +"long-term support" (or LTS for short) versions are published every two years. +Check out the `Symfony roadmap`_ to know which is the latest LTS version. + +By default, the command that creates new Symfony applications uses the latest +stable version. If you want to use an LTS version, add the ``--version`` option: + +.. code-block:: terminal + + # find the latest LTS version at https://symfony.com/roadmap + $ symfony new --version=3.4 my_project_name_name + + # you can also base your project on development versions + $ symfony new --version=4.4.x-dev my_project_name + $ symfony new --version=dev-master my_project_name + The Symfony Demo application ---------------------------- @@ -134,15 +220,19 @@ The Symfony Demo application recommended way to develop Symfony applications. It's a great learning tool for Symfony newcomers and its code contains tons of comments and helpful notes. -To check out its code and install it locally, see `symfony/symfony-demo`_. +Run this command to create a new project based on the Symfony Demo application: + +.. code-block:: terminal + + $ symfony new --demo my_project_name Start Coding! ------------- With setup behind you, it's time to :doc:`Create your first page in Symfony `. -Go Deeper with Setup --------------------- +Learn More +---------- .. toctree:: :hidden: @@ -158,10 +248,13 @@ Go Deeper with Setup setup/* .. _`Stellar Development with Symfony`: http://symfonycasts.com/screencast/symfony -.. _`Composer`: https://getcomposer.org/ -.. _`installed Composer`: https://getcomposer.org/download/ -.. _`Download the Symfony local web server`: https://symfony.com/download -.. _`Symfony download page`: https://symfony.com/download -.. _`the Security Checker`: https://github.com/sensiolabs/security-checker#integration +.. _`Install Composer`: https://getcomposer.org/download/ +.. _`Install Symfony`: https://symfony.com/download +.. _`install Symfony`: https://symfony.com/download .. _`The Symfony Demo application`: https://github.com/symfony/demo -.. _`symfony/symfony-demo`: https://github.com/symfony/demo +.. _`Symfony Flex`: https://github.com/symfony/flex +.. _`PHP security advisories database`: https://github.com/FriendsOfPHP/security-advisories +.. _`Symfony roadmap`: https://symfony.com/roadmap +.. _`Main recipe repository`: https://github.com/symfony/recipes +.. _`Contrib recipe repository`: https://github.com/symfony/recipes-contrib +.. _`Symfony Recipes documentation`: https://github.com/symfony/recipes/blob/master/README.rst diff --git a/setup/flex.rst b/setup/flex.rst index 328f13a098b..00f734820e2 100644 --- a/setup/flex.rst +++ b/setup/flex.rst @@ -1,137 +1,15 @@ .. index:: Flex -Using Symfony Flex to Manage Symfony Applications -================================================= - -`Symfony Flex`_ is the new way to install and manage Symfony applications. Flex -is not a new Symfony version, but a tool that replaces and improves the -`Symfony Installer`_ and the `Symfony Standard Edition`_. - -Symfony Flex **automates the most common tasks of Symfony applications**, like -installing and removing bundles and other Composer dependencies. Symfony -Flex works for Symfony 3.3 and higher. Starting from Symfony 4.0, Flex -should be used by default, but it is still optional. - -How Does Flex Work ------------------- - -Symfony Flex is a Composer plugin that modifies the behavior of the -``require``, ``update``, and ``remove`` commands. When installing or removing -dependencies in a Flex-enabled application, Symfony can perform tasks before -and after the execution of Composer tasks. - -Consider the following example: - -.. code-block:: terminal - - $ cd my-project/ - $ composer require mailer - -If you execute that command in a Symfony application which doesn't use Flex, -you'll see a Composer error explaining that ``mailer`` is not a valid package -name. However, if the application has Symfony Flex installed, that command ends -up installing and enabling the SwiftmailerBundle, which is the best way to -integrate Swiftmailer, the official mailer for Symfony applications. - -When Symfony Flex is installed in the application and you execute ``composer -require``, the application makes a request to the Symfony Flex server before -trying to install the package with Composer. - -* If there's no information about that package, the Flex server returns nothing and - the package installation follows the usual procedure based on Composer; - -* If there's special information about that package, Flex returns it in a file - called a "recipe" and the application uses it to decide which package to - install and which automated tasks to run after the installation. - -In the above example, Symfony Flex asks about the ``mailer`` package and the -Symfony Flex server detects that ``mailer`` is in fact an alias for -SwiftmailerBundle and returns the "recipe" for it. - -Flex keeps tracks of the recipes it installed in a ``symfony.lock`` file, which -must be committed to your code repository. - -.. _flex-recipe: - -Symfony Flex Recipes -~~~~~~~~~~~~~~~~~~~~ - -Recipes are defined in a ``manifest.json`` file and can contain any number of -other files and directories. For example, this is the ``manifest.json`` for -SwiftmailerBundle: - -.. code-block:: javascript - - { - "bundles": { - "Symfony\\Bundle\\SwiftmailerBundle\\SwiftmailerBundle": ["all"] - }, - "copy-from-recipe": { - "config/": "%CONFIG_DIR%/" - }, - "env": { - "MAILER_URL": "smtp://localhost:25?encryption=&auth_mode=" - }, - "aliases": ["mailer", "mail"] - } - -The ``aliases`` option allows Flex to install packages using short and easy to -remember names (``composer require mailer`` vs -``composer require symfony/swiftmailer-bundle``). The ``bundles`` option tells -Flex in which environments this bundle should be enabled automatically (``all`` -in this case). The ``env`` option makes Flex add new environment variables to -the application. Finally, the ``copy-from-recipe`` option allows the recipe to -copy files and directories into your application. - -The instructions defined in this ``manifest.json`` file are also used by -Symfony Flex when uninstalling dependencies (e.g. ``composer remove mailer``) -to undo all changes. This means that Flex can remove the SwiftmailerBundle from -the application, delete the ``MAILER_URL`` environment variable and any other -file and directory created by this recipe. - -Symfony Flex recipes are contributed by the community and they are stored in -two public repositories: - -* `Main recipe repository`_, is a curated list of recipes for high quality and - maintained packages. Symfony Flex only looks in this repository by default. - -* `Contrib recipe repository`_, contains all the recipes created by the - community. All of them are guaranteed to work, but their associated packages - could be unmaintained. Symfony Flex will ask your permission before installing - any of these recipes. - -Read the `Symfony Recipes documentation`_ to learn everything about how to -create recipes for your own packages. - -Using Symfony Flex in New Applications --------------------------------------- - -Symfony has published a new "skeleton" project, which is a minimal Symfony -project recommended to create new applications. This "skeleton" already -includes Symfony Flex as a dependency. This means you can create a new Flex-enabled -Symfony application by executing the following command: - -.. code-block:: terminal - - $ composer create-project symfony/skeleton my-project - -.. note:: - - The use of the Symfony Installer to create new applications is no longer - recommended since Symfony 3.3. Use the Composer ``create-project`` command - instead. - -.. _upgrade-to-flex: - -Upgrading Existing Applications to Flex ---------------------------------------- +Upgrading Existing Applications to Symfony Flex +=============================================== Using Symfony Flex is optional, even in Symfony 4, where Flex is used by default. However, Flex is so convenient and improves your productivity so much that it's strongly recommended to upgrade your existing applications to it. -The only caveat is that Symfony Flex requires that applications use the -following directory structure, which is the same used by default in Symfony 4: +Symfony Flex recommends that applications use the following directory structure, +which is the same used by default in Symfony 4, but you can +:ref:`customize some directories `: .. code-block:: text @@ -283,6 +161,8 @@ manual steps: #. Update the ``.gitignore`` file to replace the existing ``var/logs/`` entry by ``var/log/``, which is the new name for the log directory. +.. _flex-customize-paths: + Customizing Flex Paths ---------------------- @@ -316,9 +196,6 @@ manually after a recipe is installed. .. _`Symfony Flex`: https://github.com/symfony/flex .. _`Symfony Installer`: https://github.com/symfony/symfony-installer .. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard -.. _`Main recipe repository`: https://github.com/symfony/recipes -.. _`Contrib recipe repository`: https://github.com/symfony/recipes-contrib -.. _`Symfony Recipes documentation`: https://github.com/symfony/recipes/blob/master/README.rst .. _`default services.yaml file`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/config/services.yaml .. _`shown in this example`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L24-L33 .. _`shown in this example of the skeleton-project`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L44-L46 diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index 05109ddadd1..a44ae3929d8 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -14,9 +14,8 @@ PHP application and even with HTML/SPA (single page applications). Installation ------------ -The Symfony server is distributed as a free installable binary and has support -for Linux, macOS and Windows. Go to `symfony.com/download`_ and follow the -instructions for your operating system. +The Symfony server is part of the ``symfony`` binary created when you +`install Symfony`_ and has support for Linux, macOS and Windows. .. note:: @@ -315,68 +314,8 @@ debug any issues. `Read SymfonyCloud technical docs`_. -Bonus Features --------------- - -In addition to being a local web server, the Symfony server provides other -useful features: - -.. _security-checker: - -Looking for Security Vulnerabilities -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Run the following command to check whether your project's dependencies contain -any known security vulnerability: - -.. code-block:: terminal - - $ symfony security:check - -A good security practice is to execute this command regularly to be able to -update or replace compromised dependencies as soon as possible. The security -check is done locally by cloning the public `PHP security advisories database`_, -so your ``composer.lock`` file is not sent on the network. - -.. tip:: - - The ``security:check`` command terminates with a non-zero exit code if - any of your dependencies is affected by a known security vulnerability. - This way you can add it to your project build process and your continuous - integration workflows to make them fail when there are vulnerabilities. - -Creating Symfony Projects -~~~~~~~~~~~~~~~~~~~~~~~~~ - -In addition to the `different ways of installing Symfony`_, you can use these -commands from the Symfony server: - -.. code-block:: terminal - - # creates a new project based on symfony/skeleton - $ symfony new my_project_name - - # creates a new project based on symfony/website-skeleton - $ symfony new --full my_project_name - - # creates a new project based on symfony/demo - $ symfony new --demo my_project_name - -You can create a project depending on a **development** version as well (note -that Composer will also set the stability to ``dev`` for all root dependencies): - -.. code-block:: terminal - - # creates a new project based on Symfony's master branch - $ symfony new --version=dev-master my_project_name - - # creates a new project based on Symfony's 4.3 dev branch - $ symfony new --version=4.3.x-dev my_project_name - -.. _`symfony.com/download`: https://symfony.com/download +.. _`install Symfony`: https://symfony.com/download .. _`symfony/cli`: https://github.com/symfony/cli -.. _`different ways of installing Symfony`: https://symfony.com/download .. _`Docker`: https://en.wikipedia.org/wiki/Docker_(software) .. _`SymfonyCloud`: https://symfony.com/cloud/ .. _`Read SymfonyCloud technical docs`: https://symfony.com/doc/master/cloud/intro.html -.. _`PHP security advisories database`: https://github.com/FriendsOfPHP/security-advisories diff --git a/setup/upgrade_major.rst b/setup/upgrade_major.rst index c2a91d52efa..e2439d41b9f 100644 --- a/setup/upgrade_major.rst +++ b/setup/upgrade_major.rst @@ -162,6 +162,6 @@ of. When upgrading to Symfony 4, you will probably also want to upgrade to the new Symfony 4 directory structure so that you can take advantage of Symfony Flex. -This takes some work, but is optional. For details, see :ref:`upgrade-to-flex`. +This takes some work, but is optional. For details, see :doc:`/setup/flex`. .. _`Symfony-Upgrade-Fixer`: https://github.com/umpirsky/Symfony-Upgrade-Fixer diff --git a/testing.rst b/testing.rst index 766f452bfdb..7a3c44836b6 100644 --- a/testing.rst +++ b/testing.rst @@ -33,7 +33,7 @@ command: .. note:: - The ``./bin/phpunit`` command is created by :doc:`Symfony Flex ` + The ``./bin/phpunit`` command is created by :ref:`Symfony Flex ` when installing the ``phpunit-bridge`` package. If the command is missing, you can remove the package (``composer remove symfony/phpunit-bridge``) and install it again. Another solution is to remove the project's ``symfony.lock`` file and diff --git a/validation.rst b/validation.rst index 52d98fe7dd6..6dd686fd24a 100644 --- a/validation.rst +++ b/validation.rst @@ -14,7 +14,7 @@ transparent. This component is based on the `JSR303 Bean Validation specificatio Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the validator before using it: .. code-block:: terminal diff --git a/workflow.rst b/workflow.rst index e2a542c8683..cf1a5802036 100644 --- a/workflow.rst +++ b/workflow.rst @@ -8,7 +8,7 @@ some basic theory and concepts about workflows and state machines. Installation ------------ -In applications using :doc:`Symfony Flex `, run this command to +In applications using :ref:`Symfony Flex `, run this command to install the workflow feature before using it: .. code-block:: terminal @@ -318,7 +318,7 @@ workflow leaves a place:: class WorkflowLogger implements EventSubscriberInterface { private $logger; - + public function __construct(LoggerInterface $logger) { $this->logger = $logger; From a63c5a6d2418544c4a5d2bd249db0fdfba2b56e9 Mon Sep 17 00:00:00 2001 From: brambaud Date: Wed, 7 Aug 2019 18:50:01 +0200 Subject: [PATCH 0297/8077] add support for custom headers when using a proxy --- deployment/proxies.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/deployment/proxies.rst b/deployment/proxies.rst index db8f05898be..bd0881bb1d4 100644 --- a/deployment/proxies.rst +++ b/deployment/proxies.rst @@ -78,3 +78,20 @@ other information. .. _`security groups`: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-groups.html .. _`RFC 7239`: http://tools.ietf.org/html/rfc7239 + +Custom Headers When Using a Reverse Proxy +----------------------------------------- + +Some reverse proxies (like CloudFront with ``CloudFront-Forwarded-Proto``) may force you to use a custom header. +For instance you have ``Custom-Forwarded-Proto`` instead of ``X-Forwarded-Proto``. + +In this case, you'll need to set the header ``X-Forwarded-Proto`` with the value of +``Custom-Forwarded-Proto`` early enough in your application, i.e. before handling the request:: + + // web/app.php + + // ... + $_SERVER['HEADER_X_FORWARDED_PROTO'] = $_SERVER['HEADER_CUSTOM_FORWARDED_PROTO']; + // ... + $response = $kernel->handle($request); + From 99a9d9c0925e7a873616c863ac32e5178c12654d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 12 Aug 2019 08:50:34 +0200 Subject: [PATCH 0298/8077] Use :: instead of code block php --- components/mailer.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/mailer.rst b/components/mailer.rst index 99797acc91e..36caa78c972 100644 --- a/components/mailer.rst +++ b/components/mailer.rst @@ -63,9 +63,7 @@ it: $ composer require symfony/google-mailer -Then, use the SMTP Gmail transport: - -.. code-block:: php +Then, use the SMTP Gmail transport:: use Symfony\Component\Mailer\Bridge\Google\Smtp\GmailTransport; From 7c1551a43a4e5d43b3e2ee20fcb6f917ff4a4487 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 12 Aug 2019 08:48:09 +0200 Subject: [PATCH 0299/8077] Fix path --- reference/configuration/framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 2889c275895..9116999e88f 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1210,14 +1210,14 @@ Unlike the other session options, ``cache_limiter`` is set as a regular .. code-block:: yaml - # app/config/services.yml + # config/services.yaml parameters: session.storage.options: cache_limiter: 0 .. code-block:: xml - + setParameter('session.storage.options', [ 'cache_limiter' => 0, ]); From 1268ba7b43be6d6793bb149f2d58b4f34570312e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 7 Aug 2019 16:59:25 +0200 Subject: [PATCH 0300/8077] Revamped the routing documentation --- _build/redirection_map | 14 + components/routing.rst | 359 +-- console/request_context.rst | 99 - controller.rst | 4 +- reference/configuration/framework.rst | 2 +- reference/forms/types/options/method.rst.inc | 2 +- routing.rst | 2114 ++++++++++++++---- routing/conditions.rst | 112 - routing/custom_route_loader.rst | 84 + routing/debug.rst | 71 - routing/external_resources.rst | 260 --- routing/extra_information.rst | 102 - routing/generate_url_javascript.rst | 25 - routing/hostname_pattern.rst | 439 ---- routing/optional_placeholders.rst | 198 -- routing/redirect_in_config.rst | 260 --- routing/redirect_trailing_slash.rst | 12 - routing/requirements.rst | 310 --- routing/scheme.rst | 94 - routing/service_container_parameters.rst | 212 -- routing/slash_in_parameter.rst | 99 - security/force_https.rst | 6 +- templating/formats.rst | 6 +- translation/locale.rst | 8 +- 24 files changed, 1833 insertions(+), 3059 deletions(-) delete mode 100644 console/request_context.rst delete mode 100644 routing/conditions.rst delete mode 100644 routing/debug.rst delete mode 100644 routing/external_resources.rst delete mode 100644 routing/extra_information.rst delete mode 100644 routing/generate_url_javascript.rst delete mode 100644 routing/hostname_pattern.rst delete mode 100644 routing/optional_placeholders.rst delete mode 100644 routing/redirect_in_config.rst delete mode 100644 routing/redirect_trailing_slash.rst delete mode 100644 routing/requirements.rst delete mode 100644 routing/scheme.rst delete mode 100644 routing/service_container_parameters.rst delete mode 100644 routing/slash_in_parameter.rst diff --git a/_build/redirection_map b/_build/redirection_map index b932501df3e..10b3bc9432a 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -429,3 +429,17 @@ /setup/composer /setup /security/security_checker /setup /service_container/parameters /configuration +/routing/generate_url_javascript /routing +/routing/slash_in_parameter /routing +/routing/scheme /routing +/routing/optional_placeholders /routing +/routing/conditions /routing +/routing/requirements /routing +/routing/redirect_trailing_slash /routing +/routing/debug /routing +/routing/service_container_parameters /routing +/routing/redirect_in_config /routing +/routing/external_resources /routing +/routing/hostname_pattern /routing +/routing/extra_information /routing +/console/request_context /routing diff --git a/components/routing.rst b/components/routing.rst index 020abfa912e..7bccb0e0a47 100644 --- a/components/routing.rst +++ b/components/routing.rst @@ -6,7 +6,8 @@ The Routing Component ===================== The Routing component maps an HTTP request to a set of configuration - variables. + variables. It's used to build routing systems for web applications where + each URL is associated with some code to execute. Installation ------------ @@ -20,45 +21,51 @@ Installation Usage ----- -.. seealso:: +The main :doc:`Symfony routing ` article explains all the features of +this component when used inside a Symfony application. This article only +explains the things you need to do to use it in a non-Symfony PHP application. - This article explains how to use the Routing features as an independent - component in any PHP application. Read the :doc:`/routing` article to learn - about how to use it in Symfony applications. +Routing System Setup +-------------------- -In order to set up a basic routing system you need three parts: +A routing system has three parts: -* A :class:`Symfony\\Component\\Routing\\RouteCollection`, which contains the route definitions (instances of the class :class:`Symfony\\Component\\Routing\\Route`) -* A :class:`Symfony\\Component\\Routing\\RequestContext`, which has information about the request -* A :class:`Symfony\\Component\\Routing\\Matcher\\UrlMatcher`, which performs the mapping of the path to a single route +* A :class:`Symfony\\Component\\Routing\\RouteCollection`, which contains the + route definitions (instances of the class :class:`Symfony\\Component\\Routing\\Route`); +* A :class:`Symfony\\Component\\Routing\\RequestContext`, which has information + about the request; +* A :class:`Symfony\\Component\\Routing\\Matcher\\UrlMatcher`, which performs + the mapping of the path to a single route. -Here is a quick example. Notice that this assumes that you've already configured -your autoloader to load the Routing component:: +Here is a quick example:: + use App\Controller\BlogController; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; - $route = new Route('/foo', ['_controller' => 'MyController']); + $route = new Route('/blog/{slug}', ['_controller' => BlogController::class]) $routes = new RouteCollection(); - $routes->add('route_name', $route); + $routes->add('blog_show', $route); $context = new RequestContext('/'); + // Routing can match routes with incoming requests $matcher = new UrlMatcher($routes, $context); + $parameters = $matcher->match('/blog/lorem-ipsum'); + // $parameters = [ + // '_controller' => 'App\Controller\BlogController', + // 'slug' => 'lorem-ipsum', + // '_route' => 'blog_show' + // ] - $parameters = $matcher->match('/foo'); - // ['_controller' => 'MyController', '_route' => 'route_name'] - -.. note:: - - The :class:`Symfony\\Component\\Routing\\RequestContext` parameters can be populated - with the values stored in ``$_SERVER``, but it's easier to use the HttpFoundation - component as explained :ref:`below `. - -You can add as many routes as you like to a -:class:`Symfony\\Component\\Routing\\RouteCollection`. + // Routing can also generate URLs for a given route + $generator = new UrlGenerator($routes, $context); + $url = $generator->generate('blog_show', [ + 'slug' => 'my-blog-post', + ]); + // $url = '/blog/my-blog-post' The :method:`RouteCollection::add() ` method takes two arguments. The first is the name of the route. The second @@ -68,50 +75,19 @@ of custom variables can be *anything* that's significant to your application, and is returned when that route is matched. The :method:`UrlMatcher::match() ` -returns the variables you set on the route as well as the wildcard placeholders -(see below). Your application can now use this information to continue -processing the request. In addition to the configured variables, a ``_route`` -key is added, which holds the name of the matched route. +returns the variables you set on the route as well as the route parameters. +Your application can now use this information to continue processing the request. +In addition to the configured variables, a ``_route`` key is added, which holds +the name of the matched route. If no matching route can be found, a :class:`Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException` will be thrown. Defining Routes -~~~~~~~~~~~~~~~ - -A full route definition can contain up to eight parts: - -#. The URL pattern. This is matched against the URL passed to the - ``RequestContext``. It is not a regular expression, but can contain named - wildcard placeholders (e.g. ``{slug}``) to match dynamic parts in the URL. - The component will create the regular expression from it. - -#. An array of default parameters. This contains an array of arbitrary values - that will be returned when the request matches the route. It is used by - convention to map a controller to the route. - -#. An array of requirements. These define constraints for the values of the - placeholders in the pattern as regular expressions. - -#. An array of options. These contain advanced settings for the route and - can be used to control encoding or customize compilation. - See :ref:`routing-unicode-support` below. You can learn more about them by - reading :method:`Symfony\\Component\\Routing\\Route::setOptions` implementation. - -#. A host. This is matched against the host of the request. See - :doc:`/routing/hostname_pattern` for more details. - -#. An array of schemes. These enforce a certain HTTP scheme (``http``, ``https``). - -#. An array of methods. These enforce a certain HTTP request method (``HEAD``, - ``GET``, ``POST``, ...). +--------------- -#. A condition, using the :doc:`/components/expression_language/syntax`. - A string that must evaluate to ``true`` so the route matches. See - :doc:`/routing/conditions` for more details. - -Take the following route, which combines several of these ideas:: +A full route definition can contain up to eight parts:: $route = new Route( '/archive/{month}', // path @@ -137,36 +113,13 @@ Take the following route, which combines several of these ideas:: $parameters = $matcher->match('/archive/foo'); // throws ResourceNotFoundException -In this case, the route is matched by ``/archive/2012-01``, because the ``{month}`` -wildcard matches the regular expression wildcard given. However, ``/archive/foo`` -does *not* match, because "foo" fails the month wildcard. - -When using wildcards, these are returned in the array result when calling -``match``. The part of the path that the wildcard matched (e.g. ``2012-01``) is used -as value. - -A placeholder matches any character except slashes ``/`` by default, unless you define -a specific requirement for it. -The reason is that they are used by convention to separate different placeholders. - -If you want a placeholder to match anything, it must be the last of the route:: - - $route = new Route( - '/start/{required}/{anything}', - ['required' => 'default'], // should always be defined - ['anything' => '.*'] // explicit requirement to allow "/" - ); - -Learn more about it by reading :ref:`routing/slash_in_parameter`. - -Using Prefixes and Collection Settings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Route Collections +----------------- You can add routes or other instances of :class:`Symfony\\Component\\Routing\\RouteCollection` to *another* collection. -This way you can build a tree of routes. Additionally you can define a prefix -and default values for the parameters, requirements, options, schemes and the -host to all routes of a subtree using methods provided by the +This way you can build a tree of routes. Additionally you can define common +options for all routes of a subtree using methods provided by the ``RouteCollection`` class:: $rootCollection = new RouteCollection(); @@ -185,8 +138,8 @@ host to all routes of a subtree using methods provided by the $rootCollection->addCollection($subCollection); -Set the Request Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting the Request Parameters +------------------------------ The :class:`Symfony\\Component\\Routing\\RequestContext` provides information about the current request. You can define all parameters of an HTTP request @@ -216,67 +169,15 @@ Normally you can pass the values from the ``$_SERVER`` variable to populate the $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); -Generate a URL -~~~~~~~~~~~~~~ - -While the :class:`Symfony\\Component\\Routing\\Matcher\\UrlMatcher` tries -to find a route that fits the given request you can also build a URL from -a certain route with the :class:`Symfony\\Component\\Routing\\Generator\\UrlGenerator`:: - - use Symfony\Component\Routing\Generator\UrlGenerator; - use Symfony\Component\Routing\RequestContext; - use Symfony\Component\Routing\Route; - use Symfony\Component\Routing\RouteCollection; - - $routes = new RouteCollection(); - $routes->add('show_post', new Route('/show/{slug}')); - - $context = new RequestContext('/'); - - $generator = new UrlGenerator($routes, $context); - - $url = $generator->generate('show_post', [ - 'slug' => 'my-blog-post', - ]); - // /show/my-blog-post - -.. note:: - - If you have defined a scheme, an absolute URL is generated if the scheme - of the current :class:`Symfony\\Component\\Routing\\RequestContext` does - not match the requirement. - -Check if a Route Exists -~~~~~~~~~~~~~~~~~~~~~~~ - -In highly dynamic applications, it may be necessary to check whether a route -exists before using it to generate a URL. In those cases, don't use the -:method:`Symfony\\Component\\Routing\\Router::getRouteCollection` method because -that regenerates the routing cache and slows down the application. - -Instead, try to generate the URL and catch the -:class:`Symfony\\Component\\Routing\\Exception\\RouteNotFoundException` thrown -when the route doesn't exist:: - - use Symfony\Component\Routing\Exception\RouteNotFoundException; +Loading Routes +-------------- - // ... - - try { - $url = $generator->generate($dynamicRouteName, $parameters); - } catch (RouteNotFoundException $e) { - // the route is not defined... - } - -Load Routes from a File -~~~~~~~~~~~~~~~~~~~~~~~ +The Routing component comes with a number of loader classes, each giving you the +ability to load a collection of route definitions from external resources. -You've already seen how you can add routes to a collection right inside -PHP. But you can also load routes from a number of different files. +File Routing Loaders +~~~~~~~~~~~~~~~~~~~~ -The Routing component comes with a number of loader classes, each giving -you the ability to load a collection of route definitions from an external -file of some format. Each loader expects a :class:`Symfony\\Component\\Config\\FileLocator` instance as the constructor argument. You can use the :class:`Symfony\\Component\\Config\\FileLocator` to define an array of paths in which the loader will look for the requested files. @@ -291,7 +192,6 @@ If you're using the ``YamlFileLoader``, then route definitions look like this: path: /foo controller: MyController::fooAction methods: GET|HEAD - route2: path: /foo/bar controller: FooBarInvokableController @@ -315,7 +215,8 @@ other loaders that work the same way: * :class:`Symfony\\Component\\Routing\\Loader\\PhpFileLoader` If you use the :class:`Symfony\\Component\\Routing\\Loader\\PhpFileLoader` you -have to provide the name of a PHP file which returns a callable handling a :class:`Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator`. +have to provide the name of a PHP file which returns a callable handling a +:class:`Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator`. This class allows to chain imports, collections or simple route definition calls:: // RouteProvider.php @@ -328,8 +229,8 @@ This class allows to chain imports, collections or simple route definition calls ; }; -Routes as Closures -.................. +Closure Routing Loaders +~~~~~~~~~~~~~~~~~~~~~~~ There is also the :class:`Symfony\\Component\\Routing\\Loader\\ClosureLoader`, which calls a closure and uses the result as a :class:`Symfony\\Component\\Routing\\RouteCollection`:: @@ -343,8 +244,8 @@ calls a closure and uses the result as a :class:`Symfony\\Component\\Routing\\Ro $loader = new ClosureLoader(); $routes = $loader->load($closure); -Routes as Annotations -..................... +Annotation Routing Loaders +~~~~~~~~~~~~~~~~~~~~~~~~~~ Last but not least there are :class:`Symfony\\Component\\Routing\\Loader\\AnnotationDirectoryLoader` and @@ -392,155 +293,6 @@ automatically in the background if you want to use it. A basic example of the are saved in the ``cache_dir``. This means your script must have write permissions for that location. -.. _routing-unicode-support: - -Unicode Routing Support -~~~~~~~~~~~~~~~~~~~~~~~ - -The Routing component supports UTF-8 characters in route paths and requirements. -Thanks to the ``utf8`` route option, you can make Symfony match and generate -routes with UTF-8 characters: - -.. configuration-block:: - - .. code-block:: php-annotations - - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class DefaultController extends AbstractController - { - /** - * @Route("/category/{name}", name="route1", utf8=true) - */ - public function category() - { - // ... - } - - .. code-block:: yaml - - route1: - path: /category/{name} - controller: App\Controller\DefaultController::category - utf8: true - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\DefaultController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('route1', '/category/{name}') - ->controller([DefaultController::class, 'category']) - ->utf8() - ; - }; - -.. versionadded:: 4.3 - - The ``utf8`` option/method has been introduced in Symfony 4.3. - Before you had to use the ``options`` setting to define this value (for - example, when using annotations: ``options={"utf8": true}``). - -In this route, the ``utf8`` option set to ``true`` makes Symfony consider the -``.`` requirement to match any UTF-8 characters instead of just a single -byte character. This means that so the following URLs would match: -``/category/日本語``, ``/category/فارسی``, ``/category/한국어``, etc. In case you -are wondering, this option also allows to include and match emojis in URLs. - -You can also include UTF-8 strings as routing requirements: - -.. configuration-block:: - - .. code-block:: php-annotations - - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class DefaultController extends AbstractController - { - /** - * @Route( - * "/category/{name}", - * name="route2", - * defaults={"name": "한국어"}, - * utf8=true - * ) - */ - public function category() - { - // ... - } - - .. code-block:: yaml - - route2: - path: /category/{name} - controller: App\Controller\DefaultController::category - defaults: - name: "한국어" - utf8: true - - .. code-block:: xml - - - - - - 한국어 - - - - .. code-block:: php - - // config/routes.php - use App\Controller\DefaultController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('route2', '/category/{name}') - ->controller([DefaultController::class, 'category']) - ->defaults([ - 'name' => '한국어', - ]) - ->utf8() - ; - }; - -.. tip:: - - In addition to UTF-8 characters, the Routing component also supports all - the `PCRE Unicode properties`_, which are escape sequences that match - generic character types. For example, ``\p{Lu}`` matches any uppercase - character in any language, ``\p{Greek}`` matches any Greek character, - ``\P{Han}`` matches any character not included in the Chinese Han script. - Learn more ---------- @@ -554,4 +306,3 @@ Learn more /controller/* .. _Packagist: https://packagist.org/packages/symfony/routing -.. _PCRE Unicode properties: http://php.net/manual/en/regexp.reference.unicode.php diff --git a/console/request_context.rst b/console/request_context.rst deleted file mode 100644 index f2fbb505cf2..00000000000 --- a/console/request_context.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. index:: - single: Console; Generating URLs - -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 -console command you'll probably end up with something like ``http://localhost/foo/bar`` -which is not very useful. - -To fix this, you need to configure the "request context", which is a fancy -way of saying that you need to configure your environment so that it knows -what URL it should use when generating URLs. - -There are two ways of configuring the request context: at the application level -and per Command. - -Configuring the Request Context Globally ----------------------------------------- - -To configure the Request Context - which is used by the URL Generator - you can -redefine the parameters it uses as default values to change the default host -(``localhost``) and scheme (``http``). You can also configure the base path (both for -the URL generator and the assets) if Symfony is not running in the root directory. - -Note that this does not impact URLs generated via normal web requests, since those -will override the defaults. - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - parameters: - router.request_context.host: 'example.org' - router.request_context.scheme: 'https' - router.request_context.base_url: 'my/path' - asset.request_context.base_path: '%router.request_context.base_url%' - asset.request_context.secure: true - - .. code-block:: xml - - - - - - - example.org - https - my/path - %router.request_context.base_url% - true - - - - - .. code-block:: php - - // config/services.php - $container->setParameter('router.request_context.host', 'example.org'); - $container->setParameter('router.request_context.scheme', 'https'); - $container->setParameter('router.request_context.base_url', 'my/path'); - $container->setParameter('asset.request_context.base_path', $container->getParameter('router.request_context.base_url')); - $container->setParameter('asset.request_context.secure', true); - -Configuring the Request Context per Command -------------------------------------------- - -To change it only in one command you need to fetch the Request Context -from the ``router`` service and override its settings:: - - // src/Command/DemoCommand.php - use Symfony\Component\Routing\RouterInterface; - // ... - - class DemoCommand extends Command - { - private $router; - - public function __construct(RouterInterface $router) - { - parent::__construct(); - - $this->router = $router; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $context = $this->router->getContext(); - $context->setHost('example.com'); - $context->setScheme('https'); - $context->setBaseUrl('my/path'); - - $url = $this->router->generate('route-name', ['param-name' => 'param-value']); - // ... - } - } diff --git a/controller.rst b/controller.rst index 0d7c225007a..2513b5d3faf 100644 --- a/controller.rst +++ b/controller.rst @@ -125,6 +125,8 @@ method is just a helper method that generates the URL for a given route:: $url = $this->generateUrl('app_lucky_number', ['max' => 10]); +.. _controller-redirect: + Redirecting ~~~~~~~~~~~ @@ -297,7 +299,7 @@ use: .. code-block:: terminal $ php bin/console make:crud Product - + created: src/Controller/ProductController.php created: src/Form/ProductType.php created: templates/product/_delete_form.html.twig diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 2889c275895..b93992ac464 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1110,7 +1110,7 @@ strict_requirements **type**: ``mixed`` **default**: ``true`` Determines the routing generator behavior. When generating a route that -has specific :doc:`requirements `, the generator +has specific :ref:`parameter requirements `, the generator can behave differently in case the used parameters do not meet these requirements. The value can be one of: diff --git a/reference/forms/types/options/method.rst.inc b/reference/forms/types/options/method.rst.inc index 0d86d1f47a0..b20fbc3f026 100644 --- a/reference/forms/types/options/method.rst.inc +++ b/reference/forms/types/options/method.rst.inc @@ -19,7 +19,7 @@ is used to decide whether to process the form submission in the When the method is PUT, PATCH, or DELETE, Symfony will automatically render a ``_method`` hidden field in your form. This is used to "fake" these HTTP methods, as they're not supported on standard browsers. This can - be useful when using :ref:`method routing requirements `. + be useful when :ref:`matching routes by HTTP method `. .. note:: diff --git a/routing.rst b/routing.rst index 36400f8609c..b5180bb4598 100644 --- a/routing.rst +++ b/routing.rst @@ -4,38 +4,31 @@ Routing ======= -Beautiful URLs are a must for any serious web application. This means leaving -behind ugly URLs like ``index.php?article_id=57`` in favor of something like -``/read/intro-to-symfony``. - -Having flexibility is even more important. What if you need to change the -URL of a page from ``/blog`` to ``/news``? How many links would you need to -hunt down and update to make the change? If you're using Symfony's router, -the change should be trivial. - -.. index:: - single: Routing; Basics +When your application receives a request, it executes a +:doc:`controller action ` to generate the response. The routing +configuration defines which action to run for each incoming URL. It also +provides other useful features, like generating SEO-friendly URLs (e.g. +``/read/intro-to-symfony`` instead of ``index.php?article_id=57``). .. _routing-creating-routes: Creating Routes --------------- -A *route* is a map from a URL path to attributes (i.e a controller). Suppose -you want one route that matches ``/blog`` exactly and another more dynamic -route that can match *any* URL like ``/blog/my-post`` or -``/blog/all-about-symfony``. - Routes can be configured in YAML, XML, PHP or using annotations. All formats -provide the same features and performance, so choose the one you prefer. If you -choose PHP annotations, run this command once in your application to add support -for them: +provide the same features and performance, so choose your favorite. +:doc:`Symfony recommends annotations ` +because it's convenient to put the route and controller in the +same place instead of dealing with multiple files. + +If you choose annotations, run this command once in your application to add +support for them: .. code-block:: terminal $ composer require annotations -Now you can configure the routes: +Suppose you want to define a route for the ``/blog`` URL in your application: .. configuration-block:: @@ -50,43 +43,25 @@ Now you can configure the routes: class BlogController extends AbstractController { /** - * Matches /blog exactly - * * @Route("/blog", name="blog_list") */ public function list() { // ... } - - /** - * Matches /blog/* - * but not /blog/slug/extra-part - * - * @Route("/blog/{slug}", name="blog_show") - */ - public function show($slug) - { - // $slug will equal the dynamic part of the URL - // e.g. at /blog/yay-routing, then $slug='yay-routing' - - // ... - } } .. code-block:: yaml # config/routes.yaml blog_list: - # Matches /blog exactly - path: /blog + path: /blog + # the controller value has the format 'controller_class::method_name' controller: App\Controller\BlogController::list - blog_show: - # Matches /blog/* - # but not /blog/slug/extra-part - path: /blog/{slug} - controller: App\Controller\BlogController::show + # if the action is implemented as the __invoke() method of the + # controller class, you can skip the '::method_name' part: + # controller: App\Controller\BlogController .. code-block:: xml @@ -97,16 +72,13 @@ Now you can configure the routes: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - - - - + + - - - - - + .. code-block:: php @@ -116,77 +88,151 @@ Now you can configure the routes: use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes) { - // Matches /blog exactly $routes->add('blog_list', '/blog') + // the controller value has the format [controller_class, method_name] ->controller([BlogController::class, 'list']) - ; - // Matches /blog/* - // but not /blog/slug/extra-part - $routes->add('blog_show', '/blog/{slug}') - ->controller([BlogController::class, 'show']) + + // if the action is implemented as the __invoke() method of the + // controller class, you can skip the ', method_name]' part: + // ->controller([BlogController::class]) ; }; -Thanks to these two routes: +This configuration defines a route called ``blog_list`` that matches when the +user requests the ``/blog`` URL. When the match occurs, the application runs +the ``list()`` method of the ``BlogController`` class. -* If the user goes to ``/blog``, the first route is matched and ``list()`` - is executed; +.. note:: -* If the user goes to ``/blog/*``, the second route is matched and ``show()`` - is executed. Because the route path is ``/blog/{slug}``, a ``$slug`` variable - is passed to ``show()`` matching that value. For example, if the user goes to - ``/blog/yay-routing``, then ``$slug`` will equal ``yay-routing``. + The query string of a URL is not considered when matching routes. In this + example, URLs like ``/blog?foo=bar`` and ``/blog?foo=bar&bar=foo`` will + also match the ``blog_list`` route. -Whenever you have a ``{placeholder}`` in your route path, that portion becomes -a wildcard: it matches *any* value. Your controller can now *also* have an -argument called ``$placeholder`` (the wildcard and argument names *must* -match). +The route name (``blog_list``) is not important for now, but it will be essential later when +:ref:`generating URLs `. You only have to keep in mind +that each route name must be unique in the application. -.. caution:: +.. _routing-matching-http-methods: - However, the slash ``/`` is ignored by default in placeholder values because - the router uses it as separator between different placeholders. - To learn more about this, you can read - :ref:`routing/slash_in_parameter`. +Matching HTTP Methods +~~~~~~~~~~~~~~~~~~~~~ -Each route also has an internal name: ``blog_list`` and ``blog_show``. These can -be anything (as long as each is unique) and don't have any meaning yet. You'll -use them later to :ref:`generate URLs `. +By default, routes match any HTTP verb (``GET``, ``POST``, ``PUT``, etc.) +Use the ``methods`` option to restrict the verbs each route should respond to: -.. sidebar:: Routing in Other Formats +.. configuration-block:: - The ``@Route`` above each method is called an *annotation*. If you'd rather - configure your routes in YAML, XML or PHP, that's no problem! Create a new - routing file (e.g. ``routes.xml``) in the ``config/`` directory and Symfony - will automatically use it. + .. code-block:: php-annotations -.. _i18n-routing: + // src/Controller/BlogApiController.php + namespace App\Controller; -Localized Routing (i18n) ------------------------- + // ... + + class BlogApiController extends AbstractController + { + /** + * @Route("/api/posts/{id}", methods={"GET","HEAD"}) + */ + public function show(int $id) + { + // ... return a JSON response with the post + } + + /** + * @Route("/api/posts/{id}", methods={"PUT"}) + */ + public function edit(int $id) + { + // ... edit a post + } + } + + .. code-block:: yaml + + # config/routes.yaml + api_post_show: + path: /api/posts/{id} + controller: App\Controller\BlogApiController::show + methods: GET|HEAD + + api_post_edit: + path: /api/posts/{id} + controller: App\Controller\BlogApiController::edit + methods: PUT + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/routes.php + use App\Controller\BlogApiController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('api_post_show', '/api/posts/{id}') + ->controller([BlogApiController::class, 'show']) + ->methods(['GET', 'HEAD']) + ; + $routes->add('api_post_edit', '/api/posts/{id}') + ->controller([BlogApiController::class, 'edit']) + ->methods(['PUT']) + ; + }; + +.. tip:: + + HTML forms only support ``GET`` and ``POST`` methods. If you're calling a + route with a different method from an HTML form, add a hidden field called + ``_method`` with the method to use (e.g. ````). + If you create your forms with :doc:`Symfony Forms ` this is done + automatically for you. -Routes can be localized to provide unique paths per :doc:`locale `. -Symfony provides a handy way to declare localized routes without duplication. +Matching Expressions +~~~~~~~~~~~~~~~~~~~~ + +Use the ``condition`` option if you need some route to match based on some +arbitrary matching logic: .. configuration-block:: .. code-block:: php-annotations - // src/Controller/CompanyController.php + // src/Controller/DefaultController.php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; - class CompanyController extends AbstractController + class DefaultController extends AbstractController { /** - * @Route({ - * "nl": "/over-ons", - * "en": "/about-us" - * }, name="about_us") + * @Route( + * "/contact", + * name="contact", + * condition="context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" + * ) + * + * expressions can also include config parameters: + * condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'" */ - public function about() + public function contact() { // ... } @@ -195,11 +241,12 @@ Symfony provides a handy way to declare localized routes without duplication. .. code-block:: yaml # config/routes.yaml - about_us: - path: - nl: /over-ons - en: /about-us - controller: App\Controller\CompanyController::about + contact: + path: /contact + controller: 'App\Controller\DefaultController::contact' + condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" + # expressions can also include config parameters: + # condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'" .. code-block:: xml @@ -210,103 +257,191 @@ Symfony provides a handy way to declare localized routes without duplication. xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - - /over-ons - /about-us + + context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i' + + .. code-block:: php // config/routes.php - use App\Controller\CompanyController; + use App\Controller\DefaultController; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes) { - $routes->add('about_us', [ - 'nl' => '/over-ons', - 'en' => '/about-us', - ]) - ->controller([CompanyController::class, 'about']) + $routes->add('contact', '') + ->controller([DefaultController::class, 'contact']) + ->condition('context.getMethod() in ["GET", "HEAD"] and request.headers.get("User-Agent") matches "/firefox/i"') + // expressions can also include config parameters: + // 'request.headers.get("User-Agent") matches "%app.allowed_browsers%"' ; }; -When a localized route is matched Symfony automatically knows which locale -should be used during the request. Defining routes this way also eliminated the -need for duplicate registration of routes which minimizes the risk for any bugs -caused by definition inconsistency. +The value of the ``condition`` option is any valid +:doc:`ExpressionLanguage expression ` +and can use and of these variables created by Symfony: -.. tip:: +``context`` + An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, + which holds the most fundamental information about the route being matched. - If the application uses full language + territory locales (e.g. ``fr_FR``, - ``fr_BE``), you can use the language part only in your routes (e.g. ``fr``). - This prevents having to define multiple paths when you want to use the same - route path for locales that share the same language. +``request`` + The :ref:`Symfony Request ` object that + represents the current request. -A common requirement for internationalized applications is to prefix all routes -with a locale. This can be done by defining a different prefix for each locale -(and setting an empty prefix for your default locale if you prefer it): +Behind the scenes, expressions are compiled down to raw PHP. Because of this, +using the ``condition`` key causes no extra overhead beyond the time it takes +for the underlying PHP to execute. + +.. caution:: + + Conditions are *not* taken into account when generating URLs (which is + explained later in this article). + +Debugging Routes +~~~~~~~~~~~~~~~~ + +As your application grows, you'll eventually have a *lot* of routes. Symfony +includes some commands to help you debug routing issues. First, the ``debug:router`` +command lists all your application routes in the same order in which Symfony +evaluates them: + +.. code-block:: terminal + + $ php bin/console debug:router + + ---------------- ------- ------- ----- -------------------------------------------- + Name Method Scheme Host Path + ---------------- ------- ------- ----- -------------------------------------------- + homepage ANY ANY ANY / + contact GET ANY ANY /contact + contact_process POST ANY ANY /contact + article_show ANY ANY ANY /articles/{_locale}/{year}/{title}.{_format} + blog ANY ANY ANY /blog/{page} + blog_show ANY ANY ANY /blog/{slug} + ---------------- ------- ------- ----- -------------------------------------------- + +Pass the name (or part of the name) of some route to this argument to print the +route details: + +.. code-block:: terminal + + $ php bin/console debug:router app_lucky_number + + +-------------+---------------------------------------------------------+ + | Property | Value | + +-------------+---------------------------------------------------------+ + | Route Name | app_lucky_number | + | Path | /lucky/number/{max} | + | ... | ... | + | Options | compiler_class: Symfony\Component\Routing\RouteCompiler | + | | utf8: true | + +-------------+---------------------------------------------------------+ + +The other command is called ``router:match`` and it shows which route will match +the given URL. It's useful to find out why some URL is not executing the +controller action that you expect: + +.. code-block:: terminal + + $ php bin/console router:match /lucky/number/8 + + [OK] Route "app_lucky_number" matches + +Route Parameters +---------------- + +The previous examples defined routes where the URL never changes (e.g. ``/blog``). +However, it's common to define routes where some parts are variable. For example, +the URL to display some blog post will probably include the title or slug +(e.g. ``/blog/my-first-post`` or ``/blog/all-about-symfony``). + +In Symfony routes, variable parts are wrapped in ``{ ... }`` and they must have +a unique name. For example, the route to display the blog post contents is +defined as ``/blog/{slug}``: .. configuration-block:: + .. code-block:: php-annotations + + // src/Controller/BlogController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class BlogController extends AbstractController + { + // ... + + /** + * @Route("/blog/{slug}", name="blog_show") + */ + public function show(string $slug) + { + // $slug will equal the dynamic part of the URL + // e.g. at /blog/yay-routing, then $slug='yay-routing' + + // ... + } + } + .. code-block:: yaml - # config/routes/annotations.yaml - controllers: - resource: '../../src/Controller/' - type: annotation - prefix: - en: '' # don't prefix URLs for English, the default locale - nl: '/nl' + # config/routes.yaml + blog_show: + path: /blog/{slug} + controller: App\Controller\BlogController::show .. code-block:: xml - + - - - - /nl - + + .. code-block:: php - // config/routes/annotations.php + // config/routes.php + use App\Controller\BlogController; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes) { - $routes->import('../src/Controller/', 'annotation') - ->prefix([ - // don't prefix URLs for English, the default locale - 'en' => '', - 'nl' => '/nl' - ]) + $routes->add('blog_show', '/blog/{slug}') + ->controller([BlogController::class, 'show']) ; }; -.. _routing-requirements: +The name of the variable part (``{slug}`` in this example) is used to create a +PHP variable where that route content is stored and passed to the controller. +If a user visits the ``/blog/my-first-post`` URL, Symfony executes the ``show()`` +method in the ``BlogController`` class and passes a ``$slug = 'my-first-post'`` +argument to the ``show()`` method. -Adding {wildcard} Requirements ------------------------------- +Routes can define any number of parameters, but each of them can only be used +once on each route (e.g. ``/blog/posts-about-{category}/page/{pageNumber}``). -Imagine the ``blog_list`` route will contain a paginated list of blog posts, with -URLs like ``/blog/2`` and ``/blog/3`` for pages 2 and 3. If you change the route's -path to ``/blog/{page}``, you'll have a problem: +.. _routing-requirements: -* blog_list: ``/blog/{page}`` will match ``/blog/*``; -* blog_show: ``/blog/{slug}`` will *also* match ``/blog/*``. +Parameters Validation +~~~~~~~~~~~~~~~~~~~~~ -When two routes match the same URL, the *first* route that's loaded wins. Unfortunately, -that means that ``/blog/yay-routing`` will match the ``blog_list``. No good! +Imagine that your application has a ``blog_show`` route (URL: ``/blog/{slug}``) +and a ``blog_list`` route (URL: ``/blog/{page}``). Given that route parameters +accept any value, there's no way to differentiate both routes. -To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match numbers -(digits): +If the user requests ``/blog/my-first-post``, both routes will match and Symfony +will use the route which was defined first. To fix this, add some validation to +the ``{page}`` parameter using the ``requirements`` option: .. configuration-block:: @@ -323,7 +458,7 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n /** * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) */ - public function list($page) + public function list(int $page) { // ... } @@ -341,13 +476,14 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n # config/routes.yaml blog_list: - path: /blog/{page} + path: /blog/{page} controller: App\Controller\BlogController::list requirements: page: '\d+' blog_show: - # ... + path: /blog/{slug} + controller: App\Controller\BlogController::show .. code-block:: xml @@ -362,7 +498,9 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n \d+ - + + .. code-block:: php @@ -376,20 +514,45 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n ->controller([BlogController::class, 'list']) ->requirements(['page' => '\d+']) ; + + $routes->add('blog_show', '/blog/{slug}') + ->controller([BlogController::class, 'show']) + ; // ... }; -The ``\d+`` is a regular expression that matches a *digit* of any length. Now: +The ``requirements`` option defines the `PHP regular expressions`_ that route +parameters must match for the entire route to match. In this example, ``\d+`` is +a regular expression that matches a *digit* of any length. Now: ======================== ============= =============================== URL Route Parameters ======================== ============= =============================== ``/blog/2`` ``blog_list`` ``$page`` = ``2`` -``/blog/yay-routing`` ``blog_show`` ``$slug`` = ``yay-routing`` +``/blog/my-first-post`` ``blog_show`` ``$slug`` = ``my-first-post`` ======================== ============= =============================== -If you prefer, requirements can be inlined in each placeholder using the syntax -``{placeholder_name}``. This feature makes configuration more +.. tip:: + + Route requirements (and route paths too) can include + :ref:`container parameters `, which is useful to + define complex regular expressions once and reuse them in multiple routes. + +.. tip:: + + Parameters also support `PCRE Unicode properties`_, which are escape + sequences that match generic character types. For example, ``\p{Lu}`` + matches any uppercase character in any language, ``\p{Greek}`` matches any + Greek character, etc. + +.. note:: + + When using regular expressions in route parameters, you can set the ``utf8`` + route option to ``true`` to make any ``.`` character match any UTF-8 + characters instead of just a single byte. + +If you prefer, requirements can be inlined in each parameter using the syntax +``{parameter_name}``. This feature makes configuration more concise, but it can decrease route readability when requirements are complex: .. configuration-block:: @@ -407,7 +570,7 @@ concise, but it can decrease route readability when requirements are complex: /** * @Route("/blog/{page<\d+>}", name="blog_list") */ - public function list($page) + public function list(int $page) { // ... } @@ -417,7 +580,7 @@ concise, but it can decrease route readability when requirements are complex: # config/routes.yaml blog_list: - path: /blog/{page<\d+>} + path: /blog/{page<\d+>} controller: App\Controller\BlogController::list .. code-block:: xml @@ -429,7 +592,8 @@ concise, but it can decrease route readability when requirements are complex: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - + @@ -447,19 +611,17 @@ concise, but it can decrease route readability when requirements are complex: // ... }; -To learn about other route requirements - like HTTP method, hostname and dynamic -expressions - see :doc:`/routing/requirements`. +Optional Parameters +~~~~~~~~~~~~~~~~~~~ -Giving {placeholders} a Default Value -------------------------------------- - -In the previous example, the ``blog_list`` has a path of ``/blog/{page}``. If -the user visits ``/blog/1``, it will match. But if they visit ``/blog``, it -will **not** match. As soon as you add a ``{placeholder}`` to a route, it -*must* have a value. +In the previous example, the URL of ``blog_list`` is ``/blog/{page}``. If users +visit ``/blog/1``, it will match. But if they visit ``/blog``, it will **not** +match. As soon as you add a parameter to a route, it must have a value. -So how can you make ``blog_list`` once again match when the user visits -``/blog``? By adding a *default* value: +You can make ``blog_list`` once again match when the user visits ``/blog`` by +adding a default value for the ``{page}`` parameter. When using annotations, +default values are defined in the arguments of the controller action. In the +other configuration formats they are defined with the ``defaults`` option: .. configuration-block:: @@ -476,7 +638,7 @@ So how can you make ``blog_list`` once again match when the user visits /** * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) */ - public function list($page = 1) + public function list(int $page = 1) { // ... } @@ -486,7 +648,7 @@ So how can you make ``blog_list`` once again match when the user visits # config/routes.yaml blog_list: - path: /blog/{page} + path: /blog/{page} controller: App\Controller\BlogController::list defaults: page: 1 @@ -531,9 +693,21 @@ So how can you make ``blog_list`` once again match when the user visits Now, when the user visits ``/blog``, the ``blog_list`` route will match and ``$page`` will default to a value of ``1``. +.. caution:: + + You can have more than one optional parameter (e.g. ``/blog/{slug}/{page}``), + but everything after an optional parameter must be optional. For example, + ``/{page}/blog`` is a valid path, but ``page`` will always be required + (i.e. ``/blog`` will not match this route). + +.. note:: + + Routes with optional parameters at the end will not match on requests + with a trailing slash (i.e. ``/blog/`` will not match, ``/blog`` will match). + If you want to always include some default value in the generated URL (for example to force the generation of ``/blog/1`` instead of ``/blog`` in the -previous example) add the ``!`` character before the placeholder name: ``/blog/{!page}`` +previous example) add the ``!`` character before the parameter name: ``/blog/{!page}`` .. versionadded:: 4.3 @@ -541,9 +715,9 @@ previous example) add the ``!`` character before the placeholder name: ``/blog/{ introduced in Symfony 4.3. As it happens with requirements, default values can also be inlined in each -placeholder using the syntax ``{placeholder_name?default_value}``. This feature +parameter using the syntax ``{parameter_name?default_value}``. This feature is compatible with inlined requirements, so you can inline both in a single -placeholder: +parameter: .. configuration-block:: @@ -560,7 +734,7 @@ placeholder: /** * @Route("/blog/{page<\d+>?1}", name="blog_list") */ - public function list($page) + public function list(int $page) { // ... } @@ -570,7 +744,7 @@ placeholder: # config/routes.yaml blog_list: - path: /blog/{page<\d+>?1} + path: /blog/{page<\d+>?1} controller: App\Controller\BlogController::list .. code-block:: xml @@ -582,7 +756,8 @@ placeholder: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - + @@ -601,35 +776,86 @@ placeholder: .. tip:: - To give a ``null`` default value to any placeholder, add nothing after the + To give a ``null`` default value to any parameter, add nothing after the ``?`` character (e.g. ``/blog/{page?}``). -Listing all of your Routes --------------------------- +Parameter Conversion +~~~~~~~~~~~~~~~~~~~~ -As your app grows, you'll eventually have a *lot* of routes! To see them all, run: +A common routing need is to convert the value stored in some parameter (e.g. an +integer acting as the user ID) into another value (e.g. the object that +represents the user). This feature is called "param converter" and is only +available when using annotations to define routes. + +In case you didn't run this command before, run it now to add support for +annotations and "param converters": .. code-block:: terminal - $ php bin/console debug:router + $ composer require annotations - ------------------------------ -------- ------------------------------------- - Name Method Path - ------------------------------ -------- ------------------------------------- - app_lucky_number ANY /lucky/number/{max} - ... - ------------------------------ -------- ------------------------------------- +Now, keep the previous route configuration, but change the arguments of the +controller action. Instead of ``string $slug``, add ``BlogPost $post``:: -.. index:: - single: Routing; Advanced example - single: Routing; _format parameter + // src/Controller/BlogController.php + namespace App\Controller; -.. _advanced-routing-example: + use App\Entity\BlogPost; + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; -Advanced Routing Example ------------------------- + class BlogController extends AbstractController + { + // ... + + /** + * @Route("/blog/{slug}", name="blog_show") + */ + public function show(BlogPost $post) + { + // $post is the object whose slug matches the routing parameter + + // ... + } + } + +If your controller arguments include type-hints for objects (``BlogPost`` in +this case), the "param converter" makes a database request to find the object +using the request parameters (``slug`` in this case). If no object is found, +Symfony generates a 404 response automatically. + +Read the `full param converter documentation`_ to learn about the converters +provided by Symfony and how to configure them. + +Special Parameters +~~~~~~~~~~~~~~~~~~ + +In addition to your own parameters, routes can include any of the following +special parameters created by Symfony: + +``_controller`` + This parameter is used to determine which controller and action is executed + when the route is matched. -With all of this in mind, check out this advanced example: +.. _routing-format-parameter: + +``_format`` + The matched value is used to set the "request format" of the ``Request`` object. + This is used for such things as setting the ``Content-Type`` of the response + (e.g. a ``json`` format translates into a ``Content-Type`` of ``application/json``). + +``_fragment`` + Used to set the fragment identifier, which is the optional last part of a URL that + starts with a ``#`` character and is used to identify a portion of a document. + +.. _routing-locale-parameter: + +``_locale`` + Used to set the :ref:`locale ` on the request. + +You can include these attributes (except ``_fragment``) both in individual routes +and in route imports. Symfony defines some special attributes with the same name +(except for the leading underscore) so you can define them easier: .. configuration-block:: @@ -642,19 +868,16 @@ With all of this in mind, check out this advanced example: { /** * @Route( - * "/articles/{_locale}/{year}/{slug}.{_format}", - * defaults={ - * "_locale": "en", - * "_format": "html" - * }, + * "/articles/{_locale}/search.{_format}", + * locale="en", + * format="html", * requirements={ * "_locale": "en|fr", - * "_format": "html|rss", - * "year": "\d+" + * "_format": "html|xml", * } * ) */ - public function show($_locale, $year, $slug) + public function search() { } } @@ -662,16 +885,14 @@ With all of this in mind, check out this advanced example: .. code-block:: yaml # config/routes.yaml - article_show: - path: /articles/{_locale}/{year}/{slug}.{_format} - controller: App\Controller\ArticleController::show - defaults: - _locale: en - _format: html - requirements: - _locale: en|fr - _format: html|rss - year: \d+ + article_search: + path: /articles/{_locale}/search.{_format} + controller: App\Controller\ArticleController::search + locale: en + format: html + requirements: + _locale: en|fr + _format: html|xml .. code-block:: xml @@ -682,15 +903,14 @@ With all of this in mind, check out this advanced example: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - + - en - html en|fr html|rss - \d+ @@ -698,310 +918,1308 @@ With all of this in mind, check out this advanced example: .. code-block:: php // config/routes.php + namespace Symfony\Component\Routing\Loader\Configurator; + use App\Controller\ArticleController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes) { - $routes->add('article_show', '/articles/{_locale}/{year}/{slug}.{_format}') - ->controller([ArticleController::class, 'show']) - ->defaults([ - '_locale' => 'en', - '_format' => 'html', - ]) + $routes->add('article_show', '/articles/{_locale}/search.{_format}') + ->controller([ArticleController::class, 'search']) + ->locale('en') + ->format('html') ->requirements([ '_locale' => 'en|fr', '_format' => 'html|rss', - 'year' => '\d+', ]) ; }; -As you've seen, this route will only match if the ``{_locale}`` portion of -the URL is either ``en`` or ``fr`` and if the ``{year}`` is a number. This -route also shows how you can use a dot between placeholders instead of -a slash. URLs matching this route might look like: - -* ``/articles/en/2010/my-post`` -* ``/articles/fr/2010/my-post.rss`` -* ``/articles/en/2013/my-latest-post.html`` +.. versionadded:: 4.3 + + The special attributes were introduced in Symfony 4.3. + +Extra Parameters +~~~~~~~~~~~~~~~~ + +In the ``defaults`` option of a route you can optionally define parameters not +included in the route configuration. This is useful to pass extra arguments to +the controllers of the routes: + +.. configuration-block:: + + .. code-block:: php-annotations + + use Symfony\Component\Routing\Annotation\Route; + + class BlogController + { + /** + * @Route("/blog/{page}", name="blog_index", defaults={"page": 1, "title": "Hello world!"}) + */ + public function index(int $page, string $title) + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + blog_index: + path: /blog/{page} + controller: App\Controller\BlogController::index + defaults: + page: 1 + title: "Hello world!" + + .. code-block:: xml + + + + + + + 1 + Hello world! + + + + .. code-block:: php + + // config/routes.php + use App\Controller\BlogController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('blog_index', '/blog/{page}') + ->controller([BlogController::class, 'index']) + ->defaults([ + 'page' => 1, + 'title' => 'Hello world!', + ]) + ; + }; + +.. _routing-slash-in-parameters: + +Slash Characters in Route Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Route parameters can contain any values except the ``/`` slash character, +because that's the character used to separate the different parts of the URLs. +For example, if the ``token`` value in the ``/share/{token}`` route contains a +``/`` character, this route won't match. + +A possible solution is to change the parameter requirements to be more permissive: + +.. configuration-block:: + + .. code-block:: php-annotations + + use Symfony\Component\Routing\Annotation\Route; + + class DefaultController + { + /** + * @Route("/share/{token}", name="share", requirements={"token"=".+"}) + */ + public function share($token) + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + share: + path: /share/{token} + controller: App\Controller\DefaultController::share + requirements: + token: .+ + + .. code-block:: xml + + + + + + + .+ + + + + .. code-block:: php + + // config/routes.php + use App\Controller\DefaultController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('share', '/share/{token}') + ->controller([DefaultController::class, 'share']) + ->requirements([ + 'token' => '.+', + ]) + ; + }; + +.. note:: + + If the route defines several parameter and you apply this permissive + regular expression to all of them, the results won't be the expected. For + example, if the route definition is ``/share/{path}/{token}`` and both + ``path`` and ``token`` accept ``/``, then ``path`` will contain its contents + and the token, and ``token`` will be empty. + +.. note:: + + If the route includes the special ``{_format}`` parameter, you shouldn't + use the ``.+`` requirement for the parameters that allow slashes. For example, + if the pattern is ``/share/{token}.{_format}`` and ``{token}`` allows any + character, the ``/share/foo/bar.json`` URL will consider ``foo/bar.json`` + as the token and the format will be empty. This can be solved by replacing + the ``.+`` requirement by ``[^.]+`` to allow any character except dots. + +.. _routing-route-groups: + +Route Groups and Prefixes +------------------------- + +It's common for a group of routes to share some options (e.g. all routes related +to the blog start with ``/blog``) That's why Symfony includes a feature to share +route configuration. + +When defining routes as annotations, put the common configuration in the +``@Route`` annotation of the controller class. In other routing formats, define +the common configuration using options when importing the routes. + +.. configuration-block:: + + .. code-block:: php-annotations + + use Symfony\Component\Routing\Annotation\Route; + + /** + * @Route("/blog", requirements={"locale": "en|es|fr"}, name="blog_") + */ + class BlogController + { + /** + * @Route("/{_locale}", name="index") + */ + public function index() + { + // ... + } + + /** + * @Route("/{_locale}/posts/{slug}", name="post") + */ + public function show(Post $post) + { + // ... + } + } + + .. code-block:: yaml + + # config/routes/annotations.yaml + controllers: + resource: '../src/Controller/' + type: annotation + # this is added to the beginning of all imported route URLs + prefix: '/blog' + # this is added to the beginning of all imported route names + name_prefix: 'blog_' + # these requirements are added to all imported routes + requirements: + locale: 'en|es|fr' + # An imported route with an empty URL will become "/blog/" + # Uncomment this option to make that URL "/blog" instead + # trailing_slash_on_root: false + + .. code-block:: xml + + + + + + + + + en|es|fr + + + + + + + + + .. code-block:: php + + // config/routes/annotations.php + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->import('../src/Controller/', 'annotation') + // this is added to the beginning of all imported route URLs + ->prefix('/blog') + // An imported route with an empty URL will become "/blog/" + // Pass FALSE as the second argument to make that URL "/blog" instead + // ->prefix('/blog', false) + // this is added to the beginning of all imported route names + ->namePrefix('blog_') + // these requirements are added to all imported routes + ->requirements(['locale' => 'en|es|fr']) + ; + }; + +In this example, the route of the ``index()`` action will be called ``blog_index`` +and its URL will be ``/blog/``. The route of the ``show()`` action will be called +``blog_post`` and its URL will be ``/blog/{_locale}/posts/{slug}``. Both routes +will also validate that the ``_locale`` parameter matches the regular expression +defined in the class annotation. + +.. seealso:: + + Symfony can :doc:`import routes from different sources ` + and you can even create your own route loader. + +Getting the Route Name and Parameters +------------------------------------- + +The ``Request`` object created by Symfony stores all the route configuration +(such as the name and parameters) in the "request attributes". You can get this +information in a controller via the ``Request`` object:: + + // src/Controller/BlogController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Routing\Annotation\Route; + + class BlogController extends AbstractController + { + /** + * @Route("/blog", name="blog_list") + */ + public function list(Request $request) + { + // ... + + $routeName = $request->attributes->get('_route'); + $routeParameters = $request->attributes->get('_route_params'); + + // use this to get all the available attributes (not only routing ones): + $allAttributes = $request->attributes->all(); + } + } + +You can get this information in services too injecting the ``request_stack`` +service to :doc:`get the Request object in a service `. +In Twig templates, use the :ref:`global app object ` +to get the request and its attributes: + +.. code-block:: twig + + {% set route_name = app.request.attributes('_route') %} + {% set route_parameters = app.request.attributes('_route_params') %} + + {# use this to get all the available attributes (not only routing ones) #} + {% set all_attributes = app.request.attributes.all %} + +Special Routes +-------------- + +Symfony defines some special controllers to render templates and redirect to +other routes from the route configuration so you don't have to create a +controller action. + +Rendering Templates +~~~~~~~~~~~~~~~~~~~ + +Use the ``TemplateController`` to render the template whose path is defined in +the ``template`` option: + +.. configuration-block:: + + .. code-block:: yaml + + # config/routes.yaml + about_us: + path: /site/about-us + controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction + defaults: + template: 'static_pages/about_us.html.twig' + + # optionally you can define some arguments passed to the template + site_name: 'ACME' + theme: 'dark' + + .. code-block:: xml + + + + + + + static_pages/about_us.html.twig + + + ACME + dark + + + + .. code-block:: php + + // config/routes.php + use App\Controller\DefaultController; + use Symfony\Bundle\FrameworkBundle\Controller\TemplateController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('about_us', '/site/about-us') + ->controller([TemplateController::class, 'templateAction']) + ->defaults([ + 'template' => 'static_pages/about_us.html.twig', + // optionally you can define some arguments passed to the template + 'site_name' => 'ACME', + 'theme' => 'dark', + ]) + ; + }; + +Redirecting to URLs and Routes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``RedirectController`` to redirect to other routes (``redirectAction``) +and URLs (``urlRedirectAction``): + +.. configuration-block:: + + .. code-block:: yaml + + # config/routes.yaml + doc_shortcut: + path: /doc + controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction + defaults: + route: 'doc_page' + # optionally you can define some arguments passed to the route + page: 'index' + version: 'current' + # redirections are temporary by default (code 302) but you can make them permanent (code 301) + permanent: true + # add this to keep the original query string parameters when redirecting + keepQueryParams: true + # add this to keep the HTTP method when redirecting. The redirect status changes + # * for temporary redirects, it uses the 307 status code instead of 302 + # * for permanent redirects, it uses the 308 status code instead of 301 + keepRequestMethod: true + + legacy_doc: + path: /legacy/doc + controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction + defaults: + # this value can be an absolute path or an absolute URL + path: 'https://legacy.example.com/doc' + permanent: true + + .. code-block:: xml + + + + + + + doc_page + + index + current + + true + + true + + true + + + + + https://legacy.example.com/doc + + true + + + + .. code-block:: php + + // config/routes.php + use App\Controller\DefaultController; + use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('doc_shortcut', '/doc') + ->controller([RedirectController::class, 'redirectAction']) + ->defaults([ + 'route' => 'doc_page', + // optionally you can define some arguments passed to the template + 'page' => 'index', + 'version' => 'current', + // redirections are temporary by default (code 302) but you can make them permanent (code 301) + 'permanent' => true, + // add this to keep the original query string parameters when redirecting + 'keepQueryParams' => true, + // add this to keep the HTTP method when redirecting. The redirect status changes: + // * for temporary redirects, it uses the 307 status code instead of 302 + // * for permanent redirects, it uses the 308 status code instead of 301 + 'keepRequestMethod' => true, + ]) + ; + + $routes->add('legacy_doc', '/legacy/doc') + ->controller([RedirectController::class, 'urlRedirectAction']) + ->defaults([ + // this value can be an absolute path or an absolute URL + 'path' => 'https://legacy.example.com/doc', + // redirections are temporary by default (code 302) but you can make them permanent (code 301) + 'permanent' => true, + ]) + ; + }; + +.. tip:: + + Symfony also provides some utilities to + :ref:`redirect inside controllers ` + +.. _routing-trailing-slash-redirection: + +Redirecting URLs with Trailing Slashes +...................................... + +Historically, URLs have followed the UNIX convention of adding trailing slashes +for directories (e.g. ``https://example.com/foo/``) and removing them to refer +to files (``https://example.com/foo``). Although serving different contents for +both URLs is OK, nowadays it's common to treat both URLs as the same URL and +redirect between them. + +Symfony follows this logic to redirect between URLs with and without trailing +slashes (but only for ``GET`` and ``HEAD`` requests): + +========== ======================================== ========================================== +Route URL If the requested URL is ``/foo`` If the requested URL is ``/foo/`` +========== ======================================== ========================================== +``/foo`` It matches (``200`` status response) It makes a ``301`` redirect to ``/foo`` +``/foo/`` It makes a ``301`` redirect to ``/foo/`` It matches (``200`` status response) +========== ======================================== ========================================== + +.. note:: + + If your application defines different routes for each path (``/foo`` and + ``/foo/``) this automatic redirection doesn't take place and the right + route is always matched. + +Sub-Domain Routing +------------------ + +Routes can configure a ``host`` option to require that the HTTP host of the +incoming requests matches some specific value. In the following example, both +routes match the same path (``/``) but one of them only responds to a specific +host name: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Controller/MainController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class MainController extends AbstractController + { + /** + * @Route("/", name="mobile_homepage", host="m.example.com") + */ + public function mobileHomepage() + { + // ... + } + + /** + * @Route("/", name="homepage") + */ + public function homepage() + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + mobile_homepage: + path: / + host: m.example.com + controller: App\Controller\MainController::mobileHomepage + + homepage: + path: / + controller: App\Controller\MainController::homepage + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/routes.php + use App\Controller\MainController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('mobile_homepage', '/') + ->controller([MainController::class, 'mobileHomepage']) + ->host('m.example.com') + ; + $routes->add('homepage', '/') + ->controller([MainController::class, 'homepage']) + ; + }; + + return $routes; + +The value of the ``host`` option can include parameters (which is useful in +multi-tenant applications) and these parameters can be validated too with +``requirements``: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Controller/MainController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class MainController extends AbstractController + { + /** + * @Route( + * "/", + * name="mobile_homepage", + * host="{subdomain}.example.com", + * defaults={"subdomain"="m"}, + * requirements={"subdomain"="m|mobile"} + * ) + */ + public function mobileHomepage() + { + // ... + } + + /** + * @Route("/", name="homepage") + */ + public function homepage() + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + mobile_homepage: + path: / + host: "{subdomain}.example.com" + controller: App\Controller\MainController::mobileHomepage + defaults: + subdomain: m + requirements: + subdomain: m|mobile + + homepage: + path: / + controller: App\Controller\MainController::homepage + + .. code-block:: xml + + + + + + + m + m|mobile + + + + + + .. code-block:: php + + // config/routes.php + use App\Controller\MainController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('mobile_homepage', '/') + ->controller([MainController::class, 'mobileHomepage']) + ->host('{subdomain}.example.com') + ->defaults([ + 'subdomain' => 'm', + ]) + ->requirements([ + 'subdomain' => 'm|mobile', + ]) + ; + $routes->add('homepage', '/') + ->controller([MainController::class, 'homepage']) + ; + }; + +In the above example, the ``domain`` parameter defines a default value because +otherwise you need to include a domain value each time you generate a URL using +these routes. + +.. tip:: + + You can also set the ``host`` option when :ref:`importing routes ` + to make all of them require that host name. + +.. note:: + + When using sub-domain routing, you must set the ``Host`` HTTP headers in + :doc:`functional tests ` or routes won't match:: + + $crawler = $client->request( + 'GET', + '/', + [], + [], + ['HTTP_HOST' => 'm.example.com'] + // or get the value from some container parameter: + // ['HTTP_HOST' => 'm.' . $client->getContainer()->getParameter('domain')] + ); + +.. _i18n-routing: + +Localized Routes (i18n) +----------------------- + +If your application is translated into multiple languages, each route can define +a different URL per each :doc:`translation locale `. This +avoids the need for duplicating routes, which also reduces the potential bugs: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Controller/CompanyController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class CompanyController extends AbstractController + { + /** + * @Route({ + * "en": "/about-us", + * "nl": "/over-ons" + * }, name="about_us") + */ + public function about() + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + about_us: + path: + en: /about-us + nl: /over-ons + controller: App\Controller\CompanyController::about + + .. code-block:: xml + + + + + + + /about-us + /over-ons + + + + .. code-block:: php + + // config/routes.php + use App\Controller\CompanyController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('about_us', [ + 'en' => '/about-us', + 'nl' => '/over-ons', + ]) + ->controller([CompanyController::class, 'about']) + ; + }; + +When a localized route is matched, Symfony uses the same locale automatically +during the entire request. + +.. tip:: + + When the application uses full "language + territory" locales (e.g. ``fr_FR``, + ``fr_BE``), if the URLs are the same in all related locales, routes can use + only the language part (e.g. ``fr``) to avoid repeating the same URLs. + +A common requirement for internationalized applications is to prefix all routes +with a locale. This can be done by defining a different prefix for each locale +(and setting an empty prefix for your default locale if you prefer it): + +.. configuration-block:: + + .. code-block:: yaml + + # config/routes/annotations.yaml + controllers: + resource: '../src/Controller/' + type: annotation + prefix: + en: '' # don't prefix URLs for English, the default locale + nl: '/nl' + + .. code-block:: xml + + + + + + + + + /nl + + + + .. code-block:: php + + // config/routes/annotations.php + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->import('../src/Controller/', 'annotation') + ->prefix([ + // don't prefix URLs for English, the default locale + 'en' => '', + 'nl' => '/nl' + ]) + ; + }; + +.. _routing-generating-urls: -.. _routing-format-param: - -.. sidebar:: The Special ``_format`` Routing Parameter +Generating URLs +--------------- - This example also highlights the special ``_format`` routing parameter. - When using this parameter, the matched value becomes the "request format" - of the ``Request`` object. +Routing systems are bidirectional: 1) they associate URLs with controllers (as +explained in the previous sections); 2) they generate URLs for a given route. +Generating URLs from routes allows you to not write the ```` +values manually in your HTML templates. Also, if the URL of some route changes, +you only have to update the route configuration and all links will be updated. - Ultimately, the request format is used for such things as setting the - ``Content-Type`` of the response (e.g. a ``json`` request format translates - into a ``Content-Type`` of ``application/json``). +To generate a URL, you need to specify the name of the route (e.g. +``blog_show``) and the values of the parameters defined by the route (e.g. +``slug = my-blog-post``). -.. note:: +For that reason each route has an internal name that must be unique in the +application. If you don't set the route name explicitly with the ``name`` +option, Symfony generates an automatic name based on the controller and action. - Sometimes you want to make certain parts of your routes globally configurable. - Symfony provides you with a way to do this by leveraging service container - parameters. Read more about this in ":doc:`/routing/service_container_parameters`". +Generating URLs in Controllers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Special Routing Parameters -~~~~~~~~~~~~~~~~~~~~~~~~~~ +If your controller extends from the :ref:`AbstractController `, +use the ``generateUrl()`` helper:: -As you've seen, each routing parameter or default value is eventually available -as an argument in the controller method. Additionally, there are four parameters -that are special: each adds a unique piece of functionality inside your application: + // src/Controller/BlogController.php + namespace App\Controller; -``_controller`` - As you've seen, this parameter is used to determine which controller is - executed when the route is matched. + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -``_format`` - Used to set the request format (:ref:`read more `). + class BlogController extends AbstractController + { + /** + * @Route("/blog", name="blog_list") + */ + public function list() + { + // ... -``_fragment`` - Used to set the fragment identifier, the optional last part of a URL that - starts with a ``#`` character and is used to identify a portion of a document. + // generate a URL with no route arguments + $signUpPage = $this->generateUrl('sign_up'); -``_locale`` - Used to set the locale on the request (:ref:`read more `). + // generate a URL with route arguments + $userProfilePage = $this->generateUrl('user_profile', [ + 'username' => $user->getUsername(), + ]); -You can also use special attributes to configure them (except ``_fragment``): + // generated URLs are "absolute paths" by default. Pass a third optional + // argument to generate different URLs (e.g. an "absolute URL") + $signUpPage = $this->generateUrl('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL); -.. configuration-block:: + // when a route is localized, Symfony uses by default the current request locale + // pass a different '_locale' value if you want to set the locale explicitly + $signUpPageInDutch = $this->generateUrl('sign_up', ['_locale' => 'nl']); + } + } - .. code-block:: php-annotations +.. note:: - // src/Controller/ArticleController.php + If you pass to the ``generateUrl()`` method some parameters that are not + part of the route definition, they are included in the generated URL as a + query string::: - // ... - class ArticleController extends AbstractController - { - /** - * @Route( - * "/articles/{_locale}/search.{_format}", - * locale="en", - * format="html", - * requirements={ - * "_locale": "en|fr", - * "_format": "html|xml", - * } - * ) - */ - public function search() - { - } - } + $this->generateUrl('blog', ['page' => 2, 'category' => 'Symfony']); + // the 'blog' route only defines the 'page' parameter; the generated URL is: + // /blog/2?category=Symfony - .. code-block:: yaml +If your controller does not extend from ``AbstractController``, you'll need to +:ref:`fetch services in your controller ` and +follow the instructions of the next section. - # config/routes.yaml - article_search: - path: /articles/{_locale}/search.{_format} - controller: App\Controller\ArticleController::search - locale: en - format: html - requirements: - _locale: en|fr - _format: html|xml +.. _routing-generating-urls-in-services: - .. code-block:: xml +Generating URLs in Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - - +Inject the ``router`` Symfony service into your own services and use its +``generate()`` method. When using :doc:`service autowiring ` +you only need to add an argument in the service constructor and type-hint it with +the :class:`Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface` class:: - + // src/Service/SomeService.php + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; - en|fr - html|rss + class SomeService + { + private $router; - - + public function __construct(UrlGeneratorInterface $router) + { + $this->router = $router; + } - .. code-block:: php + public function someMethod() + { + // ... - // config/routes.php - namespace Symfony\Component\Routing\Loader\Configurator; + // generate a URL with no route arguments + $signUpPage = $this->router->generate('sign_up'); - use App\Controller\ArticleController; + // generate a URL with route arguments + $userProfilePage = $this->router->generate('user_profile', [ + 'username' => $user->getUsername(), + ]); - return function (RoutingConfigurator $routes) { - $routes->add('article_show', '/articles/{_locale}/search.{_format}') - ->controller([ArticleController::class, 'search']) - ->locale('en') - ->format('html') - ->requirements([ - '_locale' => 'en|fr', - '_format' => 'html|rss', - ]) - ; - }; + // generated URLs are "absolute paths" by default. Pass a third optional + // argument to generate different URLs (e.g. an "absolute URL") + $signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL); -These attributes can also be used for route imports. + // when a route is localized, Symfony uses by default the current request locale + // pass a different '_locale' value if you want to set the locale explicitly + $signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']); + } + } -.. versionadded:: 4.3 +Generating URLs in Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The special attributes were introduced in Symfony 4.3. +The Twig template language used in :doc:`Symfony templates ` +provides some functions to generate both relative and absolute URLs: -.. _routing-trailing-slash-redirection: +.. code-block:: twig -Redirecting URLs with Trailing Slashes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + {# generates relative URLs #} + Sign up + + View your profile + + + Ver mi perfil + -Historically, URLs have followed the UNIX convention of adding trailing slashes -for directories (e.g. ``https://example.com/foo/``) and removing them to refer -to files (``https://example.com/foo``). Although serving different contents for -both URLs is OK, nowadays it's common to treat both URLs as the same URL and -redirect between them. + {# generates absolute URLs #} + Sign up + + View your profile + + + Ver mi perfil + -Symfony follows this logic to redirect between URLs with and without trailing -slashes (but only for ``GET`` and ``HEAD`` requests): +Generating URLs in JavaScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -========== ======================================== ========================================== -Route path If the requested URL is ``/foo`` If the requested URL is ``/foo/`` ----------- ---------------------------------------- ------------------------------------------ -``/foo`` It matches (``200`` status response) It makes a ``301`` redirect to ``/foo`` -``/foo/`` It makes a ``301`` redirect to ``/foo/`` It matches (``200`` status response) -========== ======================================== ========================================== +If your JavaScript code is included in a Twig template, you can use the same +``path()`` and ``url()`` functions to generate the URLs and store them in +JavaScript variables. The ``escape()`` function is needed to escape any +non-JavaScript-safe values: -.. note:: +.. code-block:: html+twig - If your application defines different routes for each path (``/foo`` and - ``/foo/``) this automatic redirection doesn't take place and the right - route is always matched. + -.. index:: - single: Routing; Controllers - single: Controller; String naming format +If you need to generate URLs dynamically or if you are using pure JavaScript +code, this solution doesn't work. In those cases, consider using the +`FOSJsRoutingBundle`_. -.. _controller-string-syntax: +Generating URLs in Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Controller Naming Pattern -------------------------- +Generating URLs in commands works the same as +:ref:`generating URLs in services `. The +only difference is that commands are not executed in the HTTP context, so they +don't have access to HTTP requests. In practice, this means that if you generate +absolute URLs, you'll get ``http://localhost/`` as the host name instead of your +real host name. -The ``controller`` value in your routes has the format ``CONTROLLER_CLASS::METHOD``. +The solution is to configure "request context" used by commands when they +generate URLs. This context can be configured globally for all commands: -.. tip:: +.. configuration-block:: - To refer to an action that is implemented as the ``__invoke()`` method of a controller class, - you do not have to pass the method name, you can also use the fully qualified class name (e.g. - ``App\Controller\BlogController``). + .. code-block:: yaml -.. index:: - single: Routing; Generating URLs + # config/services.yaml + parameters: + router.request_context.host: 'example.org' + router.request_context.base_url: 'my/path' + asset.request_context.base_path: '%router.request_context.base_url%' -.. _routing-generate: + .. code-block:: xml -Generating URLs ---------------- + + + -The routing system can also generate URLs. In reality, routing is a bidirectional -system: mapping the URL to a controller and also a route back to a URL. + + example.org + my/path + %router.request_context.base_url% + -To generate a URL, you need to specify the name of the route (e.g. ``blog_show``) -and any wildcards (e.g. ``slug = my-blog-post``) used in the path for that -route. With this information, an URL can be generated in a controller:: + - class BlogController extends AbstractController - { - public function show($slug) - { - // ... + .. code-block:: php - // /blog/my-blog-post - $url = $this->generateUrl( - 'blog_show', - ['slug' => 'my-blog-post'] - ); - } - } + // config/services.php + $container->setParameter('router.request_context.host', 'example.org'); + $container->setParameter('router.request_context.base_url', 'my/path'); + $container->setParameter('asset.request_context.base_path', $container->getParameter('router.request_context.base_url')); -If you need to generate a URL from a service, type-hint the :class:`Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface` -service:: +This information can be configured per command too:: - // src/Service/SomeService.php + // src/Command/SomeCommand.php use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + use Symfony\Component\Routing\RouterInterface; + // ... - class SomeService + class SomeCommand extends Command { private $router; - public function __construct(UrlGeneratorInterface $router) + public function __construct(RouterInterface $router) { + parent::__construct(); + $this->router = $router; } - public function someMethod() + protected function execute(InputInterface $input, OutputInterface $output) { - $url = $this->router->generate( - 'blog_show', - ['slug' => 'my-blog-post'] - ); + // these values override any global configuration + $context = $this->router->getContext(); + $context->setHost('example.com'); + $context->setBaseUrl('my/path'); + + // generate a URL with no route arguments + $signUpPage = $this->router->generate('sign_up'); + + // generate a URL with route arguments + $userProfilePage = $this->router->generate('user_profile', [ + 'username' => $user->getUsername(), + ]); + + // generated URLs are "absolute paths" by default. Pass a third optional + // argument to generate different URLs (e.g. an "absolute URL") + $signUpPage = $this->router->generate('sign_up', [], UrlGeneratorInterface::ABSOLUTE_URL); + + // when a route is localized, Symfony uses by default the current request locale + // pass a different '_locale' value if you want to set the locale explicitly + $signUpPageInDutch = $this->router->generate('sign_up', ['_locale' => 'nl']); + // ... } } -.. index:: - single: Routing; Generating URLs in a template +Checking if a Route Exists +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Generating URLs with Query Strings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In highly dynamic applications, it may be necessary to check whether a route +exists before using it to generate a URL. In those cases, don't use the +:method:`Symfony\\Component\\Routing\\Router::getRouteCollection` method because +that regenerates the routing cache and slows down the application. -The ``generate()`` method takes an array of wildcard values to generate the URI. -But if you pass extra ones, they will be added to the URI as a query string:: +Instead, try to generate the URL and catch the +:class:`Symfony\\Component\\Routing\\Exception\\RouteNotFoundException` thrown +when the route doesn't exist:: - $this->router->generate('blog', [ - 'page' => 2, - 'category' => 'Symfony', - ]); - // /blog/2?category=Symfony + use Symfony\Component\Routing\Exception\RouteNotFoundException; -Generating Localized URLs -~~~~~~~~~~~~~~~~~~~~~~~~~ + // ... -When a route is localized, Symfony uses by default the current request locale to -generate the URL. In order to generate the URL for a different locale you must -pass the ``_locale`` in the parameters array:: + try { + $url = $this->router->generate($routeName, $routeParameters); + } catch (RouteNotFoundException $e) { + // the route is not defined... + } - $this->router->generate('about_us', [ - '_locale' => 'nl', - ]); - // generates: /over-ons +.. _routing-force-https: -Generating URLs from a Template +Forcing HTTPS on Generated URLs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To generate URLs inside Twig, see the templating article: :ref:`templating-pages`. -If you also need to generate URLs in JavaScript, see :doc:`/routing/generate_url_javascript`. +By default, generated URLs use the same HTTP scheme as the current request. +In console commands, where there is no HTTP request, URLs use ``http`` by +default. You can change this per command (via the router's ``getContext()`` +method) or globally with these configuration parameters: -.. index:: - single: Routing; Absolute URLs +.. configuration-block:: -Generating Absolute URLs -~~~~~~~~~~~~~~~~~~~~~~~~ + .. code-block:: yaml -By default, the router will generate relative URLs (e.g. ``/blog``). From -a controller, pass ``UrlGeneratorInterface::ABSOLUTE_URL`` to the third argument of the ``generateUrl()`` -method:: + # config/services.yaml + parameters: + router.request_context.scheme: 'https' + asset.request_context.secure: true - use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + .. code-block:: xml + + + + + + + https + true + + + + + .. code-block:: php + + // config/services.php + $container->setParameter('router.request_context.scheme', 'https'); + $container->setParameter('asset.request_context.secure', true); + +Outside of console commands, use the ``schemes`` option to define the scheme of +each route explicitly:: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Controller/MainController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class SecurityController extends AbstractController + { + /** + * @Route("/login", name="login", schemes={"https"}) + */ + public function login() + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + login: + path: /login + controller: App\Controller\SeurityController::login + schemes: [https] + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // config/routes.php + use App\Controller\SecurityController; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('login', '/login') + ->controller([SecurityController::class, 'login']) + ->schemes(['https']) + ; + }; + +The URL generated for the ``login`` route will always use HTTPS. This means that +when using the ``path()`` Twig function to generate URLs, you may get an +absolute URL instead of a relative URL if the HTTP scheme of the original +request is different from the scheme used by the route: + +.. code-block:: twig + + {# if the current scheme is HTTPS, generates a relative URL: /login #} + {{ path('login') }} + + {# if the current scheme is HTTP, generates an absolute URL to change + the scheme: https://example.com/login #} + {{ path('login') }} + +The scheme requirement is also enforced for incoming requests. If you try to +access the ``/login`` URL with HTTP, you will automatically be redirected to the +same URL, but with the HTTPS scheme. + +If you want to force a group of routes to use HTTPS, you can define the default +scheme when importing them. The following example forces HTTPS on all routes +defined as annotations: + +.. configuration-block:: - $this->generateUrl('blog_show', ['slug' => 'my-blog-post'], UrlGeneratorInterface::ABSOLUTE_URL); - // http://www.example.com/blog/my-blog-post + .. code-block:: yaml + + # config/routes/annotations.yaml + controllers: + resource: '../src/Controller/' + type: annotation + defaults: + schemes: [https] + + .. code-block:: xml + + + + + + + HTTPS + + + + .. code-block:: php + + // config/routes/annotations.php + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->import('../src/Controller/', 'annotation') + ->schemes(['https']) + ; + }; .. note:: - 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:`/console/request_context` to learn how to - solve this problem. + The Security component provides + :doc:`another way to enforce HTTP or HTTPS ` + via the ``requires_channel`` setting. Troubleshooting --------------- @@ -1018,27 +2236,22 @@ This happens when your controller method has an argument (e.g. ``$slug``):: // ... } -But your route path does *not* have a ``{slug}`` wildcard (e.g. it is ``/blog/show``). -Add a ``{slug}`` to your route path: ``/blog/show/{slug}`` or give the argument -a default value (i.e. ``$slug = null``). +But your route path does *not* have a ``{slug}`` parameter (e.g. it is +``/blog/show``). Add a ``{slug}`` to your route path: ``/blog/show/{slug}`` or +give the argument a default value (i.e. ``$slug = null``). Some mandatory parameters are missing ("slug") to generate a URL for route "blog_show". -This means that you're trying to generate a URL to the ``blog_show`` route but you -are *not* passing a ``slug`` value (which is required, because it has a ``{slug}``) -wildcard in the route path. To fix this, pass a ``slug`` value when generating the -route:: +This means that you're trying to generate a URL to the ``blog_show`` route but +you are *not* passing a ``slug`` value (which is required, because it has a +``{slug}`` parameter in the route path). To fix this, pass a ``slug`` value when +generating the route:: $this->generateUrl('blog_show', ['slug' => 'slug-value']); // or, in Twig - // {{ path('blog_show', {'slug': 'slug-value'}) }} - -Keep Going! ------------ - -Routing, check! Now, uncover the power of :doc:`controllers `. + // {{ path('blog_show', {slug: 'slug-value'}) }} Learn more about Routing ------------------------ @@ -1053,3 +2266,8 @@ Learn more about Routing :glob: routing/* + +.. _`PHP regular expressions`: https://www.php.net/manual/en/book.pcre.php +.. _`PCRE Unicode properties`: http://php.net/manual/en/regexp.reference.unicode.php +.. _`full param converter documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`FOSJsRoutingBundle`: https://github.com/FriendsOfSymfony/FOSJsRoutingBundle diff --git a/routing/conditions.rst b/routing/conditions.rst deleted file mode 100644 index 48b00f8b55f..00000000000 --- a/routing/conditions.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. index:: - single: Routing; Conditions - -How to Restrict Route Matching through Conditions -================================================= - -A route can be made to match only certain routing placeholders (via regular -expressions), HTTP methods, or host names. If you need more flexibility to -define arbitrary matching logic, use the ``condition`` routing setting: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/DefaultController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class DefaultController extends AbstractController - { - /** - * @Route( - * "/contact", - * name="contact", - * condition="context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" - * ) - * - * expressions can also include config parameters - * condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'" - */ - public function contact() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - contact: - path: /contact - controller: 'App\Controller\DefaultController::contact' - condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" - # expressions can also include config parameters - # condition: "request.headers.get('User-Agent') matches '%app.allowed_browsers%'" - - .. code-block:: xml - - - - - - - context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i' - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\DefaultController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('contact', '') - ->controller([DefaultController::class, 'contact']) - ->condition('context.getMethod() in ["GET", "HEAD"] and request.headers.get("User-Agent") matches "/firefox/i"') - // expressions can also include config parameters - // 'request.headers.get("User-Agent") matches "%app.allowed_browsers%"' - ; - }; - -The ``condition`` is an expression, and you can learn more about its syntax -here: :doc:`/components/expression_language/syntax`. With this, the route -won't match unless the HTTP method is either GET or HEAD *and* if the ``User-Agent`` -header matches ``firefox``. - -You can do any complex logic you need in the expression by leveraging two -variables that are passed into the expression: - -``context`` - An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, - which holds the most fundamental information about the route being matched. -``request`` - The Symfony :class:`Symfony\\Component\\HttpFoundation\\Request` object - (see :ref:`component-http-foundation-request`). - -.. caution:: - - Conditions are *not* taken into account when generating a URL. - -.. sidebar:: Expressions are Compiled to PHP - - Behind the scenes, expressions are compiled down to raw PHP. Our example - would generate the following PHP in the cache directory:: - - if (rtrim($pathInfo, '/contact') === '' && ( - in_array($context->getMethod(), [0 => "GET", 1 => "HEAD"]) - && preg_match("/firefox/i", $request->headers->get("User-Agent")) - )) { - // ... - } - - Because of this, using the ``condition`` key causes no extra overhead - beyond the time it takes for the underlying PHP to execute. diff --git a/routing/custom_route_loader.rst b/routing/custom_route_loader.rst index ebd24d80b14..60202690f17 100644 --- a/routing/custom_route_loader.rst +++ b/routing/custom_route_loader.rst @@ -4,6 +4,90 @@ How to Create a custom Route Loader =================================== +Simple applications can define all their routes in a single configuration file - +usually ``config/routes.yaml`` (see :ref:`routing-creating-routes`). +However, in most applications it's common to import routes definitions from +different resources: PHP annotations in controller files, YAML, XML or PHP +files stored in some directory, etc. + +Built-in Route Loaders +---------------------- + +Symfony provides several route loaders for the most common needs: + +.. configuration-block:: + + .. code-block:: yaml + + # config/routes.yaml + app_file: + # loads routes from the given routing file stored in some bundle + resource: '@AcmeBundle/Resources/config/routing.yaml' + + app_annotations: + # loads routes from the PHP annotations of the controllers found in that directory + resource: '../src/Controller/' + type: annotation + + app_directory: + # loads routes from the YAML, XML or PHP files found in that directory + resource: '../legacy/routing/' + type: directory + + app_bundle: + # loads routes from the YAML, XML or PHP files found in some bundle directory + resource: '@AcmeOtherBundle/Resources/config/routing/' + type: directory + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/routes.php + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + // loads routes from the given routing file stored in some bundle + $routes->import('@AcmeBundle/Resources/config/routing.yaml'); + + // loads routes from the PHP annotations of the controllers found in that directory + $routes->import('../src/Controller/', 'annotation'); + + // loads routes from the YAML or XML files found in that directory + $routes->import('../legacy/routing/', 'directory'); + + // loads routes from the YAML or XML files found in some bundle directory + $routes->import('@AcmeOtherBundle/Resources/config/routing/', 'directory'); + }; + +.. note:: + + When importing resources, the key (e.g. ``app_file``) is the name of collection. + Just be sure that it's unique per file so no other lines override it. + +If your application needs are different, you can create your own custom route +loader as explained in the next section. + What is a Custom Route Loader ----------------------------- diff --git a/routing/debug.rst b/routing/debug.rst deleted file mode 100644 index f83331488cc..00000000000 --- a/routing/debug.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. index:: - single: Routing; Debugging - -How to Visualize And Debug Routes -================================= - -While adding and customizing routes, it's helpful to be able to visualize -and get detailed information about your routes. A great way to see every -route in your application is via the ``debug:router`` console command, which, -by default, lists *all* the configured routes in your application: - -.. code-block:: terminal - - $ php bin/console debug:router - - ------------------ -------- -------- ------ ---------------------------------------------- - Name Method Scheme Host Path - ------------------ -------- -------- ------ ---------------------------------------------- - homepage ANY ANY ANY / - contact GET ANY ANY /contact - contact_process POST ANY ANY /contact - article_show ANY ANY ANY /articles/{_locale}/{year}/{title}.{_format} - blog ANY ANY ANY /blog/{page} - blog_show ANY ANY ANY /blog/{slug} - ------------------ -------- -------- ------ ---------------------------------------------- - -You can also get very specific information on a single route by including -the route name as the command argument: - -.. code-block:: terminal - - $ php bin/console debug:router article_show - - # or use part of the name to search for routes - $ php bin/console debug:router blo - - Select one of the matching routes: - [0] blog - [1] blog_show - -Likewise, if you want to test whether a URL matches a given route, use the -``router:match`` command. This is useful to debug routing issues and find out -which route is associated with the given URL: - -.. code-block:: terminal - - $ php bin/console router:match /blog/my-latest-post - - Route "blog_show" matches - - +--------------+---------------------------------------------------------+ - | Property | Value | - +--------------+---------------------------------------------------------+ - | Route Name | blog_show | - | Path | /blog/{slug} | - | Path Regex | #^/blog/(?P[^/]++)$#sDu | - | Host | ANY | - | Host Regex | | - | Scheme | ANY | - | Method | ANY | - | Requirements | NO CUSTOM | - | Class | Symfony\Component\Routing\Route | - | Defaults | _controller: App\Controller\BlogController:show | - | Options | compiler_class: Symfony\Component\Routing\RouteCompiler | - | | utf8: true | - | Condition | context.getMethod() in ['GET', 'HEAD', 'POST'] | - +--------------+---------------------------------------------------------+ - -.. versionadded:: 4.3 - - The ``Condition`` was added to the router debug output in Symfony 4.3. diff --git a/routing/external_resources.rst b/routing/external_resources.rst deleted file mode 100644 index c3283e6561e..00000000000 --- a/routing/external_resources.rst +++ /dev/null @@ -1,260 +0,0 @@ -.. index:: - single: Routing; Importing routing resources - -How to Include External Routing Resources -========================================= - -Simple applications can define all their routes in a single configuration file - -usually ``config/routes.yaml`` (see :ref:`routing-creating-routes`). -However, in most applications it's common to import routes definitions from -different resources: PHP annotations in controller files, YAML, XML or PHP -files stored in some directory, etc. - -This can be done by importing routing resources from the main routing file: - -.. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - app_file: - # loads routes from the given routing file stored in some bundle - resource: '@AcmeBundle/Resources/config/routing.yaml' - - app_annotations: - # loads routes from the PHP annotations of the controllers found in that directory - resource: '../src/Controller/' - type: annotation - - app_directory: - # loads routes from the YAML, XML or PHP files found in that directory - resource: '../legacy/routing/' - type: directory - - app_bundle: - # loads routes from the YAML, XML or PHP files found in some bundle directory - resource: '@AcmeOtherBundle/Resources/config/routing/' - type: directory - - .. code-block:: xml - - - - - - - - - - - - - - - - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - // loads routes from the given routing file stored in some bundle - $routes->import('@AcmeBundle/Resources/config/routing.yaml'); - - // loads routes from the PHP annotations of the controllers found in that directory - $routes->import('../src/Controller/', 'annotation'); - - // loads routes from the YAML or XML files found in that directory - $routes->import('../legacy/routing/', 'directory'); - - // loads routes from the YAML or XML files found in some bundle directory - $routes->import('@AcmeOtherBundle/Resources/config/routing/', 'directory'); - }; - -.. note:: - - When importing resources, the key (e.g. ``app_file``) is the name of collection. - Just be sure that it's unique per file so no other lines override it. - -.. _prefixing-imported-routes: - -Prefixing the URLs of Imported Routes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can also choose to provide a "prefix" for the imported routes. For example, -to prefix all application routes with ``/site`` (e.g. ``/site/blog/{slug}`` -instead of ``/blog/{slug}``): - -.. configuration-block:: - - .. code-block:: php-annotations - - use Symfony\Component\Routing\Annotation\Route; - - /** - * @Route("/site") - */ - class DefaultController - { - // ... - } - - .. code-block:: yaml - - # config/routes.yaml - controllers: - resource: '../src/Controller/' - type: annotation - prefix: /site - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->import('../src/Controller/', 'annotation') - ->prefix('/site') - ; - }; - -The path of each route being loaded from the new routing resource will now -be prefixed with the string ``/site``. - -.. note:: - - If any of the prefixed routes defines an empty path, Symfony adds a trailing - slash to it. In the previous example, an empty path prefixed with ``/site`` - will result in the ``/site/`` URL. If you want to avoid this behavior, set - the ``trailing_slash_on_root`` option to ``false``: - - .. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - controllers: - resource: '../src/Controller/' - type: annotation - prefix: /site - trailing_slash_on_root: false - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\ArticleController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->import('../src/Controller/', 'annotation') - ->prefix('/site', false) - ; - }; - -Prefixing the Names of Imported Routes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You also have the possibility to prefix the names of all the routes defined in -a controller class or imported from a configuration file: - -.. configuration-block:: - - .. code-block:: php-annotations - - use Symfony\Component\Routing\Annotation\Route; - - /** - * @Route(name="blog_") - */ - class BlogController - { - /** - * @Route("/blog", name="index") - */ - public function index() - { - // ... - } - - /** - * @Route("/blog/posts/{slug}", name="post") - */ - public function show(Post $post) - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - controllers: - resource: '../src/Controller/' - type: annotation - name_prefix: 'blog_' - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->import('../src/Controller/', 'annotation') - ->namePrefix('blog_') - ; - }; - -In this example, the names of the routes will be ``blog_index`` and ``blog_post``. - -Adding a Host Requirement to Imported Routes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set the host regex on imported routes. For more information, see -:ref:`component-routing-host-imported`. diff --git a/routing/extra_information.rst b/routing/extra_information.rst deleted file mode 100644 index ce833426102..00000000000 --- a/routing/extra_information.rst +++ /dev/null @@ -1,102 +0,0 @@ -.. index:: - single: Routing; Extra Information - -How to Pass Extra Information from a Route to a Controller -========================================================== - -Parameters inside the ``defaults`` collection don't necessarily have to match -a placeholder in the route ``path``. In fact, you can use the ``defaults`` -array to specify extra parameters that will then be accessible as arguments -to your controller, and as attributes of the ``Request`` object: - -.. configuration-block:: - - .. code-block:: php-annotations - - use Symfony\Component\Routing\Annotation\Route; - - /** - * @Route(name="blog_") - */ - class BlogController - { - /** - * @Route("/blog/{page}", name="index", defaults={"page": 1, "title": "Hello world!"}) - */ - public function index($page) - { - // ... - } - } - - # config/routes.yaml - blog: - path: /blog/{page} - controller: App\Controller\BlogController::index - defaults: - page: 1 - title: "Hello world!" - - .. code-block:: yaml - - # config/routes.yaml - blog: - path: /blog/{page} - controller: App\Controller\BlogController::index - defaults: - page: 1 - title: "Hello world!" - - .. code-block:: xml - - - - - - - 1 - Hello world! - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('blog', '/blog/{page}') - ->controller([BlogController::class, 'index']) - ->defaults([ - 'page' => 1, - 'title' => 'Hello world!', - ]) - ; - }; - -Now, you can access this extra parameter in your controller, as an argument -to the controller method:: - - public function index($page, $title) - { - // ... - } - -Alternatively, the title could be accessed through the ``Request`` object:: - - use Symfony\Component\HttpFoundation\Request; - - public function index(Request $request, $page) - { - $title = $request->attributes->get('title'); - - // ... - } - -As you can see, the ``$title`` variable was never defined inside the route -path, but you can still access its value from inside your controller, through -the method's argument, or from the ``Request`` object's ``attributes`` bag. diff --git a/routing/generate_url_javascript.rst b/routing/generate_url_javascript.rst deleted file mode 100644 index 0893b53de8e..00000000000 --- a/routing/generate_url_javascript.rst +++ /dev/null @@ -1,25 +0,0 @@ -How to Generate Routing URLs in JavaScript -========================================== - -If you're in a Twig template, you can use the same ``path()`` function to set -JavaScript variables. The ``escape()`` function helps escape any -non-JavaScript-safe values: - -.. code-block:: html+twig - - - -But if you *actually* need to generate routes in pure JavaScript, consider using -the `FOSJsRoutingBundle`_. It makes the following possible: - -.. code-block:: html+twig - - - -.. _`FOSJsRoutingBundle`: https://github.com/FriendsOfSymfony/FOSJsRoutingBundle diff --git a/routing/hostname_pattern.rst b/routing/hostname_pattern.rst deleted file mode 100644 index f92e13f8285..00000000000 --- a/routing/hostname_pattern.rst +++ /dev/null @@ -1,439 +0,0 @@ -.. index:: - single: Routing; Matching on Hostname - -How to Match a Route Based on the Host -====================================== - -You can also match any route with the HTTP *host* of the incoming request. - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route("/", name="mobile_homepage", host="m.example.com") - */ - public function mobileHomepage() - { - // ... - } - - /** - * @Route("/", name="homepage") - */ - public function homepage() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - mobile_homepage: - path: / - host: m.example.com - controller: App\Controller\MainController::mobileHomepage - - homepage: - path: / - controller: App\Controller\MainController::homepage - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('mobile_homepage', '/') - ->controller([MainController::class, 'mobileHomepage']) - ->host('m.example.com') - ; - $routes->add('homepage', '/') - ->controller([MainController::class, 'homepage']) - ; - }; - - return $routes; - -Both routes match the same path, ``/``. However, the first one will only -match if the host is ``m.example.com``. - -Using Placeholders ------------------- - -The host option uses the same syntax as the path matching system. This means -you can use placeholders in your hostname: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route("/", name="projects_homepage", host="{project}.example.com") - */ - public function projectsHomepage(string $project) - { - // ... - } - - /** - * @Route("/", name="homepage") - */ - public function homepage() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - projects_homepage: - path: / - host: "{project}.example.com" - controller: App\Controller\MainController::projectsHomepage - - homepage: - path: / - controller: App\Controller\MainController::homepage - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('project_homepage', '/') - ->controller([MainController::class, 'projectHomepage']) - ->host('{project}.example.com') - ; - $routes->add('homepage', '/') - ->controller([MainController::class, 'homepage']) - ; - }; - -Also, any requirement or default can be set for these placeholders. For -instance, if you want to match both ``m.example.com`` and -``mobile.example.com``, you can use this: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route( - * "/", - * name="mobile_homepage", - * host="{subdomain}.example.com", - * defaults={"subdomain"="m"}, - * requirements={"subdomain"="m|mobile"} - * ) - */ - public function mobileHomepage() - { - // ... - } - - /** - * @Route("/", name="homepage") - */ - public function homepage() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - mobile_homepage: - path: / - host: "{subdomain}.example.com" - controller: App\Controller\MainController::mobileHomepage - defaults: - subdomain: m - requirements: - subdomain: m|mobile - - homepage: - path: / - controller: App\Controller\MainController::homepage - - .. code-block:: xml - - - - - - - m - m|mobile - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('mobile_homepage', '/') - ->controller([MainController::class, 'mobileHomepage']) - ->host('{subdomain}.example.com') - ->defaults([ - 'subdomain' => 'm', - ]) - ->requirements([ - 'subdomain' => 'm|mobile', - ]) - ; - $routes->add('homepage', '/') - ->controller([MainController::class, 'homepage']) - ; - }; - -.. tip:: - - You can also use service parameters if you do not want to hardcode the - hostname: - - .. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route( - * "/", - * name="mobile_homepage", - * host="m.{domain}", - * defaults={"domain"="%domain%"}, - * requirements={"domain"="%domain%"} - * ) - */ - public function mobileHomepage() - { - // ... - } - - /** - * @Route("/", name="homepage") - */ - public function homepage() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - mobile_homepage: - path: / - host: "m.{domain}" - controller: App\Controller\MainController::mobileHomepage - defaults: - domain: '%domain%' - requirements: - domain: '%domain%' - - homepage: - path: / - controller: App\Controller\MainController::homepage - - .. code-block:: xml - - - - - - - %domain% - %domain% - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('mobile_homepage', '/') - ->controller([MainController::class, 'mobileHomepage']) - ->host('m.{domain}') - ->defaults([ - 'domain' => '%domain%', - ]) - ->requirements([ - 'domain' => '%domain%', - ]) - ; - $routes->add('homepage', '/') - ->controller([MainController::class, 'homepage']) - ; - }; - -.. tip:: - - Make sure you also include a default option for the ``domain`` placeholder, - otherwise you need to include a domain value each time you generate - a URL using the route. - -.. _component-routing-host-imported: - -Using Host Matching of Imported Routes --------------------------------------- - -You can also set the host option on imported routes: - -.. configuration-block:: - - .. code-block:: php-annotations - - // vendor/acme/acme-hello-bundle/src/Controller/MainController.php - namespace Acme\AcmeHelloBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - /** - * @Route(host="hello.example.com") - */ - class MainController extends AbstractController - { - // ... - } - - .. code-block:: yaml - - # config/routes.yaml - app_hello: - resource: '@AcmeHelloBundle/Resources/config/routing.yaml' - host: "hello.example.com" - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->import("@AcmeHelloBundle/Resources/config/routing.php") - ->host('hello.example.com') - ; - }; - -The host ``hello.example.com`` will be set on each route loaded from the new -routing resource. - -Testing your Controllers ------------------------- - -You need to set the Host HTTP header on your request objects if you want to get -past url matching in your functional tests:: - - $crawler = $client->request( - 'GET', - '/', - [], - [], - ['HTTP_HOST' => 'm.' . $client->getContainer()->getParameter('domain')] - ); diff --git a/routing/optional_placeholders.rst b/routing/optional_placeholders.rst deleted file mode 100644 index a988e4f7840..00000000000 --- a/routing/optional_placeholders.rst +++ /dev/null @@ -1,198 +0,0 @@ -.. index:: - single: Routing; Optional Placeholders - -How to Define Optional Placeholders -=================================== - -To make things more exciting, add a new route that displays a list of all -the available blog posts for this imaginary blog application: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/BlogController.php - use Symfony\Component\Routing\Annotation\Route; - - class BlogController - { - /** - * @Route("/blog") - */ - public function index() - { - // ... - } - // ... - } - - .. code-block:: yaml - - # config/routes.yaml - blog: - path: /blog - controller: App\Controller\BlogController::index - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('blog', '/blog') - ->controller([BlogController::class, 'index']) - ; - }; - -So far, this route is as simple as possible - it contains no placeholders -and will only match the exact URL ``/blog``. But what if you need this route -to support pagination, where ``/blog/2`` displays the second page of blog -entries? Update the route to have a new ``{page}`` placeholder: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/BlogController.php - - // ... - - /** - * @Route("/blog/{page}") - */ - public function index($page) - { - // ... - } - - .. code-block:: yaml - - # config/routes.yaml - blog: - path: /blog/{page} - controller: App\Controller\BlogController::index - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('blog', '/blog/{page}') - ->controller([BlogController::class, 'index']) - ; - }; - -Like the ``{slug}`` placeholder before, the value matching ``{page}`` will -be available inside your controller. Its value can be used to determine which -set of blog posts to display for the given page. - -But hold on! Since placeholders are required by default, this route will -no longer match on ``/blog`` alone. Instead, to see page 1 of the blog, -you'd need to use the URL ``/blog/1``! Since that's no way for a rich web -app to behave, modify the route to make the ``{page}`` parameter optional. -This is done by including it in the ``defaults`` collection: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/BlogController.php - - // ... - - /** - * @Route("/blog/{page}", defaults={"page"=1}) - */ - public function index($page) - { - // ... - } - - .. code-block:: yaml - - # config/routes.yaml - blog: - path: /blog/{page} - controller: App\Controller\BlogController::index - defaults: { page: 1 } - - .. code-block:: xml - - - - - - - 1 - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('blog', '/blog/{page}') - ->controller([BlogController::class, 'index']) - ->defaults([ - 'page' => 1, - ]) - ; - }; - -By adding ``page`` to the ``defaults`` key, the ``{page}`` placeholder is -no longer required. The URL ``/blog`` will match this route and the value -of the ``page`` parameter will be set to ``1``. The URL ``/blog/2`` will -also match, giving the ``page`` parameter a value of ``2``. Perfect. - -=========== ======== ================== -URL Route Parameters -=========== ======== ================== -``/blog`` ``blog`` ``{page}`` = ``1`` -``/blog/1`` ``blog`` ``{page}`` = ``1`` -``/blog/2`` ``blog`` ``{page}`` = ``2`` -=========== ======== ================== - -.. caution:: - - You can have more than one optional placeholder (e.g. ``/blog/{slug}/{page}``), - but everything after an optional placeholder must be optional. For example, - ``/{page}/blog`` is a valid path, but ``page`` will always be required - (i.e. ``/blog`` will not match this route). - -.. tip:: - - Routes with optional parameters at the end will not match on requests - with a trailing slash (i.e. ``/blog/`` will not match, ``/blog`` will match). diff --git a/routing/redirect_in_config.rst b/routing/redirect_in_config.rst deleted file mode 100644 index 87abde17df3..00000000000 --- a/routing/redirect_in_config.rst +++ /dev/null @@ -1,260 +0,0 @@ -.. index:: - single: Routing; Redirect using Framework:RedirectController - -How to Configure a Redirect without a custom Controller -======================================================= - -Sometimes, a URL needs to redirect to another URL. You can do that by creating -a new controller action whose only task is to redirect, but using the -:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController` of -the FrameworkBundle is even easier. - -You can redirect to a specific path (e.g. ``/about``) or to a specific route -using its name (e.g. ``homepage``). - -Redirecting Using a Path ------------------------- - -Assume there is no default controller for the ``/`` path of your application -and you want to redirect these requests to ``/app``. You will need to use the -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction` -action to redirect to this new url: - -.. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - - # load some routes - one should ultimately have the path "/app" - controllers: - resource: '../src/Controller/' - type: annotation - prefix: /app - - # redirecting the homepage - homepage: - path: / - controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction - defaults: - path: /app - permanent: true - - .. code-block:: xml - - - - - - - - - - - /app - true - - - - .. code-block:: php - - // config/routes.php - use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - // load some routes - one should ultimately have the path "/app" - $routes->import('../src/Controller/', 'annotation') - ->prefix('/app') - ; - // redirecting the homepage - $routes->add('homepage', '/') - ->controller([RedirectController::class, 'urlRedirectAction']) - ->defaults([ - 'path' => '/app', - 'permanent' => true, - ]) - ; - }; - -In this example, you configured a route for the ``/`` path and let the -``RedirectController`` redirect it to ``/app``. The ``permanent`` switch -tells the action to issue a ``301`` HTTP status code instead of the default -``302`` HTTP status code. - -Redirecting Using a Route -------------------------- - -Assume you are migrating your website from WordPress to Symfony, you want to -redirect ``/wp-admin`` to the route ``sonata_admin_dashboard``. You don't know -the path, only the route name. This can be achieved using the -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::redirectAction` -action: - -.. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - - # ... - - admin: - path: /wp-admin - controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction - defaults: - route: sonata_admin_dashboard - # make a permanent redirection... - permanent: true - # ...and keep the original query string parameters - keepQueryParams: true - - .. code-block:: xml - - - - - - - - - sonata_admin_dashboard - - true - - true - - - - .. code-block:: php - - // config/routes.php - use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - // redirecting the homepage - $routes->add('admin', '/wp-admin') - ->controller([RedirectController::class, 'redirectAction']) - ->defaults([ - 'route' => 'sonata_admin_dashboard', - // make a permanent redirection... - 'permanent' => true, - // ...and keep the original query string parameters - 'keepQueryParams' => true, - ]) - ; - }; - -.. caution:: - - Because you are redirecting to a route instead of a path, the required - option is called ``route`` in the ``redirect()`` action, instead of ``path`` - in the ``urlRedirect()`` action. - -Keeping the Request Method when Redirecting -------------------------------------------- - -The redirections performed in the previous examples use the ``301`` and ``302`` -HTTP status codes. For legacy reasons, these HTTP redirections change the method -of ``POST`` requests to ``GET`` (because redirecting a ``POST`` request didn't -work well in old browsers). - -However, in some scenarios it's either expected or required that the redirection -request uses the same HTTP method. That's why the HTTP standard defines two -additional status codes (``307`` and ``308``) to perform temporary/permanent -redirects that maintain the original request method. - -The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction` -and :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::redirectAction` -methods accept an additional argument called ``keepRequestMethod``. When it's -set to ``true``, temporary redirects use ``307`` code instead of ``302`` and -permanent redirects use ``308`` code instead of ``301``: - -.. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - - # redirects with the 308 status code - route_foo: - # ... - controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction - defaults: - # ... - permanent: true - keepRequestMethod: true - - # redirects with the 307 status code - route_bar: - # ... - controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction - defaults: - # ... - permanent: false - keepRequestMethod: true - - .. code-block:: xml - - - - - - - - - true - true - - - - - - false - true - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\Route; - use Symfony\Component\Routing\RouteCollection; - - $collection = new RouteCollection(); - - // redirects with the 308 status code - $collection->add('route_foo', new Route('...', [ - // ... - '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', - 'permanent' => true, - 'keepRequestMethod' => true, - ])); - - // redirects with the 307 status code - $collection->add('route_bar', new Route('...', [ - // ... - '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', - 'permanent' => false, - 'keepRequestMethod' => true, - ])); - - return $collection; diff --git a/routing/redirect_trailing_slash.rst b/routing/redirect_trailing_slash.rst deleted file mode 100644 index 9f17a8c0b07..00000000000 --- a/routing/redirect_trailing_slash.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. index:: - single: Routing; Redirect URLs with a trailing slash - -Redirect URLs with a Trailing Slash -=================================== - -.. caution:: - - In Symfony 4.1 the automatic URL redirection was improved as explained in - :ref:`routing-trailing-slash-redirection`. That's why you no longer need to - do that redirection yourself and this article has been removed because it's - no longer needed. diff --git a/routing/requirements.rst b/routing/requirements.rst deleted file mode 100644 index e01655fb7ff..00000000000 --- a/routing/requirements.rst +++ /dev/null @@ -1,310 +0,0 @@ -.. index:: - single: Routing; Requirements - -How to Define Route Requirements -================================ - -:ref:`Route requirements ` can be used to make a specific route -*only* match under specific conditions. The simplest example involves restricting -a routing ``{wildcard}`` to only match some regular expression: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/BlogController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class BlogController extends AbstractController - { - /** - * @Route("/blog/{page}", name="blog_list", requirements={"page"="\d+"}) - */ - public function list($page) - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - blog_list: - path: /blog/{page} - controller: App\Controller\BlogController::list - requirements: - page: '\d+' - - .. code-block:: xml - - - - - - - \d+ - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('blog_list', '/blog/{page}') - ->controller([BlogController::class, 'list']) - ->requirements([ - 'page' => '\d+', - ]) - ; - // ... - }; - -Thanks to the ``\d+`` requirement (i.e. a "digit" of any length), ``/blog/2`` will -match this route but ``/blog/some-string`` will *not* match. - -.. sidebar:: Earlier Routes Always Win - - Why would you ever care about requirements? If a request matches *two* routes, - then the first route always wins. By adding requirements to the first route, - you can make each route match in just the right situations. See :ref:`routing-requirements` - for an example. - -Since the parameter requirements are regular expressions, the complexity -and flexibility of each requirement is entirely up to you. Suppose the homepage -of your application is available in two different languages, based on the -URL: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - - // ... - class MainController extends AbstractController - { - /** - * @Route("/{_locale}", defaults={"_locale"="en"}, requirements={ - * "_locale"="en|fr" - * }) - */ - public function homepage($_locale) - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - homepage: - path: /{_locale} - controller: App\Controller\MainController::homepage - defaults: { _locale: en } - requirements: - _locale: en|fr - - .. code-block:: xml - - - - - - - en - en|fr - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('homepage', '/{_locale}') - ->controller([MainController::class, 'homepage']) - ->defaults([ - '_locale' => 'en', - ]) - ->requirements([ - '_locale' => 'en|fr', - ]) - ; - }; - -For incoming requests, the ``{_locale}`` portion of the URL is matched against -the regular expression ``(en|fr)``. - -======= ======================== -Path Parameters -======= ======================== -``/`` ``{_locale}`` = ``"en"`` -``/en`` ``{_locale}`` = ``"en"`` -``/fr`` ``{_locale}`` = ``"fr"`` -``/es`` *won't match this route* -======= ======================== - -.. note:: - - You can enable UTF-8 route matching by setting the ``utf8`` option when - declaring or importing routes. This will make e.g. a ``.`` in requirements - match any UTF-8 characters instead of just a single byte. - -.. tip:: - - The route requirements can also include container parameters, as explained - in :doc:`this article `. - This comes in handy when the regular expression is very complex and used - repeatedly in your application. - -.. index:: - single: Routing; Method requirement - -.. _routing-method-requirement: - -Adding HTTP Method Requirements -------------------------------- - -In addition to the URL, you can also match on the *method* of the incoming -request (i.e. GET, HEAD, POST, PUT, DELETE). Suppose you create an API for -your blog and you have 2 routes: One for displaying a post (on a GET or HEAD -request) and one for updating a post (on a PUT request). This can be -accomplished with the following route configuration: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/BlogApiController.php - namespace App\Controller; - - // ... - - class BlogApiController extends AbstractController - { - /** - * @Route("/api/posts/{id}", methods={"GET","HEAD"}) - */ - public function show($id) - { - // ... return a JSON response with the post - } - - /** - * @Route("/api/posts/{id}", methods={"PUT"}) - */ - public function edit($id) - { - // ... edit a post - } - } - - .. code-block:: yaml - - # config/routes.yaml - api_post_show: - path: /api/posts/{id} - controller: App\Controller\BlogApiController::show - methods: GET|HEAD - - api_post_edit: - path: /api/posts/{id} - controller: App\Controller\BlogApiController::edit - methods: PUT - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\BlogApiController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('api_post_show', '/api/posts/{id}') - ->controller([BlogApiController::class, 'show']) - ->methods(['GET', 'HEAD']) - ; - $routes->add('api_post_edit', '/api/posts/{id}') - ->controller([BlogApiController::class, 'edit']) - ->methods(['PUT']) - ; - - // or use collection - $api = $routes->collection('api_post_') - ->prefix('/api/posts/{id}') - ; - $api->add('show') - ->controller([BlogApiController::class, 'show']) - ->methods(['GET', 'HEAD']) - ; - $api->add('edit') - ->controller([BlogApiController::class, 'edit']) - ->methods(['PUT']) - ; - }; - -Despite the fact that these two routes have identical paths -(``/api/posts/{id}``), the first route will match only GET or HEAD requests and -the second route will match only PUT requests. This means that you can display -and edit the post with the same URL, while using distinct controllers for the -two actions. - -.. note:: - - If no ``methods`` are specified, the route will match on *all* methods. - -.. tip:: - - If you're using HTML forms and HTTP methods *other* than ``GET`` and ``POST``, - you'll need to include a ``_method`` parameter to *fake* the HTTP method. See - :doc:`/form/action_method` for more information. - -Adding a Host Requirement -------------------------- - -You can also match on the HTTP *host* of the incoming request. For more -information, see :doc:`/routing/hostname_pattern` in the Routing -component documentation. - -Adding Dynamic Requirements with Expressions --------------------------------------------- - -For really complex requirements, you can use dynamic expressions to match *any* -information on the request. See :doc:`/routing/conditions`. - -.. _`PCRE Unicode property`: http://php.net/manual/en/regexp.reference.unicode.php diff --git a/routing/scheme.rst b/routing/scheme.rst deleted file mode 100644 index 0ca8b8587fb..00000000000 --- a/routing/scheme.rst +++ /dev/null @@ -1,94 +0,0 @@ -.. index:: - single: Routing; Scheme requirement - -How to Force Routes to Always Use HTTPS or HTTP -=============================================== - -Sometimes, you want to secure some routes and be sure that they are always -accessed via the HTTPS protocol. The Routing component allows you to enforce -the URI scheme with the ``schemes`` setting: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route("/secure", name="secure", schemes={"https"}) - */ - public function secure() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - secure: - path: /secure - controller: App\Controller\MainController::secure - schemes: [https] - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('secure', '/secure') - ->controller([MainController::class, 'secure']) - ->schemes(['https']) - ; - }; - -The above configuration forces the ``secure`` route to always use HTTPS. - -When generating the ``secure`` URL, and if the current scheme is HTTP, Symfony -will automatically generate an absolute URL with HTTPS as the scheme, even when -using the ``path()`` function: - -.. code-block:: twig - - {# If the current scheme is HTTPS #} - {{ path('secure') }} - {# generates a relative URL: /secure #} - - {# If the current scheme is HTTP #} - {{ path('secure') }} - {# generates an absolute URL: https://example.com/secure #} - -The requirement is also enforced for incoming requests. If you try to access -the ``/secure`` path with HTTP, you will automatically be redirected to the -same URL, but with the HTTPS scheme. - -The above example uses ``https`` for the scheme, but you can also force a URL -to always use ``http``. - -.. note:: - - The Security component provides another way to enforce HTTP or HTTPS via - the ``requires_channel`` setting. This alternative method is better suited - to secure an "area" of your website (all URLs under ``/admin``) or when - you want to secure URLs defined in a third party bundle (see - :doc:`/security/force_https` for more details). diff --git a/routing/service_container_parameters.rst b/routing/service_container_parameters.rst deleted file mode 100644 index 58c26ad7da2..00000000000 --- a/routing/service_container_parameters.rst +++ /dev/null @@ -1,212 +0,0 @@ -.. index:: - single: Routing; Service Container Parameters - -How to Use Service Container Parameters in your Routes -====================================================== - -Sometimes you may find it useful to make some parts of your routes -globally configurable. For instance, if you build an internationalized -site, you'll probably start with one or two locales. Surely you'll -add a requirement to your routes to prevent a user from matching a locale -other than the locales you support. - -You *could* hardcode your ``_locale`` requirement in all your routes, but -a better solution is to use a configurable service container parameter right -inside your routing configuration: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route("/{_locale}/contact", name="contact", requirements={ - * "_locale"="%app.locales%" - * }) - */ - public function contact() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - contact: - path: /{_locale}/contact - controller: App\Controller\MainController::contact - requirements: - _locale: '%app.locales%' - - .. code-block:: xml - - - - - - - %app.locales% - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('contact', '/{_locale}/contact') - ->controller([MainController::class, 'contact']) - ->requirements([ - '_locale' => '%app.locales%', - ]) - ; - }; - -.. versionadded:: 4.3 - - Support for boolean container parameters in routes was introduced in Symfony 4.3. - -You can now control and set the ``app.locales`` parameter somewhere -in your container: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - parameters: - app.locales: en|es - - .. code-block:: xml - - - - - - - en|es - - - - .. code-block:: php - - // config/services.php - $container->setParameter('app.locales', 'en|es'); - -You can also use a parameter to define your route path (or part of your -path): - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Controller/MainController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Routing\Annotation\Route; - - class MainController extends AbstractController - { - /** - * @Route("/%app.route_prefix%/contact", name="contact") - */ - public function contact() - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - some_route: - path: /%app.route_prefix%/contact - controller: App\Controller\MainController::contact - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // config/routes.php - use App\Controller\MainController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('contact', '/%app.route_prefix%/contact') - ->controller([MainController::class, 'contact']) - ; - }; - -Now make sure that the ``app.route_prefix`` parameter is set somewhere in your -container: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - parameters: - app.route_prefix: 'foo' - - .. code-block:: xml - - - - - - - foo - - - - .. code-block:: php - - // config/services.php - $container->setParameter('app.route_prefix', 'foo'); - -.. note:: - - Just like in normal service container configuration files, if you actually - need a ``%`` in your route, you can escape the percent sign by doubling - it, e.g. ``/score-50%%``, which would resolve to ``/score-50%``. - - However, as the ``%`` characters included in any URL are automatically encoded, - the resulting URL of this example would be ``/score-50%25`` (``%25`` is the - result of encoding the ``%`` character). - -.. seealso:: - - For parameter handling within a Dependency Injection Class see - :doc:`/configuration/using_parameters_in_dic`. diff --git a/routing/slash_in_parameter.rst b/routing/slash_in_parameter.rst deleted file mode 100644 index 9b56def082b..00000000000 --- a/routing/slash_in_parameter.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. index:: - single: Routing; Allow / in route parameter - -.. _routing/slash_in_parameter: - -How to Allow a "/" Character in a Route Parameter -================================================= - -Sometimes, you need to compose URLs with parameters that can contain a slash -``/``. For example, consider the ``/share/{token}`` route. If the ``token`` -value contains a ``/`` character this route won't match. This is because Symfony -uses this character as separator between route parts. - -This article explains how you can modify a route definition so that placeholders -can contain the ``/`` character too. - -Configure the Route -------------------- - -By default, the Symfony Routing component requires that the parameters match -the following regular expression: ``[^/]+``. This means that all characters are -allowed except ``/``. - -You must explicitly allow ``/`` to be part of your placeholder by specifying -a more permissive regular expression for it: - -.. configuration-block:: - - .. code-block:: php-annotations - - use Symfony\Component\Routing\Annotation\Route; - - class DefaultController - { - /** - * @Route("/share/{token}", name="share", requirements={"token"=".+"}) - */ - public function share($token) - { - // ... - } - } - - .. code-block:: yaml - - # config/routes.yaml - share: - path: /share/{token} - controller: App\Controller\DefaultController::share - requirements: - token: .+ - - .. code-block:: xml - - - - - - - .+ - - - - .. code-block:: php - - // config/routes.php - use App\Controller\DefaultController; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - - return function (RoutingConfigurator $routes) { - $routes->add('share', '/share/{token}') - ->controller([DefaultController::class, 'share']) - ->requirements([ - 'token' => '.+', - ]) - ; - }; - -That's it! Now, the ``{token}`` parameter can contain the ``/`` character. - -.. note:: - - If the route includes the special ``{_format}`` placeholder, you shouldn't - use the ``.+`` requirement for the parameters that allow slashes. For example, - if the pattern is ``/share/{token}.{_format}`` and ``{token}`` allows any - character, the ``/share/foo/bar.json`` URL will consider ``foo/bar.json`` - as the token and the format will be empty. This can be solved by replacing the - ``.+`` requirement by ``[^.]+`` to allow any character except dots. - -.. note:: - - If the route defines several placeholders and you apply this permissive - regular expression to all of them, the results won't be the expected. For - example, if the route definition is ``/share/{path}/{token}`` and both - ``path`` and ``token`` accept ``/``, then ``path`` will contain its contents - and the token, and ``token`` will be empty. diff --git a/security/force_https.rst b/security/force_https.rst index bf69d7ad995..6358f663942 100644 --- a/security/force_https.rst +++ b/security/force_https.rst @@ -85,8 +85,10 @@ like ``requires_channel: '%env(SECURE_SCHEME)%'``. In your ``.env`` file, set See :doc:`/security/access_control` for more details about ``access_control`` in general. -It is also possible to specify using HTTPS in the routing configuration, -see :doc:`/routing/scheme` for more details. +.. note:: + + An alternative way to enforce HTTP or HTTPS is to use + :ref:`the scheme option ` of a route or group of routes. .. note:: diff --git a/templating/formats.rst b/templating/formats.rst index 17fe4c4e0d9..b12f471a6ff 100644 --- a/templating/formats.rst +++ b/templating/formats.rst @@ -48,7 +48,7 @@ but can return any other format based on the format requested by the user. The request format is most often managed by the routing, where a route can be configured so that ``/about-us`` sets the request format to ``html`` while ``/about-us.xml`` sets the format to ``xml``. This can be achieved by using the -special ``_format`` placeholder in your route definition:: +:ref:`special _format parameter ` in your route definition:: /** * @Route("/{slug}.{_format}", defaults={"_format"="html"}, requirements={"_format"="html|xml"})) @@ -67,10 +67,6 @@ format: View as XML -.. seealso:: - - For more information, see this :ref:`Advanced Routing Example `. - .. tip:: When building APIs, using file name extensions often isn't the best diff --git a/translation/locale.rst b/translation/locale.rst index da2fc84cfd9..2dfd7c80b7c 100644 --- a/translation/locale.rst +++ b/translation/locale.rst @@ -59,8 +59,8 @@ this violates a fundamental rule of the Web: that a particular URL returns the same resource regardless of the user. To further muddy the problem, which version of the content would be indexed by search engines? -A better policy is to include the locale in the URL. This is fully-supported -by the routing system using the special ``_locale`` parameter: +A better policy is to include the locale in the URL using the +:ref:`special _locale parameter `: .. configuration-block:: @@ -114,8 +114,8 @@ application. .. tip:: - Read :doc:`/routing/service_container_parameters` to learn how to avoid - hardcoding the ``_locale`` requirement in all your routes. + Define the locale requirement as a :ref:`container parameter ` + to avoid hardcoding its value in all your routes. .. index:: single: Translations; Fallback and default locale From 214b181bbe3561cc3f772fea9d6402508cd11858 Mon Sep 17 00:00:00 2001 From: fancyweb Date: Sun, 7 Apr 2019 11:08:30 +0200 Subject: [PATCH 0301/8077] [Routing] Route loader service tag --- routing/custom_route_loader.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing/custom_route_loader.rst b/routing/custom_route_loader.rst index 60202690f17..28d50119d9c 100644 --- a/routing/custom_route_loader.rst +++ b/routing/custom_route_loader.rst @@ -211,6 +211,10 @@ of the service whose ID is ``admin_route_loader``. Your service doesn't have to extend or implement any special class, but the called method must return a :class:`Symfony\\Component\\Routing\\RouteCollection` object. +If you're using :ref:`autoconfigure `, your service should +implement the ``RouteLoaderInterface`` interface to be tagged automatically. +If you're **not using autoconfigure**, tag it manually with ``routing.route_loader``. + .. note:: The routes defined using service route loaders will be automatically From 6d4b9f20ce435c86cc59611e7cdb662db0b065bb Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 12 Aug 2019 13:03:53 +0200 Subject: [PATCH 0302/8077] [#11337] Added a deprecation directive --- routing/custom_route_loader.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/routing/custom_route_loader.rst b/routing/custom_route_loader.rst index 28d50119d9c..8f9be5dee5c 100644 --- a/routing/custom_route_loader.rst +++ b/routing/custom_route_loader.rst @@ -211,9 +211,14 @@ of the service whose ID is ``admin_route_loader``. Your service doesn't have to extend or implement any special class, but the called method must return a :class:`Symfony\\Component\\Routing\\RouteCollection` object. -If you're using :ref:`autoconfigure `, your service should -implement the ``RouteLoaderInterface`` interface to be tagged automatically. -If you're **not using autoconfigure**, tag it manually with ``routing.route_loader``. +If you're using :ref:`autoconfigure `, your class should +implement the :class:`Symfony\\Bundle\\FrameworkBundle\\Routing\\RouteLoaderInterface` +interface to be tagged automatically. If you're **not using autoconfigure**, +tag it manually with ``routing.route_loader``. + +.. deprecated:: 4.4 + + Not tagging or implementing your route loader was deprecated in Symfony 4.4. .. note:: From 0a1badfb592177c2d0530aff89c5d3a4225078cf Mon Sep 17 00:00:00 2001 From: Andrey Bolonin Date: Sun, 19 Aug 2018 10:58:47 +0300 Subject: [PATCH 0303/8077] Update routing.rst --- components/routing.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/components/routing.rst b/components/routing.rst index 7bccb0e0a47..cdd7dc6ae10 100644 --- a/components/routing.rst +++ b/components/routing.rst @@ -255,6 +255,28 @@ out here. .. include:: /_includes/_annotation_loader_tip.rst.inc +Some final example to use routes as annotations:: + + \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']); + + use Symfony\Component\Routing\Matcher\UrlMatcher; + use Symfony\Component\Routing\RequestContext; + + $loader = new \Symfony\Component\Routing\Loader\AnnotationDirectoryLoader( + new \Symfony\Component\Config\FileLocator(__DIR__.'/app/controllers/'), + new \Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader( + new \Doctrine\Common\Annotations\AnnotationReader() + ) + ); + + $routes = $loader->load(__DIR__.'/app/controllers/'); + + $context = new RequestContext('/'); + + $matcher = new UrlMatcher($routes, $context); + + $parameters = $matcher->match($_SERVER['REQUEST_URI']); + The all-in-one Router ~~~~~~~~~~~~~~~~~~~~~ From 54743d63d7e18cb1906f0e575294fffc1589b55d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Aug 2018 14:58:06 -0400 Subject: [PATCH 0304/8077] cleaning up some use statements, etc --- components/routing.rst | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/components/routing.rst b/components/routing.rst index cdd7dc6ae10..5977e1c498a 100644 --- a/components/routing.rst +++ b/components/routing.rst @@ -250,32 +250,24 @@ Annotation Routing Loaders Last but not least there are :class:`Symfony\\Component\\Routing\\Loader\\AnnotationDirectoryLoader` and :class:`Symfony\\Component\\Routing\\Loader\\AnnotationFileLoader` to load -route definitions from class annotations. The specific details are left -out here. +route definitions from class annotations:: -.. include:: /_includes/_annotation_loader_tip.rst.inc - -Some final example to use routes as annotations:: - - \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']); - - use Symfony\Component\Routing\Matcher\UrlMatcher; - use Symfony\Component\Routing\RequestContext; + use Doctrine\Common\Annotations\AnnotationReader; + use Symfony\Component\Config\FileLocator; + use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader; + use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; - $loader = new \Symfony\Component\Routing\Loader\AnnotationDirectoryLoader( - new \Symfony\Component\Config\FileLocator(__DIR__.'/app/controllers/'), - new \Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader( - new \Doctrine\Common\Annotations\AnnotationReader() + $loader = new AnnotationDirectoryLoader( + new FileLocator(__DIR__.'/app/controllers/'), + new AnnotatedRouteControllerLoader( + new AnnotationReader() ) ); $routes = $loader->load(__DIR__.'/app/controllers/'); + // ... - $context = new RequestContext('/'); - - $matcher = new UrlMatcher($routes, $context); - - $parameters = $matcher->match($_SERVER['REQUEST_URI']); +.. include:: /_includes/_annotation_loader_tip.rst.inc The all-in-one Router ~~~~~~~~~~~~~~~~~~~~~ From e8d6eb4116eeee3adb6250c60317b78e53c3d6c3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 12 Aug 2019 13:31:09 +0200 Subject: [PATCH 0305/8077] Sort imports alphabetically --- components/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/routing.rst b/components/routing.rst index 5977e1c498a..b49230286eb 100644 --- a/components/routing.rst +++ b/components/routing.rst @@ -253,8 +253,8 @@ Last but not least there are route definitions from class annotations:: use Doctrine\Common\Annotations\AnnotationReader; - use Symfony\Component\Config\FileLocator; use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader; + use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; $loader = new AnnotationDirectoryLoader( From 48f84370947ca73c34c44a3298ac4ed16e9a7b22 Mon Sep 17 00:00:00 2001 From: LucileDT Date: Mon, 12 Aug 2019 14:15:22 +0200 Subject: [PATCH 0306/8077] Add doc about how to get a link by its id or class attribute in DOM crawler page --- components/dom_crawler.rst | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 81ba58d38d8..c9905a63762 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -370,16 +370,28 @@ This behavior is best illustrated with examples:: Links ~~~~~ -To find a link by name (or a clickable image by its ``alt`` attribute), use -the ``selectLink()`` method on an existing crawler. This returns a ``Crawler`` -instance with just the selected link(s). Calling ``link()`` gives you a special -:class:`Symfony\\Component\\DomCrawler\\Link` object:: +To find a link by its ``id`` or its ``class`` attribute(s), use the ``filter()`` +method on an existing crawler. To find a link by name (or a clickable image by +its ``alt`` attribute), use the ``selectLink()`` method on an existing crawler. +This returns a ``Crawler`` instance with just the selected link(s). Calling +``link()`` gives you a special :class:`Symfony\\Component\\DomCrawler\\Link` object:: - $linksCrawler = $crawler->selectLink('Go elsewhere...'); - $link = $linksCrawler->link(); + // select the link by its id + $linksByIdCrawler = $crawler->filter('#your-link'); + $link = $linksByIdCrawler->link(); + + // select the link by its class + $linksByClassCrawler = $crawler->filter('.link'); + $link = $linksByClassCrawler->link(); + + // select the link by its name + $linksByNameCrawler = $crawler->selectLink('Go elsewhere...'); + $linkByName = $linksCrawler->link(); // or do this all at once - $link = $crawler->selectLink('Go elsewhere...')->link(); + $linkById = $crawler->filter('#your-link')->link(); + $linkByClass = $crawler->filter('.link')->link(); + $linkByName = $crawler->selectLink('Go elsewhere...')->link(); The :class:`Symfony\\Component\\DomCrawler\\Link` object has several useful methods to get more information about the selected link itself:: From e95edabb2d317d4aa6763b81a9675602047e0542 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 12 Aug 2019 14:55:19 +0200 Subject: [PATCH 0307/8077] Minor fixes for the new Routing article --- routing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routing.rst b/routing.rst index b5180bb4598..8acc0f38cc3 100644 --- a/routing.rst +++ b/routing.rst @@ -1933,8 +1933,8 @@ provides some functions to generate both relative and absolute URLs: Generating URLs in JavaScript ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If your JavaScript code is included in a Twig template, you can use the same -``path()`` and ``url()`` functions to generate the URLs and store them in +If your JavaScript code is included in a Twig template, you can use the +``path()`` and ``url()`` Twig functions to generate the URLs and store them in JavaScript variables. The ``escape()`` function is needed to escape any non-JavaScript-safe values: @@ -2100,7 +2100,7 @@ method) or globally with these configuration parameters: $container->setParameter('asset.request_context.secure', true); Outside of console commands, use the ``schemes`` option to define the scheme of -each route explicitly:: +each route explicitly: .. configuration-block:: From cbf6beea4c484af3514ce502738df7c52f53c8be Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 12 Aug 2019 15:00:08 +0200 Subject: [PATCH 0308/8077] Renamed the security:check command as check:security --- contributing/code/security.rst | 5 +++-- security/security_checker.rst | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index f263d3c4fc2..ca6724c6f0a 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -38,7 +38,8 @@ confirmed, the core team works on a solution following these steps: #. Publish the post on the official Symfony `blog`_ (it must also be added to the "`Security Advisories`_" category); #. Update the public `security advisories database`_ maintained by the - FriendsOfPHP organization and which is used by the ``security:check`` command. + FriendsOfPHP organization and which is used by + :doc:`the check:security command `. .. note:: @@ -169,7 +170,7 @@ Security Advisories .. tip:: You can check your Symfony application for known security vulnerabilities - using the ``security:check`` command (see :doc:`/security/security_checker`). + using the ``check:security`` command (see :doc:`/security/security_checker`). Check the `Security Advisories`_ blog category for a list of all security vulnerabilities that were fixed in Symfony releases, starting from Symfony diff --git a/security/security_checker.rst b/security/security_checker.rst index 331270f86ec..dbf4b0d2ee7 100644 --- a/security/security_checker.rst +++ b/security/security_checker.rst @@ -6,12 +6,12 @@ How to Check for Known Security Vulnerabilities in Your Dependencies When using lots of dependencies in your Symfony projects, some of them may contain security vulnerabilities. That's why the :doc:`Symfony local server ` -includes a command called ``security:check`` that checks your ``composer.lock`` +includes a command called ``check:security`` that checks your ``composer.lock`` file to find known security vulnerabilities in your installed dependencies: .. code-block:: terminal - $ symfony security:check + $ symfony check:security A good security practice is to execute this command regularly to be able to update or replace compromised dependencies as soon as possible. The security @@ -21,7 +21,7 @@ the network. .. tip:: - The ``security:check`` command terminates with a non-zero exit code if + The ``check:security`` command terminates with a non-zero exit code if any of your dependencies is affected by a known security vulnerability. This way you can add it to your project build process and your continuous integration workflows to make them fail when there are vulnerabilities. From 067441456e2f7f408db563dc459078d235485b51 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 Aug 2019 17:36:54 +0200 Subject: [PATCH 0309/8077] Use PSR-13 implementation from WebLink component --- components/web_link.rst | 4 ++-- mercure.rst | 4 ++-- web_link.rst | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/web_link.rst b/components/web_link.rst index 634da8b81b7..7d30b4775e7 100644 --- a/components/web_link.rst +++ b/components/web_link.rst @@ -22,9 +22,9 @@ Usage The following example shows the component in action:: - use Fig\Link\GenericLinkProvider; - use Fig\Link\Link; use Symfony\Component\WebLink\HttpHeaderSerializer; + use Symfony\Component\WebLink\GenericLinkProvider; + use Symfony\Component\WebLink\Link; $linkProvider = (new GenericLinkProvider()) ->withLink(new Link('preload', '/bootstrap.min.css')); diff --git a/mercure.rst b/mercure.rst index 6c8ba2f8fed..3768ecd09f5 100644 --- a/mercure.rst +++ b/mercure.rst @@ -283,10 +283,10 @@ by using the ``AbstractController::addLink`` helper method:: // src/Controller/DiscoverController.php namespace App\Controller; - use Fig\Link\Link; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\WebLink\LInk; class DiscoverController extends AbstractController { @@ -383,12 +383,12 @@ And here is the controller:: // src/Controller/DiscoverController.php namespace App\Controller; - use Fig\Link\Link; use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer\Hmac\Sha256; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\WebLink\Link; class DiscoverController extends AbstractController { diff --git a/web_link.rst b/web_link.rst index 5630223d82b..73700e1c99e 100644 --- a/web_link.rst +++ b/web_link.rst @@ -145,10 +145,10 @@ You can also add links to the HTTP response directly from controllers and servic // src/Controller/BlogController.php namespace App\Controller; - use Fig\Link\GenericLinkProvider; - use Fig\Link\Link; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\WebLink\GenericLinkProvider; + use Symfony\Component\WebLink\Link; class BlogController extends AbstractController { From 34e7cc52d88b8bd830eb098b3301fa303d7d2cb4 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Thu, 16 May 2019 19:10:03 +0200 Subject: [PATCH 0310/8077] [Workflow] config doc --- reference/configuration/framework.rst | 13 ++++++------- workflow.rst | 22 +++++----------------- workflow/introduction.rst | 2 +- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index c03cc8500c7..8bceae41cf8 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -255,7 +255,7 @@ Configuration * :ref:`name ` * `audit_trail`_ - * `initial_place`_ + * `initial_marking`_ * `marking_store`_ * `places`_ * `supports`_ @@ -2736,12 +2736,12 @@ audit_trail If set to ``true``, the :class:`Symfony\\Component\\Workflow\\EventListener\\AuditTrailListener` will be enabled. -initial_place -""""""""""""" +initial_marking +""""""""""""""" -**type**: ``string`` **default**: ``null`` +**type**: ``string`` | ``array`` -One of the ``places`` or ``null``. If not null and the supported object is not +One of the ``places`` or ``empty``. If not null and the supported object is not already initialized via the workflow, this place will be set. marking_store @@ -2753,8 +2753,7 @@ Each marking store can define any of these options: * ``arguments`` (**type**: ``array``) * ``service`` (**type**: ``string``) -* ``type`` (**type**: ``string`` **possible values**: ``'multiple_state'`` or - ``'single_state'``) +* ``type`` (**type**: ``string`` **allow value**: ``'method'``) places """""" diff --git a/workflow.rst b/workflow.rst index cf1a5802036..b668e6f5fa0 100644 --- a/workflow.rst +++ b/workflow.rst @@ -54,12 +54,12 @@ like this: audit_trail: enabled: true marking_store: - type: 'multiple_state' # or 'single_state' - arguments: + type: 'method' + property: - 'currentPlace' supports: - App\Entity\BlogPost - initial_place: draft + initial_marking: draft places: - draft - reviewed @@ -134,8 +134,8 @@ like this: 'enabled' => true ], 'marking_store' => [ - 'type' => 'multiple_state', // or 'single_state' - 'arguments' => ['currentPlace'] + 'type' => 'method' + 'property' => ['currentPlace'] ], 'supports' => ['App\Entity\BlogPost'], 'places' => [ @@ -177,18 +177,6 @@ As configured, the following property is used by the marking store:: public $content; } -.. note:: - - The marking store type could be "multiple_state" or "single_state". A single - state marking store does not support a model being on multiple places at the - same time. - -.. tip:: - - The ``type`` (default value ``single_state``) and ``arguments`` (default - value ``marking``) attributes of the ``marking_store`` option are optional. - If omitted, their default values will be used. - .. tip:: Setting the ``audit_trail.enabled`` option to ``true`` makes the application diff --git a/workflow/introduction.rst b/workflow/introduction.rst index c8302870ac1..0b146164f03 100644 --- a/workflow/introduction.rst +++ b/workflow/introduction.rst @@ -79,7 +79,7 @@ Below is the configuration for the pull request state machine. type: 'state_machine' supports: - App\Entity\PullRequest - initial_place: start + initial_marking: start places: - start - coding From cf3a32c5f7269e49cca4b9da06178522b11debc9 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Mon, 20 May 2019 09:31:23 +0200 Subject: [PATCH 0311/8077] Doc config metadata --- reference/configuration/framework.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 8bceae41cf8..08f55dcf2a7 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -257,6 +257,7 @@ Configuration * `audit_trail`_ * `initial_marking`_ * `marking_store`_ + * `metadata`_ * `places`_ * `supports`_ * `support_strategy`_ @@ -2755,6 +2756,15 @@ Each marking store can define any of these options: * ``service`` (**type**: ``string``) * ``type`` (**type**: ``string`` **allow value**: ``'method'``) +metadata +"""""""" + +**type**: ``array`` + +Metadata available for the workflow configuration. +Note that ``places`` and ``transitions`` can also have their own +``metadata`` entry. + places """""" From f65f1bcad46935aa8cc9a4d6a77a20af4496c362 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Mon, 27 May 2019 07:20:31 +0200 Subject: [PATCH 0312/8077] Review --- workflow.rst | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/workflow.rst b/workflow.rst index b668e6f5fa0..69eb91984ac 100644 --- a/workflow.rst +++ b/workflow.rst @@ -90,35 +90,28 @@ like this: - currentPlace - App\Entity\BlogPost - + draft draft reviewed rejected published - draft reviewed - reviewed published - reviewed rejected - - @@ -138,6 +131,7 @@ like this: 'property' => ['currentPlace'] ], 'supports' => ['App\Entity\BlogPost'], + 'initial_marking' => 'draft', 'places' => [ 'draft', 'reviewed', @@ -618,8 +612,8 @@ requires: reviewed published - 20 - You can not publish after 8 PM. + 20 + You can not publish after 8 PM. From 7bb204129d091f0fbfbadc0c7b2821c9e55165c1 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Mon, 27 May 2019 07:26:04 +0200 Subject: [PATCH 0313/8077] Fix indent --- workflow/introduction.rst | 76 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/workflow/introduction.rst b/workflow/introduction.rst index 0b146164f03..385ec8e8cd1 100644 --- a/workflow/introduction.rst +++ b/workflow/introduction.rst @@ -190,46 +190,46 @@ Below is the configuration for the pull request state machine. // ... 'workflows' => [ 'pull_request' => [ - 'type' => 'state_machine', - 'supports' => ['App\Entity\PullRequest'], - 'places' => [ - 'start', - 'coding', - 'test', - 'review', - 'merged', - 'closed', - ], - 'transitions' => [ - 'submit'=> [ - 'from' => 'start', - 'to' => 'test', + 'type' => 'state_machine', + 'supports' => ['App\Entity\PullRequest'], + 'places' => [ + 'start', + 'coding', + 'test', + 'review', + 'merged', + 'closed', ], - 'update'=> [ - 'from' => ['coding', 'test', 'review'], - 'to' => 'test', + 'transitions' => [ + 'submit'=> [ + 'from' => 'start', + 'to' => 'test', + ], + 'update'=> [ + 'from' => ['coding', 'test', 'review'], + 'to' => 'test', + ], + 'wait_for_review'=> [ + 'from' => 'test', + 'to' => 'review', + ], + 'request_change'=> [ + 'from' => 'review', + 'to' => 'coding', + ], + 'accept'=> [ + 'from' => 'review', + 'to' => 'merged', + ], + 'reject'=> [ + 'from' => 'review', + 'to' => 'closed', + ], + 'reopen'=> [ + 'from' => 'start', + 'to' => 'review', + ], ], - 'wait_for_review'=> [ - 'from' => 'test', - 'to' => 'review', - ], - 'request_change'=> [ - 'from' => 'review', - 'to' => 'coding', - ], - 'accept'=> [ - 'from' => 'review', - 'to' => 'merged', - ], - 'reject'=> [ - 'from' => 'review', - 'to' => 'closed', - ], - 'reopen'=> [ - 'from' => 'start', - 'to' => 'review', - ], - ], ], ], ]); From a640d147c5981f482ae7fbeb4254ac5c27c79275 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Thu, 16 May 2019 19:10:03 +0200 Subject: [PATCH 0314/8077] [Workflow] config doc --- components/workflow.rst | 3 +- workflow.rst | 3 +- workflow/dumping-workflows.rst | 106 ++++++++++++++++----------------- 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/components/workflow.rst b/components/workflow.rst index 137695ddb0f..de936fa5baa 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -79,10 +79,11 @@ Usage ----- When you have configured a ``Registry`` with your workflows, -you can retrieve a workflow from it and use it as follows:: +you can retreive a workflow from it and use it as follows:: // ... // Consider that $blogPost is in place "draft" by default + // Consider that $post is in state "draft" by default $blogPost = new BlogPost(); $workflow = $registry->get($blogPost); diff --git a/workflow.rst b/workflow.rst index 69eb91984ac..a0573f3c9e9 100644 --- a/workflow.rst +++ b/workflow.rst @@ -182,7 +182,7 @@ what actions are allowed on a blog post:: use App\Entity\BlogPost; use Symfony\Component\Workflow\Exception\LogicException; - $post = BlogPost(); + $post = new BlogPost(); $workflow = $this->container->get('workflow.blog_publishing'); $workflow->can($post, 'publish'); // False @@ -664,6 +664,7 @@ Then you can access this metadata in your controller as follows:: use App\Entity\BlogPost; use Symfony\Component\Workflow\Registry; + use App\Entity\BlogPost; public function myController(Registry $registry, BlogPost $post) { diff --git a/workflow/dumping-workflows.rst b/workflow/dumping-workflows.rst index 78a2d566606..2499dd35225 100644 --- a/workflow/dumping-workflows.rst +++ b/workflow/dumping-workflows.rst @@ -228,63 +228,63 @@ Below is the configuration for the pull request state machine with styling added // ... 'workflows' => [ 'pull_request' => [ - 'type' => 'state_machine', - 'supports' => ['App\Entity\PullRequest'], - 'places' => [ - 'start', - 'coding', - 'test', - 'review' => [ - 'metadata' => [ - 'description' => 'Human review', + 'type' => 'state_machine', + 'supports' => ['App\Entity\PullRequest'], + 'places' => [ + 'start', + 'coding', + 'test', + 'review' => [ + 'metadata' => [ + 'description' => 'Human review', + ], ], - ], - 'merged', - 'closed' => [ - 'metadata' => [ - 'bg_color' => 'DeepSkyBlue', + 'merged', + 'closed' => [ + 'metadata' => [ + 'bg_color' => 'DeepSkyBlue', + ], ], ], - ], - 'transitions' => [ - 'submit'=> [ - 'from' => 'start', - 'to' => 'test', - ], - 'update'=> [ - 'from' => ['coding', 'test', 'review'], - 'to' => 'test', - 'metadata' => [ - 'arrow_color' => 'Turquoise', - ], - ], - 'wait_for_review'=> [ - 'from' => 'test', - 'to' => 'review', - 'metadata' => [ - 'color' => 'Orange', - ], - ], - 'request_change'=> [ - 'from' => 'review', - 'to' => 'coding', - ], - 'accept'=> [ - 'from' => 'review', - 'to' => 'merged', - 'metadata' => [ - 'label' => 'Accept PR', - ], - ], - 'reject'=> [ - 'from' => 'review', - 'to' => 'closed', - ], - 'reopen'=> [ - 'from' => 'start', - 'to' => 'review', + 'transitions' => [ + 'submit'=> [ + 'from' => 'start', + 'to' => 'test', + ], + 'update'=> [ + 'from' => ['coding', 'test', 'review'], + 'to' => 'test', + 'metadata' => [ + 'arrow_color' => 'Turquoise', + ], + ], + 'wait_for_review'=> [ + 'from' => 'test', + 'to' => 'review', + 'metadata' => [ + 'color' => 'Orange', + ], + ], + 'request_change'=> [ + 'from' => 'review', + 'to' => 'coding', + ], + 'accept'=> [ + 'from' => 'review', + 'to' => 'merged', + 'metadata' => [ + 'label' => 'Accept PR', + ], + ], + 'reject'=> [ + 'from' => 'review', + 'to' => 'closed', + ], + 'reopen'=> [ + 'from' => 'start', + 'to' => 'review', + ], ], - ], ], ], ]); From fdc221b525399c9dd1c970d622dac2127cb538c4 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 12 Aug 2019 22:42:59 +0200 Subject: [PATCH 0315/8077] Minor XML improvement --- workflow.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow.rst b/workflow.rst index a0573f3c9e9..f477ace9665 100644 --- a/workflow.rst +++ b/workflow.rst @@ -94,7 +94,7 @@ like this: currentPlace App\Entity\BlogPost - draft + draft draft reviewed rejected From 403369768582b839f99dbb299639d503f316d6b8 Mon Sep 17 00:00:00 2001 From: Nicolas Hart Date: Tue, 13 Aug 2019 09:13:00 +0200 Subject: [PATCH 0316/8077] [Messenger] Fix NewOrdersFromCsvFileReceiver::get return type --- components/messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/messenger.rst b/components/messenger.rst index e3cc98436f7..bdcaaecc073 100644 --- a/components/messenger.rst +++ b/components/messenger.rst @@ -282,7 +282,7 @@ do is to write your own CSV receiver:: $this->filePath = $filePath; } - public function get(): void + public function get(): iterable { $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); From 53e50981bfc56f64dc5d0efdb8c6ed2e4e81219a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 13 Aug 2019 09:20:14 +0200 Subject: [PATCH 0317/8077] [#12146] fix typos --- components/workflow.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/workflow.rst b/components/workflow.rst index de936fa5baa..137695ddb0f 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -79,11 +79,10 @@ Usage ----- When you have configured a ``Registry`` with your workflows, -you can retreive a workflow from it and use it as follows:: +you can retrieve a workflow from it and use it as follows:: // ... // Consider that $blogPost is in place "draft" by default - // Consider that $post is in state "draft" by default $blogPost = new BlogPost(); $workflow = $registry->get($blogPost); From 8f799bc4cf40a966247439076d24dc4ef2d0496e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 13 Aug 2019 09:58:09 +0200 Subject: [PATCH 0318/8077] Added the Code of Conduct file --- CODE_OF_CONDUCT.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..d211dd419d0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,83 @@ +Code of Conduct +=============== + +Our Pledge +---------- + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnic origin, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +religion, or sexual identity and orientation. + +Our Standards +------------- + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities +-------------------- + +[CoC Active Response Ensurers, or CARE][1], are responsible for clarifying the +standards of acceptable behavior and are expected to take appropriate and fair +corrective action in response to any instances of unacceptable behavior. + +CARE team members have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +Scope +----- + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by CARE team members. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior +[may be reported][2] by contacting the [CARE team members][1]. +All complaints will be reviewed and investigated and will result in a response +that is deemed necessary and appropriate to the circumstances. The CARE team is +obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. + +CARE team members who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by the +[core team][3]. + +Attribution +----------- + +This Code of Conduct is adapted from the [Contributor Covenant version 1.4][4]. + +[1]: https://symfony.com/doc/current/contributing/code_of_conduct/care_team.html +[2]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html +[3]: https://symfony.com/doc/current/contributing/code/core_team.html +[4]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html From a0dd1d1938c40ae7d53f1ddc903e84dc08352806 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 13 Aug 2019 10:33:02 +0200 Subject: [PATCH 0319/8077] Added the LICENSE file --- LICENSE.md | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000000..01524e6ec84 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,340 @@ +LICENSE +======= + +**Creative Commons Attribution-ShareAlike 3.0 Unported** +https://creativecommons.org/licenses/by-sa/3.0/ + +----- + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS +PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR +OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS +LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE +BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED +TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN +CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions +-------------- + +a. **"Adaptation"** means a work based upon the Work, or upon the Work and other +pre-existing works, such as a translation, adaptation, derivative work, +arrangement of music or other alterations of a literary or artistic work, or +phonogram or performance and includes cinematographic adaptations or any other +form in which the Work may be recast, transformed, or adapted including in any +form recognizably derived from the original, except that a work that constitutes +a Collection will not be considered an Adaptation for the purpose of this +License. For the avoidance of doubt, where the Work is a musical work, +performance or phonogram, the synchronization of the Work in timed-relation with +a moving image ("synching") will be considered an Adaptation for the purpose of +this License. + +b. **"Collection"** means a collection of literary or artistic works, such as +encyclopedias and anthologies, or performances, phonograms or broadcasts, or +other works or subject matter other than works listed in Section 1(f) below, +which, by reason of the selection and arrangement of their contents, constitute +intellectual creations, in which the Work is included in its entirety in +unmodified form along with one or more other contributions, each constituting +separate and independent works in themselves, which together are assembled into +a collective whole. A work that constitutes a Collection will not be considered +an Adaptation (as defined below) for the purposes of this License. + +c. **"Creative Commons Compatible License"** means a license that is listed at +https://creativecommons.org/compatiblelicenses that has been approved by +Creative Commons as being essentially equivalent to this License, including, at +a minimum, because that license: (i) contains terms that have the same purpose, +meaning and effect as the License Elements of this License; and, (ii) explicitly +permits the relicensing of adaptations of works made available under that +license under this License or a Creative Commons jurisdiction license with the +same License Elements as this License. + +d. **"Distribute"** means to make available to the public the original and +copies of the Work or Adaptation, as appropriate, through sale or other transfer +of ownership. + +e. **"License Elements"** means the following high-level license attributes as +selected by Licensor and indicated in the title of this License: Attribution, +ShareAlike. + +f. **"Licensor"** means the individual, individuals, entity or entities that +offer(s) the Work under the terms of this License. + +g. **"Original Author""** means, in the case of a literary or artistic work, the +individual, individuals, entity or entities who created the Work or if no +individual or entity can be identified, the publisher; and in addition (i) in +the case of a performance the actors, singers, musicians, dancers, and other +persons who act, sing, deliver, declaim, play in, interpret or otherwise perform +literary or artistic works or expressions of folklore; (ii) in the case of a +phonogram the producer being the person or legal entity who first fixes the +sounds of a performance or other sounds; and, (iii) in the case of broadcasts, +the organization that transmits the broadcast. + +h. **"Work"** means the literary and/or artistic work offered under the terms of +this License including without limitation any production in the literary, +scientific and artistic domain, whatever may be the mode or form of its +expression including digital form, such as a book, pamphlet and other writing; a +lecture, address, sermon or other work of the same nature; a dramatic or +dramatico-musical work; a choreographic work or entertainment in dumb show; a +musical composition with or without words; a cinematographic work to which are +assimilated works expressed by a process analogous to cinematography; a work of +drawing, painting, architecture, sculpture, engraving or lithography; a +photographic work to which are assimilated works expressed by a process +analogous to photography; a work of applied art; an illustration, map, plan, +sketch or three-dimensional work relative to geography, topography, architecture +or science; a performance; a broadcast; a phonogram; a compilation of data to +the extent it is protected as a copyrightable work; or a work performed by a +variety or circus performer to the extent it is not otherwise considered a +literary or artistic work. + +i. **"You"** means an individual or entity exercising rights under this License +who has not previously violated the terms of this License with respect to the +Work, or who has received express permission from the Licensor to exercise +rights under this License despite a previous violation. + +j. **"Publicly Perform"** means to perform public recitations of the Work and to +communicate to the public those public recitations, by any means or process, +including by wire or wireless means or public digital performances; to make +available to the public Works in such a way that members of the public may +access these Works from a place and at a place individually chosen by them; to +perform the Work to the public by any means or process and the communication to +the public of the performances of the Work, including by public digital +performance; to broadcast and rebroadcast the Work by any means including signs, +sounds or images. + +k. **"Reproduce"** means to make copies of the Work by any means including +without limitation by sound or visual recordings and the right of fixation and +reproducing fixations of the Work, including storage of a protected performance +or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights +---------------------- + +Nothing in this License is intended to reduce, limit, or restrict any uses free +from copyright or rights arising from limitations or exceptions that are +provided for in connection with the copyright protection under copyright law or +other applicable laws. + +3. License Grant +---------------- + +Subject to the terms and conditions of this License, Licensor hereby grants You +a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the +applicable copyright) license to exercise the rights in the Work as stated +below: + +a. to Reproduce the Work, to incorporate the Work into one or more Collections, +and to Reproduce the Work as incorporated in the Collections; + +b. to create and Reproduce Adaptations provided that any such Adaptation, +including any translation in any medium, takes reasonable steps to clearly +label, demarcate or otherwise identify that changes were made to the original +Work. For example, a translation could be marked "The original work was +translated from English to Spanish," or a modification could indicate "The +original work has been modified."; + +c. to Distribute and Publicly Perform the Work including as incorporated in +Collections; and, + +d. to Distribute and Publicly Perform Adaptations. + +e. For the avoidance of doubt: + + 1. **Non-waivable Compulsory License Schemes.** In those jurisdictions in + which the right to collect royalties through any statutory or compulsory + licensing scheme cannot be waived, the Licensor reserves the exclusive + right to collect such royalties for any exercise by You of the rights + granted under this License; + + 2. **Waivable Compulsory License Schemes.** In those jurisdictions in which + the right to collect royalties through any statutory or compulsory + licensing scheme can be waived, the Licensor waives the exclusive right to + collect such royalties for any exercise by You of the rights granted under + this License; and, + + 3. **Voluntary License Schemes.** The Licensor waives the right to collect + royalties, whether individually or, in the event that the Licensor is a + member of a collecting society that administers voluntary licensing + schemes, via that society, from any exercise by You of the rights granted + under this License. + +The above rights may be exercised in all media and formats whether now known or +hereafter devised. The above rights include the right to make such modifications +as are technically necessary to exercise the rights in other media and formats. +Subject to Section 8(f), all rights not expressly granted by Licensor are hereby +reserved. + +4. Restrictions +--------------- + +The license granted in Section 3 above is expressly made subject to and limited +by the following restrictions: + +a. You may Distribute or Publicly Perform the Work only under the terms of this +License. You must include a copy of, or the Uniform Resource Identifier (URI) +for, this License with every copy of the Work You Distribute or Publicly +Perform. You may not offer or impose any terms on the Work that restrict the +terms of this License or the ability of the recipient of the Work to exercise +the rights granted to that recipient under the terms of the License. You may not +sublicense the Work. You must keep intact all notices that refer to this License +and to the disclaimer of warranties with every copy of the Work You Distribute +or Publicly Perform. When You Distribute or Publicly Perform the Work, You may +not impose any effective technological measures on the Work that restrict the +ability of a recipient of the Work from You to exercise the rights granted to +that recipient under the terms of the License. This Section 4(a) applies to the +Work as incorporated in a Collection, but this does not require the Collection +apart from the Work itself to be made subject to the terms of this License. If +You create a Collection, upon notice from any Licensor You must, to the extent +practicable, remove from the Collection any credit as required by Section 4(c), +as requested. If You create an Adaptation, upon notice from any Licensor You +must, to the extent practicable, remove from the Adaptation any credit as +required by Section 4(c), as requested. + +b. You may Distribute or Publicly Perform an Adaptation only under the terms of: +(i) this License; (ii) a later version of this License with the same License +Elements as this License; (iii) a Creative Commons jurisdiction license (either +this or a later license version) that contains the same License Elements as this +License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons +Compatible License. If you license the Adaptation under one of the licenses +mentioned in (iv), you must comply with the terms of that license. If you +license the Adaptation under the terms of any of the licenses mentioned in (i), +(ii) or (iii) (the "Applicable License"), you must comply with the terms of the +Applicable License generally and the following provisions: (I) You must include +a copy of, or the URI for, the Applicable License with every copy of each +Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose +any terms on the Adaptation that restrict the terms of the Applicable License or +the ability of the recipient of the Adaptation to exercise the rights granted to +that recipient under the terms of the Applicable License; (III) You must keep +intact all notices that refer to the Applicable License and to the disclaimer of +warranties with every copy of the Work as included in the Adaptation You +Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the +Adaptation, You may not impose any effective technological measures on the +Adaptation that restrict the ability of a recipient of the Adaptation from You +to exercise the rights granted to that recipient under the terms of the +Applicable License. This Section 4(b) applies to the Adaptation as incorporated +in a Collection, but this does not require the Collection apart from the +Adaptation itself to be made subject to the terms of the Applicable License. + +c. If You Distribute, or Publicly Perform the Work or any Adaptations or +Collections, You must, unless a request has been made pursuant to Section 4(a), +keep intact all copyright notices for the Work and provide, reasonable to the +medium or means You are utilizing: (i) the name of the Original Author (or +pseudonym, if applicable) if supplied, and/or if the Original Author and/or +Licensor designate another party or parties (e.g., a sponsor institute, +publishing entity, journal) for attribution ("Attribution Parties") in +Licensor's copyright notice, terms of service or by other reasonable means, the +name of such party or parties; (ii) the title of the Work if supplied; (iii) to +the extent reasonably practicable, the URI, if any, that Licensor specifies to +be associated with the Work, unless such URI does not refer to the copyright +notice or licensing information for the Work; and (iv) , consistent with Section +3(b), in the case of an Adaptation, a credit identifying the use of the Work in +the Adaptation (e.g., "French translation of the Work by Original Author," or +"Screenplay based on original Work by Original Author"). The credit required by +this Section 4(c) may be implemented in any reasonable manner; provided, +however, that in the case of a Adaptation or Collection, at a minimum such +credit will appear, if a credit for all contributing authors of the Adaptation +or Collection appears, then as part of these credits and in a manner at least as +prominent as the credits for the other contributing authors. For the avoidance +of doubt, You may only use the credit required by this Section for the purpose +of attribution in the manner set out above and, by exercising Your rights under +this License, You may not implicitly or explicitly assert or imply any +connection with, sponsorship or endorsement by the Original Author, Licensor +and/or Attribution Parties, as appropriate, of You or Your use of the Work, +without the separate, express prior written permission of the Original Author, +Licensor and/or Attribution Parties. + +d. Except as otherwise agreed in writing by the Licensor or as may be otherwise +permitted by applicable law, if You Reproduce, Distribute or Publicly Perform +the Work either by itself or as part of any Adaptations or Collections, You must +not distort, mutilate, modify or take other derogatory action in relation to the +Work which would be prejudicial to the Original Author's honor or reputation. +Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise +of the right granted in Section 3(b) of this License (the right to make +Adaptations) would be deemed to be a distortion, mutilation, modification or +other derogatory action prejudicial to the Original Author's honor and +reputation, the Licensor will waive or not assert, as appropriate, this Section, +to the fullest extent permitted by the applicable national law, to enable You to +reasonably exercise Your right under Section 3(b) of this License (right to make +Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer +--------------------------------------------- + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS +THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING +THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT +LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR +PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, +OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME +JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH +EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability +-------------------------- + +EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE +LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, +PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE +WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination +-------------- + +a. This License and the rights granted hereunder will terminate automatically +upon any breach by You of the terms of this License. Individuals or entities who +have received Adaptations or Collections from You under this License, however, +will not have their licenses terminated provided such individuals or entities +remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 +will survive any termination of this License. + +b. Subject to the above terms and conditions, the license granted here is +perpetual (for the duration of the applicable copyright in the Work). +Notwithstanding the above, Licensor reserves the right to release the Work under +different license terms or to stop distributing the Work at any time; provided, +however that any such election will not serve to withdraw this License (or any +other license that has been, or is required to be, granted under the terms of +this License), and this License will continue in full force and effect unless +terminated as stated above. + +8. Miscellaneous +---------------- + +a. Each time You Distribute or Publicly Perform the Work or a Collection, the +Licensor offers to the recipient a license to the Work on the same terms and +conditions as the license granted to You under this License. + +b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers +to the recipient a license to the original Work on the same terms and conditions +as the license granted to You under this License. + +c. If any provision of this License is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this License, and without further action by the parties to this +agreement, such provision shall be reformed to the minimum extent necessary to +make such provision valid and enforceable. + +d. No term or provision of this License shall be deemed waived and no breach +consented to unless such waiver or consent shall be in writing and signed by the +party to be charged with such waiver or consent. + +e. This License constitutes the entire agreement between the parties with +respect to the Work licensed here. There are no understandings, agreements or +representations with respect to the Work not specified here. Licensor shall not +be bound by any additional provisions that may appear in any communication from +You. This License may not be modified without the mutual written agreement of +the Licensor and You. + +f. The rights granted under, and the subject matter referenced, in this License +were drafted utilizing the terminology of the Berne Convention for the +Protection of Literary and Artistic Works (as amended on September 28, 1979), +the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO +Performances and Phonograms Treaty of 1996 and the Universal Copyright +Convention (as revised on July 24, 1971). These rights and subject matter take +effect in the relevant jurisdiction in which the License terms are sought to be +enforced according to the corresponding provisions of the implementation of +those treaty provisions in the applicable national law. If the standard suite of +rights granted under applicable copyright law includes additional rights not +granted under this License, such additional rights are deemed to be included in +the License; this License is not intended to restrict the license of any rights +under applicable law. From ee19b6bc9303a098734be6fcd432926afeac3007 Mon Sep 17 00:00:00 2001 From: Johann Pardanaud Date: Tue, 13 Aug 2019 10:53:50 +0200 Subject: [PATCH 0320/8077] Fix ctype links --- reference/constraints/Type.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/constraints/Type.rst b/reference/constraints/Type.rst index 5a23f2f3c95..1bdd49519ca 100644 --- a/reference/constraints/Type.rst +++ b/reference/constraints/Type.rst @@ -175,5 +175,5 @@ You can use the following parameters in this message: .. include:: /reference/constraints/_payload-option.rst.inc -.. _built-in PHP extension: https://php.net/book.ctype.php -.. _a list of ctype functions: https://php.net/ref.ctype.php +.. _built-in PHP extension: https://php.net/book.ctype +.. _a list of ctype functions: https://php.net/ref.ctype From 27339f223344daad5e1cd359f248a2d46832a96f Mon Sep 17 00:00:00 2001 From: smoench Date: Tue, 13 Aug 2019 11:13:04 +0200 Subject: [PATCH 0321/8077] [Messenger] Information about why consumers do not show up in the (rabbimq) admin panel --- messenger.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/messenger.rst b/messenger.rst index cbf1676ee44..a1a9629d3f8 100644 --- a/messenger.rst +++ b/messenger.rst @@ -759,6 +759,17 @@ your Envelope:: new AmqpStamp('custom-routing-key', AMQP_NOPARAM, $attributes) ]); +.. caution:: + + The consumers do not show up in an admin panel as this transport does not rely on + ``\AmqpQueue::consume()`` which is blocking. Having a blocking receiver makes + the ``--time-limit/--memory-limit`` options of the ``messenger:consume`` command as well as + the ``messenger:stop-workers`` command inefficient, as they all rely on the fact that + the receiver returns immediately no matter if it finds a message or not. The consume + worker is responsible for iterating until it receives a message to handle and/or until one + of the stop conditions is reached. Thus, the worker's stop logic cannot be reached if it + is stuck in a blocking call. + Doctrine Transport ~~~~~~~~~~~~~~~~~~ From 9d6d8646fe3e38b870da2029c0ad3b15e45cf014 Mon Sep 17 00:00:00 2001 From: Denis Brumann Date: Tue, 13 Aug 2019 14:06:22 +0200 Subject: [PATCH 0322/8077] Fixes typo in routing. --- routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.rst b/routing.rst index 8acc0f38cc3..75405cac9f4 100644 --- a/routing.rst +++ b/routing.rst @@ -281,7 +281,7 @@ arbitrary matching logic: The value of the ``condition`` option is any valid :doc:`ExpressionLanguage expression ` -and can use and of these variables created by Symfony: +and can use any of these variables created by Symfony: ``context`` An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, From 6f816efdef065bef74db663865c395ebd2ed62db Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 13 Aug 2019 08:56:49 +0200 Subject: [PATCH 0323/8077] Mention the new check:requirements command --- deployment.rst | 6 ++++-- reference/requirements.rst | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/deployment.rst b/deployment.rst index fef394ef256..bf5c07165b6 100644 --- a/deployment.rst +++ b/deployment.rst @@ -117,11 +117,12 @@ you'll need to do: A) Check Requirements ~~~~~~~~~~~~~~~~~~~~~ -Check if your server meets the requirements by running: +Check if your server meets the requirements by running the following command +provided by the ``symfony`` binary created when `installing Symfony`_: .. code-block:: terminal - $ php bin/symfony_requirements + $ symfony check:requirements .. _b-configure-your-app-config-parameters-yml-file: @@ -248,3 +249,4 @@ kernel and return your project's root directory:: .. _`Deployer`: http://deployer.org/ .. _`Git Tagging`: https://git-scm.com/book/en/v2/Git-Basics-Tagging .. _`EasyDeployBundle`: https://github.com/EasyCorp/easy-deploy-bundle +.. _`installing Symfony`: https://symfony.com/download diff --git a/reference/requirements.rst b/reference/requirements.rst index a252ee07bab..15d9a9e6ae1 100644 --- a/reference/requirements.rst +++ b/reference/requirements.rst @@ -29,14 +29,18 @@ to avoid leaking internal information about your application to visitors. Checking Requirements for the Command Console --------------------------------------------- -Open your console or terminal, enter in your project directory, execute this -command and fix the reported issues: +Open your console or terminal and run the following command provided by the +``symfony`` binary created when `installing Symfony`_: .. code-block:: terminal - $ cd my-project/ - $ php bin/symfony_requirements + # checks if your computer/server is ready to run Symfony projects + $ symfony check:requirements + + # use the --dir option to also check if some specific Symfony project is ready to be run + $ symfony check:requirements --dir=/path/to/my-project .. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard .. _`skeleton`: https://github.com/symfony/skeleton .. _`website-skeleton`: https://github.com/symfony/website-skeleton +.. _`installing Symfony`: https://symfony.com/download From 3f0406f0c645544d213efeefe125dbc0bba8902d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 12 Aug 2019 22:31:57 +0200 Subject: [PATCH 0324/8077] Updated the main Form article --- _build/redirection_map | 1 + best_practices/forms.rst | 2 + components/form.rst | 4 +- doctrine.rst | 2 + form/action_method.rst | 132 ---- form/create_custom_field_type.rst | 2 +- form/direct_submit.rst | 49 +- form/form_themes.rst | 5 +- form/validation_groups.rst | 9 +- forms.rst | 895 +++++++++++++++----------- reference/configuration/framework.rst | 3 +- reference/dic_tags.rst | 2 +- validation.rst | 2 +- 13 files changed, 554 insertions(+), 554 deletions(-) delete mode 100644 form/action_method.rst diff --git a/_build/redirection_map b/_build/redirection_map index 10b3bc9432a..f9c0a2b3fc3 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -443,3 +443,4 @@ /routing/hostname_pattern /routing /routing/extra_information /routing /console/request_context /routing +/form/action_method /forms diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 74b3f70f3de..07596e73e54 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -212,6 +212,8 @@ Handling a form submit usually follows a similar template:: // render the template } +.. _best-practice-handle-form: + We recommend that you use a single action for both rendering the form and handling the form submit. For example, you *could* have a ``new()`` action that *only* renders the form and a ``create()`` action that *only* processes the form diff --git a/components/form.rst b/components/form.rst index 60b4abf874c..3a39154afda 100644 --- a/components/form.rst +++ b/components/form.rst @@ -80,8 +80,8 @@ object to read data off of the correct PHP superglobals (i.e. ``$_POST`` or .. seealso:: If you need more control over exactly when your form is submitted or which - data is passed to it, you can use the :method:`Symfony\\Component\\Form\\FormInterface::submit` - for this. Read more about it :ref:`form-call-submit-directly`. + data is passed to it, + :doc:`use the submit() method to handle form submissions `. .. sidebar:: Integration with the HttpFoundation Component diff --git a/doctrine.rst b/doctrine.rst index e12e4d9546b..93b049f3965 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -418,6 +418,8 @@ Take a look at the previous example in more detail: Whether you're creating or updating objects, the workflow is always the same: Doctrine is smart enough to know if it should INSERT or UPDATE your entity. +.. _automatic_object_validation: + Validating Objects ------------------ diff --git a/form/action_method.rst b/form/action_method.rst deleted file mode 100644 index f0b40db24e7..00000000000 --- a/form/action_method.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. index:: - single: Forms; Changing the action and method - -How to Change the Action and Method of a Form -============================================= - -By default, a form will be submitted via an HTTP POST request to the same -URL under which the form was rendered. Sometimes you want to change these -parameters. You can do so in a few different ways. - -If you use the :class:`Symfony\\Component\\Form\\FormBuilder` to build your -form, you can use ``setAction()`` and ``setMethod()``: - -.. configuration-block:: - - .. code-block:: php-symfony - - // src/Controller/DefaultController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - use Symfony\Component\Form\Extension\Core\Type\DateType; - use Symfony\Component\Form\Extension\Core\Type\SubmitType; - use Symfony\Component\Form\Extension\Core\Type\TextType; - - class DefaultController extends AbstractController - { - public function new() - { - // ... - - $form = $this->createFormBuilder($task) - ->setAction($this->generateUrl('target_route')) - ->setMethod('GET') - ->add('task', TextType::class) - ->add('dueDate', DateType::class) - ->add('save', SubmitType::class) - ->getForm(); - - // ... - } - } - - .. code-block:: php-standalone - - use Symfony\Component\Form\Forms; - use Symfony\Component\Form\Extension\Core\Type\DateType; - use Symfony\Component\Form\Extension\Core\Type\FormType; - use Symfony\Component\Form\Extension\Core\Type\SubmitType; - use Symfony\Component\Form\Extension\Core\Type\TextType; - - // ... - - $formFactoryBuilder = Forms::createFormFactoryBuilder(); - - // Form factory builder configuration ... - - $formFactory = $formFactoryBuilder->getFormFactory(); - - $form = $formFactory->createBuilder(FormType::class, $task) - ->setAction('...') - ->setMethod('GET') - ->add('task', TextType::class) - ->add('dueDate', DateType::class) - ->add('save', SubmitType::class) - ->getForm(); - -.. note:: - - This example assumes that you've created a route called ``target_route`` - that points to the controller that processes the form. - -When using a form type class, you can pass the action and method as form -options: - -.. configuration-block:: - - .. code-block:: php-symfony - - // src/Controller/DefaultController.php - namespace App\Controller; - - use App\Form\TaskType; - use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; - - class DefaultController extends AbstractController - { - public function new() - { - // ... - - $form = $this->createForm(TaskType::class, $task, [ - 'action' => $this->generateUrl('target_route'), - 'method' => 'GET', - ]); - - // ... - } - } - - .. code-block:: php-standalone - - use App\Form\TaskType; - use Symfony\Component\Form\Forms; - - $formFactoryBuilder = Forms::createFormFactoryBuilder(); - - // Form factory builder configuration ... - - $formFactory = $formFactoryBuilder->getFormFactory(); - - $form = $formFactory->create(TaskType::class, $task, [ - 'action' => '...', - 'method' => 'GET', - ]); - -Finally, you can override the action and method in the template by passing them -to the ``form()`` or the ``form_start()`` helper functions: - -.. code-block:: twig - - {# templates/default/new.html.twig #} - {{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }} - -.. note:: - - If the form's method is not GET or POST, but PUT, PATCH or DELETE, Symfony - will insert a hidden field with the name ``_method`` that stores this method. - The form will be submitted in a normal POST request, but Symfony's router - is capable of detecting the ``_method`` parameter and will interpret it as - a PUT, PATCH or DELETE request. See the :ref:`configuration-framework-http_method_override` - option. diff --git a/form/create_custom_field_type.rst b/form/create_custom_field_type.rst index f4356a008d8..cc9ffe65df1 100644 --- a/form/create_custom_field_type.rst +++ b/form/create_custom_field_type.rst @@ -137,7 +137,7 @@ These are the most important methods that a form type class can define: ``buildForm()`` It adds and configures other types into this type. It's the same method used - when :ref:`creating Symfony form classes `. + when :ref:`creating Symfony form classes `. ``buildView()`` It sets any extra variables you'll need when rendering the field in a template. diff --git a/form/direct_submit.rst b/form/direct_submit.rst index 77d2819aafe..48388b100e5 100644 --- a/form/direct_submit.rst +++ b/form/direct_submit.rst @@ -4,53 +4,18 @@ How to Use the submit() Function to Handle Form Submissions =========================================================== -Handle the form submission with the ``handleRequest()`` method:: +The recommended way of :ref:`processing Symfony forms ` is to +use the :method:`Symfony\\Component\\Form\\FormInterface::handleRequest` method +to detect when the form has been submitted. However, you can also use the +:method:`Symfony\\Component\\Form\\FormInterface::submit` method to have better +control over when exactly your form is submitted and what data is passed to it:: use Symfony\Component\HttpFoundation\Request; // ... public function new(Request $request) { - $form = $this->createFormBuilder() - // ... - ->getForm(); - - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - // perform some action... - - return $this->redirectToRoute('task_success'); - } - - return $this->render('product/new.html.twig', [ - 'form' => $form->createView(), - ]); - } - -.. tip:: - - To see more about this method, read :ref:`form-handling-form-submissions`. - -.. _form-call-submit-directly: - -Calling Form::submit() manually -------------------------------- - -In some cases, you want better control over when exactly your form is submitted -and what data is passed to it. Instead of using the -:method:`Symfony\\Component\\Form\\FormInterface::handleRequest` -method, pass the submitted data directly to -:method:`Symfony\\Component\\Form\\FormInterface::submit`:: - - use Symfony\Component\HttpFoundation\Request; - // ... - - public function new(Request $request) - { - $form = $this->createFormBuilder() - // ... - ->getForm(); + $form = $this->createForm(TaskType::class, $task); if ($request->isMethod('POST')) { $form->submit($request->request->get($form->getName())); @@ -62,7 +27,7 @@ method, pass the submitted data directly to } } - return $this->render('product/new.html.twig', [ + return $this->render('task/new.html.twig', [ 'form' => $form->createView(), ]); } diff --git a/form/form_themes.rst b/form/form_themes.rst index 32322381c08..ad64b8f49a9 100644 --- a/form/form_themes.rst +++ b/form/form_themes.rst @@ -15,7 +15,8 @@ Symfony Built-In Form Themes Symfony comes with several **built-in form themes** that make your forms look great when using some of the most popular CSS frameworks. Each theme is defined -in a single Twig template: +in a single Twig template and they are enabled in the +:ref:`twig.form_themes ` option: * `form_div_layout.html.twig`_, wraps each form field inside a ``
`` element and it's the theme used by default in Symfony applications unless you configure @@ -182,6 +183,8 @@ of form themes: {# ... #} +.. _create-your-own-form-theme: + Creating your Own Form Theme ---------------------------- diff --git a/form/validation_groups.rst b/form/validation_groups.rst index 2b8d87e43e5..09b420bceaf 100644 --- a/form/validation_groups.rst +++ b/form/validation_groups.rst @@ -8,21 +8,22 @@ Validation Groups ----------------- If your object takes advantage of :doc:`validation groups `, -you'll need to specify which validation group(s) your form should use:: +you'll need to specify which validation group(s) your form should use. Pass +this as an option when :ref:`creating forms in controllers `:: $form = $this->createFormBuilder($user, [ 'validation_groups' => ['registration'], ])->add(...); -If you're creating :ref:`form classes ` (a good -practice), then you'll need to add the following to the ``configureOptions()`` -method:: +When :ref:`creating forms in classes `, add the +following to the ``configureOptions()`` method:: use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ + // ... 'validation_groups' => ['registration'], ]); } diff --git a/forms.rst b/forms.rst index c0e03f74419..5848d120321 100644 --- a/forms.rst +++ b/forms.rst @@ -9,10 +9,10 @@ Forms Do you prefer video tutorials? Check out the `Symfony Forms screencast series`_. -Dealing with HTML forms is one of the most common - and challenging - tasks for -a web developer. Symfony integrates a Form component that helps you dealing -with forms. In this article, you'll build a complex form from the ground up, -learning the most important features of the form library along the way. +Creating and processing HTML forms is hard and repetitive. You need to deal with +rendering HTML form fields, validating submitted data, mapping the form data +into objects and a lot more. Symfony includes a powerful form feature that +provides all these features and many more for truly complex scenarios. Installation ------------ @@ -24,22 +24,22 @@ install the form feature before using it: $ composer require symfony/form -.. note:: +Usage +----- - The Symfony Form component is a standalone library that can be used outside - of Symfony projects. For more information, see the - :doc:`Form component documentation ` on GitHub. +The recommended workflow when working with Symfony forms is the following: -.. index:: - single: Forms; Create a simple form +#. **Build the form** in a Symfony controller or using a dedicated form class; +#. **Render the form** in a template so the user can edit and submit it; +#. **Process the form** to validate the submitted data, transform it into PHP + data and do something with it (e.g. persist it in a database). -Creating a Simple Form ----------------------- +Each of these steps is explained in detail in the next sections. To make +examples easier to follow, all of them assume that you're building a simple Todo +list application that displays "tasks". -Suppose you're building a simple todo list application that will need to -display "tasks". Because your users will need to edit and create tasks, you're -going to need to build a form. But before you begin, first focus on the generic -``Task`` class that represents and stores the data for a single task:: +Users create and edit tasks using Symfony forms. Each task is an instance of the +following ``Task`` class:: // src/Entity/Task.php namespace App\Entity; @@ -73,20 +73,45 @@ going to need to build a form. But before you begin, first focus on the generic This class is a "plain-old-PHP-object" because, so far, it has nothing to do with Symfony or any other library. It's a normal PHP object that directly solves a problem inside *your* application (i.e. the need to represent a task in your -application). By the end of this article, you'll be able to submit data to a -``Task`` instance (via an HTML form), validate its data and persist it to the -database. +application). But you can also edit :doc:`Doctrine entities ` in the +same way. -.. index:: - single: Forms; Create a form in a controller +.. _form-types: -Building the Form -~~~~~~~~~~~~~~~~~ +Form Types +~~~~~~~~~~ + +Before creating your first Symfony form, it's important to understand the +concept of "form type". In other projects, it's common to differentiate between +"forms" and "form fields". In Symfony, all of them are "form types": + +* a single ```` form field is a "form type" (e.g. ``TextType``); +* a group of several HTML fields used to input a postal address is a "form type" + (e.g. ``PostalAddressType``); +* an entire ``
`` with multiple fields to edit a user profile is a + "form type" (e.g. ``UserProfileType``). + +This may be confusing at first, but it will feel natural to you soon enough. +Besides, it simplifies code and makes "composing" and "embedding" form fields +much easier to implement. + +There are tens of :doc:`form types provided by Symfony ` +and you can also :doc:`create your own form types
`. + +Building Forms +-------------- + +Symfony provides a "form builder" object which allows you to describe the form +fields using a fluent interface. Later, this builder creates the actual form +object used to render and process contents. + +.. _creating-forms-in-controllers: -Now that you've created a ``Task`` class, the next step is to create and -render the actual HTML form. In Symfony, this is done by building a form -object and then rendering it in a template. For now, this can all be done -from inside a controller:: +Creating Forms in Controllers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your controller extends from the :ref:`AbstractController `, +use the ``createFormBuilder()`` helper:: // src/Controller/TaskController.php namespace App\Controller; @@ -102,7 +127,7 @@ from inside a controller:: { public function new(Request $request) { - // creates a task and gives it some dummy data for this example + // creates a task object and initializes some data for this example $task = new Task(); $task->setTask('Write a blog post'); $task->setDueDate(new \DateTime('tomorrow')); @@ -113,119 +138,244 @@ from inside a controller:: ->add('save', SubmitType::class, ['label' => 'Create Task']) ->getForm(); - return $this->render('task/new.html.twig', [ - 'form' => $form->createView(), - ]); + // ... } } -.. tip:: +If your controller does not extend from ``AbstractController``, you'll need to +:ref:`fetch services in your controller ` and +use the ``createBuilder()`` method of the ``form.factory`` service. - This example shows you how to build your form directly in the controller. - Later, in the ":ref:`form-creating-form-classes`" section, you'll learn - how to build your form in a standalone class, which is recommended as - your form becomes reusable. +In this example, you've added two fields to your form - ``task`` and ``dueDate`` +- corresponding to the ``task`` and ``dueDate`` properties of the ``Task`` +class. You've also assigned each a :ref:`form type ` (e.g. ``TextType`` +and ``DateType``), represented by its fully qualified class name. Finally, you +added a submit button with a custom label for submitting the form to the server. -Creating a form requires relatively little code because Symfony form objects -are built with a "form builder". The form builder's purpose is to allow you -to write simple form "recipes" and have it do all the heavy-lifting of actually -building the form. +.. _creating-forms-in-classes: -In this example, you've added two fields to your form - ``task`` and ``dueDate`` - -corresponding to the ``task`` and ``dueDate`` properties of the ``Task`` class. -You've also assigned each a "type" (e.g. ``TextType`` and ``DateType``), -represented by its fully qualified class name. Among other things, it determines -which HTML form tag(s) is rendered for that field. +Creating Form Classes +~~~~~~~~~~~~~~~~~~~~~ -Finally, you added a submit button with a custom label for submitting the form to -the server. +Symfony recommends to :doc:`create thin controllers `. +That's why it's better to move complex forms to dedicated classes instead of +defining them in controller actions. Besides, forms defined in classes can be +reused in multiple actions and services. -Symfony comes with many built-in types that will be discussed shortly -(see :ref:`forms-type-reference`). +Form classes are :ref:`form types ` that implement +:class:`Symfony\\Component\\Form\\FormTypeInterface`. However, it's better to +extend from :class:`Symfony\\Component\\Form\\AbstractType`, which already +implements the interface and provides some utilities:: -.. index:: - single: Forms; Basic template rendering + // src/Form/Type/TaskType.php + namespace App\Form; -Rendering the Form -~~~~~~~~~~~~~~~~~~ + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\Extension\Core\Type\DateType; + use Symfony\Component\Form\Extension\Core\Type\SubmitType; + use Symfony\Component\Form\Extension\Core\Type\TextType; + use Symfony\Component\Form\FormBuilderInterface; + + class TaskType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('task', TextType::class) + ->add('dueDate', DateType::class) + ->add('save', SubmitType::class) + ; + } + } + +The form class contains all the directions needed to create the task form. In +controllers extending from the :ref:`AbstractController `, +use the ``createForm()`` helper (otherwise, use the ``create()`` method of the +``form.factory`` service):: + + // src/Controller/TaskController.php + use App\Form\Type\TaskType; + // ... + + class TaskController extends AbstractController + { + public function new() + { + // creates a task object and initializes some data for this example + $task = new Task(); + $task->setTask('Write a blog post'); + $task->setDueDate(new \DateTime('tomorrow')); + + $form = $this->createForm(TaskType::class, $task); + + // ... + } + } + +.. _form-data-class: + +Every form needs to know the name of the class that holds the underlying data +(e.g. ``App\Entity\Task``). Usually, this is just guessed based off of the +object passed to the second argument to ``createForm()`` (i.e. ``$task``). +Later, when you begin :doc:`embedding forms `, this will no +longer be sufficient. + +So, while not always necessary, it's generally a good idea to explicitly specify +the ``data_class`` option by adding the following to your form type class:: -Now that the form has been created, the next step is to render it. This is -done by passing a special form "view" object to your template (notice the -``$form->createView()`` in the controller above) and using a set of -:ref:`form helper functions `: + // src/Form/Type/TaskType.php + use App\Entity\Task; + use Symfony\Component\OptionsResolver\OptionsResolver; + // ... + + class TaskType extends AbstractType + { + // ... + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Task::class, + ]); + } + } + +Rendering Forms +--------------- + +Now that the form has been created, the next step is to render it. Instead of +passing the entire form object to the template, use the ``createView()`` method +to build another object with the visual representation of the form:: + + // src/Controller/TaskController.php + namespace App\Controller; + + use App\Entity\Task; + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Form\Extension\Core\Type\DateType; + use Symfony\Component\Form\Extension\Core\Type\SubmitType; + use Symfony\Component\Form\Extension\Core\Type\TextType; + use Symfony\Component\HttpFoundation\Request; + + class TaskController extends AbstractController + { + public function new(Request $request) + { + $task = new Task(); + // ... + + $form = $this->createForm(TaskType::class, $task); + + return $this->render('task/new.html.twig', [ + 'form' => $form->createView(), + ]); + } + } + +Then, use some :ref:`form helper functions ` to +render the form contents: .. code-block:: twig {# templates/task/new.html.twig #} {{ form(form) }} -.. image:: /_images/form/simple-form.png - :align: center - That's it! The :ref:`form() function ` renders all fields *and* the ``
`` start and end tags. By default, the form method is -``POST`` and the target URL is the same that displayed the form. - -As short as this is, it's not very flexible. Usually, you'll need more control -about how the entire form or some of its fields look. Symfony provides several -ways of doing that: - -* If your app uses a CSS framework such as Bootstrap or Foundation, use any of - the :ref:`built-in form themes ` to make all your forms - match the style of the rest of your app; -* If you want to customize only a few fields or a few forms of your app, read - the :doc:`How to Customize Form Rendering ` article; -* If you want to customize all your forms in the same way, create a - :doc:`Symfony form theme ` (based on any of the built-in - themes or from scratch). - -Before moving on, notice how the rendered ``task`` input field has the value -of the ``task`` property from the ``$task`` object (i.e. "Write a blog post"). -This is the first job of a form: to take data from an object and translate -it into a format that's suitable for being rendered in an HTML form. +``POST`` and the target URL is the same that displayed the form, but +:ref:`you can change both `. + +Notice how the rendered ``task`` input field has the value of the ``task`` +property from the ``$task`` object (i.e. "Write a blog post"). This is the first +job of a form: to take data from an object and translate it into a format that's +suitable for being rendered in an HTML form. .. tip:: The form system is smart enough to access the value of the protected ``task`` property via the ``getTask()`` and ``setTask()`` methods on the ``Task`` class. Unless a property is public, it *must* have a "getter" and - "setter" method so that the Form component can get and put data onto the - property. For a boolean property, you can use an "isser" or "hasser" method - (e.g. ``isPublished()`` or ``hasReminder()``) instead of a getter (e.g. + "setter" method so that Symfony can get and put data onto the property. For + a boolean property, you can use an "isser" or "hasser" method (e.g. + ``isPublished()`` or ``hasReminder()``) instead of a getter (e.g. ``getPublished()`` or ``getReminder()``). -.. index:: - single: Forms; Handling form submissions +As short as this rendering is, it's not very flexible. Usually, you'll need more +control about how the entire form or some of its fields look. For example, thanks +to the :doc:`Bootstrap 4 integration with Symfony forms ` you +can set this option to generate forms compatible with the Bootstrap 4 CSS framework: -.. _form-handling-form-submissions: +.. configuration-block:: -Handling Form Submissions -~~~~~~~~~~~~~~~~~~~~~~~~~ + .. code-block:: yaml -By default, the form will submit a POST request back to the same controller that -renders it. + # config/packages/twig.yaml + twig: + form_themes: ['bootstrap_4_layout.html.twig'] -Here, the second job of a form is to translate user-submitted data back to the -properties of an object. To make this happen, the submitted data from the -user must be written into the Form object. Add the following functionality to -your controller:: + .. code-block:: xml + + + + + + + bootstrap_4_layout.html.twig + + + + + .. code-block:: php + + // config/packages/twig.php + $container->loadFromExtension('twig', [ + 'form_themes' => [ + 'bootstrap_4_layout.html.twig', + ], + + // ... + ]); + +The :ref:`built-in Symfony form themes ` include +Bootstrap 3 and 4 and Foundation 5. You can also +:ref:`create your own Symfony form theme `. + +In addition to form themes, Symfony allows you to +:doc:`customize the way fields are rendered ` with +multiple functions to render each field part separately (widgets, labels, +errors, help messages, etc.) + +.. _processing-forms: + +Processing Forms +---------------- + +The :ref:`recommended way of processing forms ` is to +use a single action for both rendering the form and handling the form submit. +You can use separate actions, but using one action simplifies everything while +keeping the code concise and maintainable. + +Processing a form means to translate user-submitted data back to the properties +of an object. To make this happen, the submitted data from the user must be +written into the form object:: // ... use Symfony\Component\HttpFoundation\Request; public function new(Request $request) { - // just setup a fresh $task object (remove the dummy data) + // just setup a fresh $task object (remove the example data) $task = new Task(); - $form = $this->createFormBuilder($task) - ->add('task', TextType::class) - ->add('dueDate', DateType::class) - ->add('save', SubmitType::class, ['label' => 'Create Task']) - ->getForm(); + $form = $this->createForm(TaskType::class, $task); $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { // $form->getData() holds the submitted values // but, the original `$task` variable has also been updated @@ -245,25 +395,17 @@ your controller:: ]); } -.. caution:: - - Be aware that the ``createView()`` method should be called *after* ``handleRequest()`` - is called. Otherwise, changes done in the ``*_SUBMIT`` events aren't applied to the - view (like validation errors). - This controller follows a common pattern for handling forms and has three possible paths: -#. When initially loading the page in a browser, the form is created and - rendered. :method:`Symfony\\Component\\Form\\FormInterface::handleRequest` - recognizes that the form was not submitted and does nothing. - :method:`Symfony\\Component\\Form\\FormInterface::isSubmitted` returns ``false`` - if the form was not submitted. +#. When initially loading the page in a browser, the form hasn't been submitted + yet and ``$form->isSubmitted()`` returns ``false``. So, the form is created + and rendered. #. When the user submits the form, :method:`Symfony\\Component\\Form\\FormInterface::handleRequest` recognizes this and immediately writes the submitted data back into the ``task`` and ``dueDate`` properties of the ``$task`` object. Then this object - is validated. If it is invalid (validation is covered in the next section), + is validated (validation is explained in the next section). If it is invalid, :method:`Symfony\\Component\\Form\\FormInterface::isValid` returns ``false`` and the form is rendered again, but now with validation errors; @@ -273,32 +415,34 @@ possible paths: the ``$task`` object (e.g. persisting it to the database) before redirecting the user to some other page (e.g. a "thank you" or "success" page). - .. note:: +.. note:: + + Redirecting a user after a successful form submission is a best practice + that prevents the user from being able to hit the "Refresh" button of + their browser and re-post the data. - Redirecting a user after a successful form submission prevents the user - from being able to hit the "Refresh" button of their browser and re-post - the data. +.. caution:: + + The ``createView()`` method should be called *after* ``handleRequest()`` is + called. Otherwise, when using :doc:`form events `, changes done + in the ``*_SUBMIT`` events won't be applied to the view (like validation errors). .. seealso:: If you need more control over exactly when your form is submitted or which - data is passed to it, you can use the :method:`Symfony\\Component\\Form\\FormInterface::submit` - method. Read more about it :ref:`form-call-submit-directly`. + data is passed to it, you can + :doc:`use the submit() method to handle form submissions `. -.. index:: - single: Forms; Validation +.. _validating-forms: -.. _forms-form-validation: - -Form Validation ---------------- +Validating Forms +---------------- In the previous section, you learned how a form can be submitted with valid -or invalid data. In Symfony, validation is applied to the underlying object -(e.g. ``Task``). In other words, the question isn't whether the "form" is -valid, but whether or not the ``$task`` object is valid after the form has -applied the submitted data to it. Calling ``$form->isValid()`` is a shortcut -that asks the ``$task`` object whether or not it has valid data. +or invalid data. In Symfony, the question isn't whether the "form" is valid, but +whether or not the underlying object (``$task`` in this example) is valid after +the form has applied the submitted data to it. Calling ``$form->isValid()`` is a +shortcut that asks the ``$task`` object whether or not it has valid data. Before using validation, add support for it in your application: @@ -389,215 +533,210 @@ object. } That's it! If you re-submit the form with invalid data, you'll see the -corresponding errors printed out with the form. +corresponding errors printed out with the form. Read the +:doc:`Symfony validation documentation ` to learn more about this +powerful feature. -Validation is a very powerful feature of Symfony and has its own -:doc:`dedicated article `. +Other Common Form Features +-------------------------- -.. _forms-html5-validation-disable: +Form Type Options +~~~~~~~~~~~~~~~~~ -.. sidebar:: HTML5 Validation +Each :ref:`form type ` has a number of options to configure it, as +explained in the :doc:`Symfony form types reference `. +Two commonly used options options are ``required`` and ``label``. - Thanks to HTML5, many browsers can natively enforce certain validation constraints - on the client side. The most common validation is activated by rendering - a ``required`` attribute on fields that are required. For browsers that - support HTML5, this will result in a native browser message being displayed - if the user tries to submit the form with that field blank. +The ``required`` Option +....................... - Generated forms take full advantage of this new feature by adding sensible - HTML attributes that trigger the validation. The client-side validation, - however, can be disabled by adding the ``novalidate`` attribute to the - ``form`` tag or ``formnovalidate`` to the submit tag. This is especially - useful when you want to test your server-side validation constraints, - but are being prevented by your browser from, for example, submitting - blank fields. +The most common option is the ``required`` option, which can be applied to any +field. By default, this option is set to ``true``, meaning that HTML5-ready +browsers will require to fill in all fields before submitting the form. - .. code-block:: twig +If you don't want this behavior, either +:ref:`disable client-side validation ` for the +entire form or set the ``required`` option to ``false`` on one or more fields:: - {# templates/task/new.html.twig #} - {{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }} - {{ form_widget(form) }} - {{ form_end(form) }} + ->add('dueDate', DateType::class, [ + 'required' => false, + ]) -.. index:: - single: Forms; Built-in field types +The ``required`` option does not perform any server-side validation. If a user +submits a blank value for the field (either with an old browser or a web +service, for example), it will be accepted as a valid value unless you also use +Symfony's ``NotBlank`` or ``NotNull`` validation constraints. -.. _forms-type-reference: +The ``label`` Option +.................... -Built-in Field Types --------------------- +By default, the label of form fields are the *humanized* version of the +property name (``user`` -> ``User``; ``postalAddress`` -> ``Postal Address``). +Set the ``label`` option on fields to define their labels explicitly:: -Symfony comes standard with a large group of field types that cover all of -the common form fields and data types you'll encounter: + ->add('dueDate', DateType::class, [ + // set it to FALSE to not display the label for this field + 'label' => 'To Be Completed Before', + ]) -.. include:: /reference/forms/types/map.rst.inc +.. tip:: -You can also create your own custom field types. See -:doc:`/form/create_custom_field_type` for info. + By default, ``