diff --git a/_build/redirection_map b/_build/redirection_map
index 983c68f78ab..fdb85af6438 100644
--- a/_build/redirection_map
+++ b/_build/redirection_map
@@ -77,14 +77,15 @@
/book/configuration /configuration
/book/propel /propel/propel
/book/performance /performance
+/bundles/installation /bundles
/cookbook/assetic/apply_to_option /frontend/assetic/apply_to_option
/cookbook/assetic/asset_management /frontend/assetic/asset_management
-/cookbook/assetic/index /frontend/assetic
+/cookbook/assetic/index /frontend/assetic/index
/cookbook/assetic/jpeg_optimize /frontend/assetic/jpeg_optimize
/cookbook/assetic/php /frontend/assetic/php
/cookbook/assetic/uglifyjs /frontend/assetic/uglifyjs
/cookbook/assetic/yuicompressor /frontend/assetic/yuicompressor
-/assetic /frontend/assetic
+/assetic /frontend/assetic/index
/assetic/apply_to_option /frontend/assetic/apply_to_option
/assetic/asset_management /frontend/assetic/asset_management
/assetic/jpeg_optimize /frontend/assetic/jpeg_optimize
@@ -96,10 +97,11 @@
/cookbook/bundles/extension /bundles/extension
/cookbook/bundles/index /bundles
/cookbook/bundles/inheritance /bundles/inheritance
-/cookbook/bundles/installation /bundles/installation
+/cookbook/bundles/installation /bundles
/cookbook/bundles/override /bundles/override
/cookbook/bundles/prepend_extension /bundles/prepend_extension
-/cookbook/bundles/remove /bundles/remove
+/cookbook/bundles/remove /bundles
+/bundles/remove /bundles
/cookbook/cache/form_csrf_caching /http_cache/form_csrf_caching
/cookbook/cache/varnish /http_cache/varnish
/cookbook/composer /setup/composer
@@ -120,14 +122,16 @@
/cookbook/console/logging /console
/cookbook/console/request_context /console/request_context
/cookbook/console/style /console/style
-/cookbook/console/usage /console/usage
-/cookbook/controller/csrf_token_validation /controller/csrf_token_validation
+/cookbook/console/usage /console
+/console/usage /console
+/cookbook/controller/csrf_token_validation /security/csrf
/cookbook/controller/error_pages /controller/error_pages
/cookbook/controller/forwarding /controller/forwarding
/cookbook/controller/index /controller
/cookbook/controller/service /controller/service
/cookbook/controller/upload_file /controller/upload_file
-/cookbook/debugging /debug/debugging
+/cookbook/debugging /
+/debug/debugging /
/cookbook/deployment/azure-website /cookbook/azure-website
/cookbook/deployment/fortrabbit /deployment/fortrabbit
/cookbook/deployment/heroku /deployment/heroku
@@ -135,22 +139,25 @@
/cookbook/deployment/platformsh /deployment/platformsh
/cookbook/deployment/tools /deployment/tools
/cookbook/doctrine/common_extensions /doctrine/common_extensions
-/cookbook/doctrine/console /doctrine/console
+/cookbook/doctrine/console /doctrine
/cookbook/doctrine/custom_dql_functions /doctrine/custom_dql_functions
/cookbook/doctrine/dbal /doctrine/dbal
/cookbook/doctrine/event_listeners_subscribers /doctrine/event_listeners_subscribers
/cookbook/doctrine/index /doctrine
-/cookbook/doctrine/mapping_model_classes /doctrine/mapping_model_classes
+/cookbook/doctrine/mapping_model_classes /doctrine
+/doctrine/mapping_model_classes /doctrine
/cookbook/doctrine/mongodb_session_storage /doctrine/mongodb_session_storage
/cookbook/doctrine/multiple_entity_managers /doctrine/multiple_entity_managers
/cookbook/doctrine/pdo_session_storage /doctrine/pdo_session_storage
/cookbook/doctrine/registration_form /doctrine/registration_form
/cookbook/doctrine/resolve_target_entity /doctrine/resolve_target_entity
/cookbook/doctrine/reverse_engineering /doctrine/reverse_engineering
-/cookbook/email/cloud /email/cloud
+/doctrine/repository /doctrine
+/doctrine/console /doctrine
+/cookbook/email/cloud /email
/cookbook/email/dev_environment /email/dev_environment
/cookbook/email/email /email
-/cookbook/email/gmail /email/gmail
+/cookbook/email/gmail /email
/cookbook/email/index /email
/cookbook/email/spool /email/spool
/cookbook/email/testing /email/testing
@@ -214,7 +221,7 @@
/cookbook/security/acl /security/acl
/cookbook/security/acl_advanced /security/acl_advanced
/cookbook/security/api_key_authentication /security/api_key_authentication
-/cookbook/security/csrf_in_login_form /security/csrf_in_login_form
+/cookbook/security/csrf_in_login_form /security/csrf
/cookbook/security/custom_authentication_provider /security/custom_authentication_provider
/cookbook/security/custom_password_authenticator /security/custom_password_authenticator
/cookbook/security/custom_provider /security/custom_provider
@@ -280,18 +287,28 @@
/cookbook/web_services/php_soap_extension /controller/soap_web_service
/cookbook/workflow/homestead /setup/homestead
/cookbook/workflow/index /setup
-/cookbook/workflow/new_project_git /setup/new_project_git
-/cookbook/workflow/new_project_svn /setup/new_project_svn
+/cookbook/workflow/new_project_git /setup
+/cookbook/workflow/new_project_svn /setup
+/setup/new_project_git /setup
+/setup/new_project_svn /setup
/components/asset/index /components/asset
/components/asset/introduction /components/asset
/components/browser_kit/index /components/browser_kit
/components/browser_kit/introduction /components/browser_kit
/components/class_loader/introduction /components/class_loader
/components/class_loader/index /components/class_loader
+/components/class_loader/cache_class_loader /components/class_loader
+/components/class_loader/class_loader /components/class_loader
+/components/class_loader/class_map_generator /components/class_loader
+/components/class_loader/debug_class_loader /components/class_loader
+/components/class_loader/map_class_loader /components/class_loader
+/components/class_loader/map_class_loader /components/class_loader
+/components/class_loader/psr4_class_loader /components/class_loader
/components/config/introduction /components/config
/components/config/index /components/config
/components/console/helpers/tablehelper /components/console/helpers/table
/components/console/helpers/progresshelper /components/console/helpers/progressbar
+/components/console/helpers/dialoghelper /components/console/helpers/questionhelper
/components/console/introduction /components/console
/components/console/index /components/console
/components/debug/class_loader /components/debug
@@ -343,21 +360,56 @@
/components/var_dumper/index /components/var_dumper
/components/yaml/introduction /components/yaml
/components/yaml/index /components/yaml
+/console/logging /console
+/controller/csrf_token_validation /security/csrf
/deployment/tools /deployment
+/form/csrf_protection /security/csrf
/install/bundles /setup/bundles
+/email/gmail /email
+/email/cloud /email
/event_dispatcher/class_extension /event_dispatcher
/form /forms
/form/use_virtual_forms /form/inherit_data_option
+/frontend/assetic /frontend/assetic/index
+/frontend/assetic/apply_to_option /frontend/assetic/index
+/frontend/assetic/asset_management /frontend/assetic/index
+/frontend/assetic/jpeg_optimize /frontend/assetic/index
+/frontend/assetic/php /frontend/assetic/index
+/frontend/assetic/uglifyjs /frontend/assetic/index
+/frontend/assetic/yuicompressor /frontend/assetic/index
+/reference/configuration/assetic /frontend/assetic/index
/security/target_path /security
+/security/csrf_in_login_form /security/csrf
/service_container/service_locators /service_container/service_subscribers_locators
/service_container/third_party /service_container
/templating/templating_service /templates
/testing/simulating_authentication /testing/http_authentication
/validation/group_service_resolver /form/validation_group_service_resolver
/request/load_balancer_reverse_proxy /deployment/proxies
+/quick_tour/the_controller /quick_tour/the_big_picture
+/quick_tour/the_view /quick_tour/flex_recipes
/service_container/service_locators /service_container/service_subscribers_locators
+/templating/overriding /bundles/override
+/security/custom_provider /security/user_provider
+/security/multiple_user_providers /security/user_provider
+/security/custom_password_authenticator /security/guard_authentication
+/security/api_key_authentication /security/guard_authentication
+/security/pre_authenticated /security/auth_providers
+/security/host_restriction /security/firewall_restriction
+/security/acl_advanced /security/acl
+/security/password_encoding /security
/weblink /web_link
/components/weblink /components/web_link
/frontend/encore/installation-no-flex /frontend/encore/installation
+/http_cache/form_csrf_caching /security/csrf
/console/logging /console
+/reference/forms/twig_reference /form/form_customization
+/form/rendering /form/form_customization
+/profiler/matchers /profiler
+/profiler/profiling_data /profiler
+/profiler/wdt_follow_ajax /profiler
+/security/entity_provider /security/user_provider
+/session/avoid_session_start /session
+/session/sessions_directory /session
/frontend/encore/legacy-apps /frontend/encore/legacy-applications
+/configuration/external_parameters /configuration/environment_variables
diff --git a/_images/components/messenger/overview.svg b/_images/components/messenger/overview.svg
new file mode 100644
index 00000000000..94737e7a6da
--- /dev/null
+++ b/_images/components/messenger/overview.svg
@@ -0,0 +1 @@
+
diff --git a/_images/components/serializer/serializer_workflow.png b/_images/components/serializer/serializer_workflow.png
deleted file mode 100644
index 3e1944e6cec..00000000000
Binary files a/_images/components/serializer/serializer_workflow.png and /dev/null differ
diff --git a/_images/components/serializer/serializer_workflow.svg b/_images/components/serializer/serializer_workflow.svg
new file mode 100644
index 00000000000..f3906506878
--- /dev/null
+++ b/_images/components/serializer/serializer_workflow.svg
@@ -0,0 +1 @@
+
diff --git a/_images/components/workflow/blogpost_puml.png b/_images/components/workflow/blogpost_puml.png
new file mode 100644
index 00000000000..14d45c8b40f
Binary files /dev/null and b/_images/components/workflow/blogpost_puml.png differ
diff --git a/_images/form/form-field-parts.svg b/_images/form/form-field-parts.svg
new file mode 100644
index 00000000000..c9856c89a99
--- /dev/null
+++ b/_images/form/form-field-parts.svg
@@ -0,0 +1 @@
+
diff --git a/_images/http/request-flow.png b/_images/http/request-flow.png
deleted file mode 100644
index cbf4019307b..00000000000
Binary files a/_images/http/request-flow.png and /dev/null differ
diff --git a/_images/http/request-flow.svg b/_images/http/request-flow.svg
new file mode 100644
index 00000000000..97061ada0d5
--- /dev/null
+++ b/_images/http/request-flow.svg
@@ -0,0 +1 @@
+
diff --git a/_images/mercure/chrome.png b/_images/mercure/chrome.png
new file mode 100644
index 00000000000..8ccc55a0a88
Binary files /dev/null and b/_images/mercure/chrome.png differ
diff --git a/_images/mercure/discovery.png b/_images/mercure/discovery.png
new file mode 100644
index 00000000000..0ef38271de6
Binary files /dev/null and b/_images/mercure/discovery.png differ
diff --git a/_images/mercure/schema.png b/_images/mercure/schema.png
new file mode 100644
index 00000000000..4616046e5cc
Binary files /dev/null and b/_images/mercure/schema.png differ
diff --git a/_images/profiler/web-interface.png b/_images/profiler/web-interface.png
new file mode 100644
index 00000000000..2e6c6061892
Binary files /dev/null and b/_images/profiler/web-interface.png differ
diff --git a/_images/quick_tour/no_routes_page.png b/_images/quick_tour/no_routes_page.png
new file mode 100644
index 00000000000..8c8c4d508d1
Binary files /dev/null and b/_images/quick_tour/no_routes_page.png differ
diff --git a/_images/quick_tour/profiler.png b/_images/quick_tour/profiler.png
deleted file mode 100644
index 3b55b75f3af..00000000000
Binary files a/_images/quick_tour/profiler.png and /dev/null differ
diff --git a/_images/quick_tour/web_debug_toolbar.png b/_images/quick_tour/web_debug_toolbar.png
index 72cd7483f2f..465020380cb 100644
Binary files a/_images/quick_tour/web_debug_toolbar.png and b/_images/quick_tour/web_debug_toolbar.png differ
diff --git a/_images/quick_tour/welcome.png b/_images/quick_tour/welcome.png
deleted file mode 100644
index 738105f715d..00000000000
Binary files a/_images/quick_tour/welcome.png and /dev/null differ
diff --git a/_images/security/authentication-guard-methods.svg b/_images/security/authentication-guard-methods.svg
index a18da9e66dc..cc042656212 100644
--- a/_images/security/authentication-guard-methods.svg
+++ b/_images/security/authentication-guard-methods.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/_images/security/http_basic_popup.png b/_images/security/http_basic_popup.png
deleted file mode 100644
index fcd9a4ed836..00000000000
Binary files a/_images/security/http_basic_popup.png and /dev/null differ
diff --git a/_images/security/symfony_loggedin_wdt.png b/_images/security/symfony_loggedin_wdt.png
index ca182192c94..b51e1cafba1 100644
Binary files a/_images/security/symfony_loggedin_wdt.png and b/_images/security/symfony_loggedin_wdt.png differ
diff --git a/_images/sources/components/messenger/overview.dia b/_images/sources/components/messenger/overview.dia
new file mode 100644
index 00000000000..55ee153439e
Binary files /dev/null and b/_images/sources/components/messenger/overview.dia differ
diff --git a/_images/sources/components/serializer/serializer_workflow.dia b/_images/sources/components/serializer/serializer_workflow.dia
new file mode 100644
index 00000000000..6cb44280d0d
Binary files /dev/null and b/_images/sources/components/serializer/serializer_workflow.dia differ
diff --git a/_images/sources/form/form-field-parts.dia b/_images/sources/form/form-field-parts.dia
new file mode 100644
index 00000000000..d6ed2dfc3fe
Binary files /dev/null and b/_images/sources/form/form-field-parts.dia differ
diff --git a/_images/sources/http/request-flow.dia b/_images/sources/http/request-flow.dia
new file mode 100644
index 00000000000..ca09a05504e
Binary files /dev/null and b/_images/sources/http/request-flow.dia differ
diff --git a/_images/sources/security/authentication-guard-methods.dia b/_images/sources/security/authentication-guard-methods.dia
index 283e74b2e05..d655be780fe 100644
Binary files a/_images/sources/security/authentication-guard-methods.dia and b/_images/sources/security/authentication-guard-methods.dia differ
diff --git a/_includes/service_container/_my_mailer.rst.inc b/_includes/service_container/_my_mailer.rst.inc
index a944097f917..01eafdfe87a 100644
--- a/_includes/service_container/_my_mailer.rst.inc
+++ b/_includes/service_container/_my_mailer.rst.inc
@@ -2,15 +2,15 @@
.. code-block:: yaml
- # app/config/services.yml
+ # config/services.yaml
services:
app.mailer:
- class: AppBundle\Mailer
+ class: App\Mailer
arguments: [sendmail]
.. code-block:: xml
-
+
-
+ sendmail
@@ -26,8 +26,8 @@
.. code-block:: php
- // app/config/services.php
- use AppBundle\Mailer;
+ // config/services.php
+ use App\Mailer;
$container->register('app.mailer', Mailer::class)
->addArgument('sendmail');
diff --git a/assetic/_standard_edition_warning.rst.inc b/assetic/_standard_edition_warning.rst.inc
deleted file mode 100644
index 2a111cd2291..00000000000
--- a/assetic/_standard_edition_warning.rst.inc
+++ /dev/null
@@ -1,5 +0,0 @@
-.. caution::
-
- Starting from Symfony 2.8, Assetic is no longer included by default in the
- Symfony Standard Edition. Refer to :doc:`this article `
- to learn how to install and enable Assetic in your Symfony application.
diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst
index d4da19721a8..8feb8163bf7 100644
--- a/best_practices/business-logic.rst
+++ b/best_practices/business-logic.rst
@@ -10,92 +10,55 @@ your app that's not specific to the framework (e.g. routing and controllers).
Domain classes, Doctrine entities and regular PHP classes that are used as
services are good examples of business logic.
-For most projects, you should store everything inside the AppBundle.
+For most projects, you should store all your code inside the ``src/`` directory.
Inside here, you can create whatever directories you want to organize things:
.. code-block:: text
symfony-project/
- ├─ app/
+ ├─ config/
+ ├─ public/
├─ src/
- │ └─ AppBundle/
- │ └─ Utils/
- │ └─ MyClass.php
+ │ └─ Utils/
+ │ └─ MyClass.php
├─ tests/
├─ var/
- ├─ vendor/
- └─ web/
+ └─ vendor/
-Storing Classes Outside of the Bundle?
---------------------------------------
+.. _services-naming-and-format:
-But there's no technical reason for putting business logic inside of a bundle.
-If you like, you can create your own namespace inside the ``src/`` directory
-and put things there:
+Services: Naming and Configuration
+----------------------------------
-.. code-block:: text
-
- symfony-project/
- ├─ app/
- ├─ src/
- │ ├─ Acme/
- │ │ └─ Utils/
- │ │ └─ MyClass.php
- │ └─ AppBundle/
- ├─ tests/
- ├─ var/
- ├─ vendor/
- └─ web/
-
-.. tip::
+.. best-practice::
- The recommended approach of using the ``AppBundle/`` directory is for
- simplicity. If you're advanced enough to know what needs to live in
- a bundle and what can live outside of one, then feel free to do that.
+ Use autowiring to automate the configuration of application services.
-Services: Naming and Format
----------------------------
+:doc:`Service autowiring ` is a feature provided
+by Symfony's Service Container to manage services with minimal configuration. It
+reads the type-hints on your constructor (or other methods) and automatically
+passes the correct services to each method. It can also add
+:doc:`service tags ` to the services needing them, such
+as Twig extensions, event subscribers, etc.
The blog application needs a utility that can transform a post title (e.g.
-"Hello World") into a slug (e.g. "hello-world"). The slug will be used as
-part of the post URL.
+"Hello World") into a slug (e.g. "hello-world") to include it as part of the
+post URL. Let's create a new ``Slugger`` class inside ``src/Utils/``::
-Let's create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and
-add the following ``slugify()`` method::
-
- // src/AppBundle/Utils/Slugger.php
- namespace AppBundle\Utils;
+ // src/Utils/Slugger.php
+ namespace App\Utils;
class Slugger
{
- public function slugify($string)
+ public function slugify(string $value): string
{
- return preg_replace(
- '/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
- );
+ // ...
}
}
-Next, define a new service for that class.
-
-.. code-block:: yaml
-
- # app/config/services.yml
- services:
- # ...
-
- # use the fully-qualified class name as the service id
- AppBundle\Utils\Slugger:
- public: false
-
-.. note::
-
- If you're using the :ref:`default services.yml configuration `,
- the class is auto-registered as a service.
-
-Traditionally, the naming convention for a service was a short, but unique
-snake case key - e.g. ``app.utils.slugger``. But for most services, you should now
-use the class name.
+If you're using the :ref:`default services.yaml configuration `,
+this class is auto-registered as a service with the ID ``App\Utils\Slugger`` (to
+prevent against typos, import the class and write ``Slugger::class`` in your code).
.. best-practice::
@@ -103,19 +66,15 @@ use the class name.
except when you have multiple services configured for the same class (in that
case, use a snake case id).
-Now you can use the custom slugger in any controller class, such as the
-``AdminController``::
+Now you can use the custom slugger in any other service or controller class,
+such as the ``AdminController``::
- use AppBundle\Utils\Slugger;
+ use App\Utils\Slugger;
- public function createAction(Request $request, Slugger $slugger)
+ public function create(Request $request, Slugger $slugger)
{
// ...
- // you can also fetch a public service like this
- // but fetching services in this way is not considered a best practice
- // $slugger = $this->get('app.slugger');
-
if ($form->isSubmitted() && $form->isValid()) {
$slug = $slugger->slugify($post->getTitle());
$post->setSlug($slug);
@@ -125,7 +84,7 @@ Now you can use the custom slugger in any controller class, such as the
}
Services can also be :ref:`public or private `. If you use the
-:ref:`default services.yml configuration `,
+:ref:`default services.yaml configuration `,
all services are private by default.
.. best-practice::
@@ -137,11 +96,13 @@ all services are private by default.
Service Format: YAML
--------------------
-In the previous section, YAML was used to define the service.
+If you use the :ref:`default services.yaml configuration `,
+most services will be configured automatically. However, in some edge cases
+you'll need to configure services (or parts of them) manually.
.. best-practice::
- Use the YAML format to define your own services.
+ Use the YAML format to configure your own services.
This is controversial, and in our experience, YAML and XML usage is evenly
distributed among developers, with a slight preference towards YAML.
@@ -149,37 +110,7 @@ Both formats have the same performance, so this is ultimately a matter of
personal taste.
We recommend YAML because it's friendly to newcomers and concise. You can
-of course use whatever format you like.
-
-Service: No Class Parameter
----------------------------
-
-You may have noticed that the previous service definition doesn't configure
-the class namespace as a parameter:
-
-.. code-block:: yaml
-
- # app/config/services.yml
-
- # service definition with class namespace as parameter
- parameters:
- slugger.class: AppBundle\Utils\Slugger
-
- services:
- app.slugger:
- class: '%slugger.class%'
-
-This practice is cumbersome and completely unnecessary for your own services.
-
-.. best-practice::
-
- Don't define parameters for the classes of your services.
-
-This practice was wrongly adopted from third-party bundles. When Symfony
-introduced its service container, some developers used this technique to
-allow overriding services. However, overriding a service by just changing its
-class name is a very rare use case because, frequently, the new service has
-different constructor arguments.
+use any of the other formats if you prefer another format.
Using a Persistence Layer
-------------------------
@@ -192,7 +123,7 @@ library or strategy you want for this.
In practice, many Symfony applications rely on the independent
`Doctrine project`_ to define their model using entities and repositories.
Just like with business logic, we recommend storing Doctrine entities in the
-AppBundle.
+``src/Entity/`` directory.
The three entities defined by our sample blog application are a good example:
@@ -201,16 +132,10 @@ The three entities defined by our sample blog application are a good example:
symfony-project/
├─ ...
└─ src/
- └─ AppBundle/
- └─ Entity/
- ├─ Comment.php
- ├─ Post.php
- └─ User.php
-
-.. tip::
-
- If you're more advanced, you can of course store them under your own
- namespace in ``src/``.
+ └─ Entity/
+ ├─ Comment.php
+ ├─ Post.php
+ └─ User.php
Doctrine Mapping Information
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -227,7 +152,7 @@ PHP and annotations.
Annotations are by far the most convenient and agile way of setting up and
looking for mapping information::
- namespace AppBundle\Entity;
+ namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
@@ -303,29 +228,14 @@ the following command to install the Doctrine fixtures bundle:
$ composer require "doctrine/doctrine-fixtures-bundle"
-Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
+Then, this bundle is enabled automatically, but only for the ``dev`` and
``test`` environments::
- use Symfony\Component\HttpKernel\Kernel;
-
- class AppKernel extends Kernel
- {
- public function registerBundles()
- {
- $bundles = [
- // ...
- ];
-
- if (in_array($this->getEnvironment(), ['dev', 'test'])) {
- // ...
- $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
- }
-
- return $bundles;
- }
-
+ // config/bundles.php
+ return [
// ...
- }
+ Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
+ ];
We recommend creating just *one* `fixture class`_ for simplicity, though
you're welcome to have more if that class gets quite large.
@@ -340,7 +250,7 @@ command:
Careful, database will be purged. Do you want to continue Y/N ? Y
> purging database
- > loading AppBundle\DataFixtures\ORM\LoadFixtures
+ > loading App\DataFixtures\ORM\LoadFixtures
Coding Standards
----------------
diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst
index 87e8a5f7ef5..ab2c38e98a5 100644
--- a/best_practices/configuration.rst
+++ b/best_practices/configuration.rst
@@ -6,62 +6,68 @@ and security credentials) and different environments (development, production).
That's why Symfony recommends that you split the application configuration into
three parts.
-.. _config-parameters.yml:
-
Infrastructure-Related Configuration
------------------------------------
+These are the options that change from one machine to another (e.g. from your
+development machine to the production server) but which don't change the
+application behavior.
+
.. best-practice::
- Define the infrastructure-related configuration options in the
- ``app/config/parameters.yml`` file.
+ 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.
-The default ``parameters.yml`` file follows this recommendation and defines the
-options related to the database and mail server infrastructure:
+By default, Symfony adds these types of options to the ``.env`` file when
+installing new dependencies in the app:
-.. code-block:: yaml
+.. code-block:: bash
- # app/config/parameters.yml
- parameters:
- database_driver: pdo_mysql
- database_host: 127.0.0.1
- database_port: ~
- database_name: symfony
- database_user: root
- database_password: ~
+ # .env
+ ###> doctrine/doctrine-bundle ###
+ DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/blog.sqlite
+ ###< doctrine/doctrine-bundle ###
- mailer_transport: smtp
- mailer_host: 127.0.0.1
- mailer_user: ~
- mailer_password: ~
+ ###> symfony/swiftmailer-bundle ###
+ MAILER_URL=smtp://localhost?encryption=ssl&auth_mode=login&username=&password=
+ ###< symfony/swiftmailer-bundle ###
- # ...
+ # ...
-These options aren't defined inside the ``app/config/config.yml`` file because
+These options aren't defined inside the ``config/services.yaml`` file because
they have nothing to do with the application's behavior. In other words, your
application doesn't care about the location of your database or the credentials
to access to it, as long as the database is correctly configured.
-.. _best-practices-canonical-parameters:
+To override these variables with machine-specific or sensitive values, create a
+``.env.local`` file. This file should not be added to version control.
+
+.. 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.
Canonical Parameters
~~~~~~~~~~~~~~~~~~~~
.. best-practice::
- Define all your application's parameters in the
- ``app/config/parameters.yml.dist`` file.
+ Define all your application's env vars in the ``.env`` file.
+
+Symfony includes a configuration file called ``.env`` at the project root, which
+stores the canonical list of environment variables for the application. This
+file should be stored in version control and so should only contain non-sensitive
+default values.
-Symfony includes a configuration file called ``parameters.yml.dist``, which
-stores the canonical list of configuration parameters for the application.
+.. caution::
-Whenever a new configuration parameter is defined for the application, you
-should also add it to this file and submit the changes to your version control
-system. Then, whenever a developer updates the project or deploys it to a server,
-Symfony will check if there is any difference between the canonical
-``parameters.yml.dist`` file and your local ``parameters.yml`` file. If there
-is a difference, Symfony will ask you to provide a value for the new parameter
-and it will add it to your local ``parameters.yml`` file.
+ Applications created before November 2018 had a slightly different system,
+ involving a ``.env.dist`` file. For information about upgrading, see:
+ :doc:`/configuration/dot-env-changes`.
Application-Related Configuration
---------------------------------
@@ -69,17 +75,17 @@ Application-Related Configuration
.. best-practice::
Define the application behavior related configuration options in the
- ``app/config/config.yml`` file.
+ ``config/services.yaml`` file.
-The ``config.yml`` file contains the options used by the application to modify
-its behavior, such as the sender of email notifications, or the enabled
-`feature toggles`_. Defining these values in ``parameters.yml`` file would
-add an extra layer of configuration that's not needed because you don't need
-or want these configuration values to change on each server.
+The ``services.yaml`` file contains the options used by the application to
+modify its behavior, such as the sender of email notifications, or the enabled
+`feature toggles`_. Defining these values in ``.env`` file would add an extra
+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 ``config.yml`` file usually vary from
-one :doc:`environment ` to another. That's
-why Symfony already includes ``app/config/config_dev.yml`` and ``app/config/config_prod.yml``
+The configuration options defined in the ``services.yaml`` may vary from one
+:doc:`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.
Constants vs Configuration Options
@@ -99,7 +105,7 @@ used to control the number of posts to display on the blog homepage:
.. code-block:: yaml
- # app/config/config.yml
+ # config/services.yaml
parameters:
homepage.number_of_items: 10
@@ -109,8 +115,8 @@ option for a value that you are never going to configure just isn't necessary.
Our recommendation is to define these values as constants in your application.
You could, for example, define a ``NUMBER_OF_ITEMS`` constant in the ``Post`` entity::
- // src/AppBundle/Entity/Post.php
- namespace AppBundle\Entity;
+ // src/Entity/Post.php
+ namespace App\Entity;
class Post
{
@@ -135,10 +141,10 @@ Constants can be used for example in your Twig templates thanks to the
And Doctrine entities and repositories can now easily access these values,
whereas they cannot access the container parameters::
- namespace AppBundle\Repository;
+ namespace App\Repository;
+ use App\Entity\Post;
use Doctrine\ORM\EntityRepository;
- use AppBundle\Entity\Post;
class PostRepository extends EntityRepository
{
@@ -165,7 +171,7 @@ just one or two words to describe the purpose of the parameter:
.. code-block:: yaml
- # app/config/config.yml
+ # config/services.yaml
parameters:
# don't do this: 'dir' is too generic and it doesn't convey any meaning
app.dir: '...'
@@ -176,44 +182,6 @@ just one or two words to describe the purpose of the parameter:
app.dir.contents: '...'
app.contents-dir: '...'
-Semantic Configuration: Don't Do It
------------------------------------
-
-.. best-practice::
-
- Don't define a semantic dependency injection configuration for your bundles.
-
-As explained in :doc:`/bundles/extension` article, Symfony bundles
-have two choices on how to handle configuration: normal service configuration
-through the ``services.yml`` file and semantic configuration through a special
-``*Extension`` class.
-
-Although semantic configuration is much more powerful and provides nice features
-such as configuration validation, the amount of work needed to define that
-configuration isn't worth it for bundles that aren't meant to be shared as
-third-party bundles.
-
-Moving Sensitive Options Outside of Symfony Entirely
-----------------------------------------------------
-
-When dealing with sensitive options, like database credentials, we also recommend
-that you store them outside the Symfony project and make them available
-through environment variables:
-
-.. code-block:: yaml
-
- # app/config/config.yml
- doctrine:
- dbal:
- # ...
- password: "%env(DB_PASSWORD)%"
-
-.. versionadded:: 3.2
-
- Support for runtime environment variables via the ``%env(...)%`` syntax
- was added in Symfony 3.2. Prior to version 3.2, you needed to use the
- :doc:`special SYMFONY__ variables `.
-
----
Next: :doc:`/best_practices/business-logic`
diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst
index 22605898357..0ef6fd1d3bc 100644
--- a/best_practices/controllers.rst
+++ b/best_practices/controllers.rst
@@ -5,16 +5,15 @@ Symfony follows the philosophy of *"thin controllers and fat models"*. This
means that controllers should hold just the thin layer of *glue-code*
needed to coordinate the different parts of the application.
-As a rule of thumb, you should follow the 5-10-20 rule, where controllers should
-only define 5 variables or less, contain 10 actions or less and include 20 lines
-of code or less in each action. This isn't an exact science, but it should
-help you realize when code should be refactored out of the controller and
-into a service.
+Your controller methods should just call to other services, trigger some events
+if needed and then return a response, but they should not contain any actual
+business logic. If they do, refactor it out of the controller and into a service.
.. best-practice::
- Make your controller extend the FrameworkBundle base controller and use
- annotations to configure routing, caching and security whenever possible.
+ Make your controller extend the ``AbstractController`` base controller
+ provided by Symfony and use annotations to configure routing, caching and
+ security whenever possible.
Coupling the controllers to the underlying framework allows you to leverage
all of its features and increases your productivity.
@@ -33,6 +32,18 @@ Overall, this means you should aggressively decouple your business logic
from the framework while, at the same time, aggressively coupling your controllers
and routing *to* the framework in order to get the most out of it.
+Controller Action Naming
+------------------------
+
+.. best-practice::
+
+ Don't add the ``Action`` suffix to the methods of the controller actions.
+
+The first Symfony versions required that controller method names ended in
+``Action`` (e.g. ``newAction()``, ``showAction()``). This suffix became optional
+when annotations were introduced for controllers. In modern Symfony applications
+this suffix is neither required nor recommended, so you can safely remove it.
+
Routing Configuration
---------------------
@@ -41,32 +52,30 @@ configuration to the main routing configuration file:
.. code-block:: yaml
- # app/config/routing.yml
- app:
- resource: '@AppBundle/Controller/'
+ # config/routes.yaml
+ controllers:
+ resource: '../src/Controller/'
type: annotation
This configuration will load annotations from any controller stored inside the
-``src/AppBundle/Controller/`` directory and even from its subdirectories.
-So if your application defines lots of controllers, it's perfectly ok to
-reorganize them into subdirectories:
+``src/Controller/`` directory and even from its subdirectories. So if your application
+defines lots of controllers, it's perfectly ok to reorganize them into subdirectories:
.. code-block:: text
/
├─ ...
└─ src/
- └─ AppBundle/
+ ├─ ...
+ └─ Controller/
+ ├─ DefaultController.php
├─ ...
- └─ Controller/
- ├─ DefaultController.php
+ ├─ Api/
+ │ ├─ ...
+ │ └─ ...
+ └─ Backend/
├─ ...
- ├─ Api/
- │ ├─ ...
- │ └─ ...
- └─ Backend/
- ├─ ...
- └─ ...
+ └─ ...
Template Configuration
----------------------
@@ -91,18 +100,18 @@ What does the Controller look like
Considering all this, here is an example of what the controller should look like
for the homepage of our app::
- namespace AppBundle\Controller;
+ namespace App\Controller;
- use AppBundle\Entity\Post;
- use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use App\Entity\Post;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
- class DefaultController extends Controller
+ class DefaultController extends AbstractController
{
/**
* @Route("/", name="homepage")
*/
- public function indexAction()
+ public function index()
{
$posts = $this->getDoctrine()
->getRepository(Post::class)
@@ -117,9 +126,9 @@ for the homepage of our app::
Fetching Services
-----------------
-If you extend the base ``Controller`` class, you can access services directly from
-the container via ``$this->container->get()`` or ``$this->get()``. But instead, you
-should use dependency injection to fetch services by
+If you extend the base ``AbstractController`` class, you can't access services
+directly from the container via ``$this->container->get()`` or ``$this->get()``.
+Instead, you must use dependency injection to fetch services by
:ref:`type-hinting action method arguments `:
.. best-practice::
@@ -145,40 +154,41 @@ to automatically query for an entity and pass it as an argument to your controll
For example::
- use AppBundle\Entity\Post;
+ use App\Entity\Post;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/{id}", name="admin_post_show")
*/
- public function showAction(Post $post)
+ public function show(Post $post)
{
$deleteForm = $this->createDeleteForm($post);
return $this->render('admin/post/show.html.twig', [
- 'post' => $post,
+ 'post' => $post,
'delete_form' => $deleteForm->createView(),
]);
}
-Normally, you'd expect a ``$id`` argument to ``showAction()``. Instead, by
-creating a new argument (``$post``) and type-hinting it with the ``Post``
-class (which is a Doctrine entity), the ParamConverter automatically queries
-for an object whose ``$id`` property matches the ``{id}`` value. It will
-also show a 404 page if no ``Post`` can be found.
+Normally, you'd expect a ``$id`` argument to ``show()``. Instead, by creating a
+new argument (``$post``) and type-hinting it with the ``Post`` class (which is a
+Doctrine entity), the ParamConverter automatically queries for an object whose
+``$id`` property matches the ``{id}`` value. It will also show a 404 page if no
+``Post`` can be found.
When Things Get More Advanced
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The above example works without any configuration because the wildcard name ``{id}`` matches
-the name of the property on the entity. If this isn't true, or if you have
-even more complex logic, the easiest thing to do is just query for the entity
-manually. In our application, we have this situation in ``CommentController``::
+The above example works without any configuration because the wildcard name
+``{id}`` matches the name of the property on the entity. If this isn't true, or
+if you have even more complex logic, your best choice is to query for
+the entity manually. In our application, we have this situation in
+``CommentController``::
/**
* @Route("/comment/{postSlug}/new", name="comment_new")
*/
- public function newAction(Request $request, $postSlug)
+ public function new(Request $request, $postSlug)
{
$post = $this->getDoctrine()
->getRepository(Post::class)
@@ -194,7 +204,7 @@ manually. In our application, we have this situation in ``CommentController``::
You can also use the ``@ParamConverter`` configuration, which is infinitely
flexible::
- use AppBundle\Entity\Post;
+ use App\Entity\Post;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
@@ -203,7 +213,7 @@ flexible::
* @Route("/comment/{postSlug}/new", name="comment_new")
* @ParamConverter("post", options={"mapping"={"postSlug"="slug"}})
*/
- public function newAction(Request $request, Post $post)
+ public function new(Request $request, Post $post)
{
// ...
}
diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst
index 6c860552abc..e323d6bcf7a 100644
--- a/best_practices/creating-the-project.rst
+++ b/best_practices/creating-the-project.rst
@@ -4,16 +4,23 @@ Creating the Project
Installing Symfony
------------------
-In the past, Symfony projects were created with `Composer`_, the dependency manager
-for PHP applications. However, the current recommendation is to use the **Symfony
-Installer**, which has to be installed before creating your first project.
+.. best-practice::
+
+ Use Composer and Symfony Flex to create and manage Symfony applications.
+
+`Composer`_ is the package manager used by modern PHP applications to manage
+their dependencies. `Symfony Flex`_ is a Composer plugin designed to automate
+some of the most common tasks performed in Symfony applications. Using Flex is
+optional but recommended because it improves your productivity significantly.
.. best-practice::
- Use the Symfony Installer to create new Symfony-based projects.
+ Use the Symfony Skeleton to create new Symfony-based projects.
-Read the :doc:`/setup` article learn how to install and use the Symfony
-Installer.
+The `Symfony Skeleton`_ is a minimal and empty Symfony project which you can
+base your new projects on. Unlike past Symfony versions, this skeleton installs
+the absolute bare minimum amount of dependencies to make a fully working Symfony
+project. Read the :doc:`/setup` article to learn more about installing Symfony.
.. _linux-and-mac-os-x-systems:
.. _windows-systems:
@@ -21,87 +28,55 @@ Installer.
Creating the Blog Application
-----------------------------
-Now that everything is correctly set up, you can create a new project based on
-Symfony. In your command console, browse to a directory where you have permission
-to create files and execute the following commands:
+In your command console, browse to a directory where you have permission to
+create files and execute the following commands:
.. code-block:: terminal
$ cd projects/
- $ symfony new blog
-
- # Windows
- c:\> cd projects/
- c:\projects\> php symfony new blog
-
-.. note::
-
- If the installer doesn't work for you or doesn't output anything, make sure
- that the `Phar extension`_ is installed and enabled on your computer.
+ $ composer create-project symfony/skeleton blog
This command creates a new directory called ``blog`` that contains a fresh new
-project based on the most recent stable Symfony version available. In addition,
-the installer checks if your system meets the technical requirements to execute
-Symfony applications. If not, you'll see the list of changes needed to meet those
-requirements.
+project based on the most recent stable Symfony version available.
.. tip::
- Symfony releases are digitally signed for security reasons. If you want to
- verify the integrity of your Symfony installation, take a look at the
- `public checksums repository`_ and follow `these steps`_ to verify the
- signatures.
+ The technical requirements to run Symfony are simple. If you want to check
+ if your system meets those requirements, read :doc:`/reference/requirements`.
Structuring the Application
---------------------------
After creating the application, enter the ``blog/`` directory and you'll see a
-number of files and directories generated automatically:
+number of files and directories generated automatically. These are the most
+important ones:
.. code-block:: text
blog/
- ├─ app/
- │ ├─ config/
- │ └─ Resources/
- ├─ bin
+ ├─ bin/
│ └─ console
+ ├─ config/
+ └─ public/
+ │ └─ index.php
├─ src/
- │ └─ AppBundle/
+ │ └─ Kernel.php
├─ var/
│ ├─ cache/
- │ ├─ logs/
- │ └─ sessions/
- ├─ tests/
- │ └─ AppBundle/
- ├─ vendor/
- └─ web/
+ │ └─ log/
+ └─ vendor/
This file and directory hierarchy is the convention proposed by Symfony to
-structure your applications. The recommended purpose of each directory is the
-following:
-
-* ``app/config/``, stores all the configuration defined for any environment;
-* ``app/Resources/``, stores all the templates and the translation files for the
- application;
-* ``src/AppBundle/``, stores the Symfony specific code (controllers and routes),
- your domain code (e.g. Doctrine classes) and all your business logic;
-* ``var/cache/``, stores all the cache files generated by the application;
-* ``var/logs/``, stores all the log files generated by the application;
-* ``var/sessions/``, stores all the session files generated by the application;
-* ``tests/AppBundle/``, stores the automatic tests (e.g. Unit tests) of the
- application.
-* ``vendor/``, this is the directory where Composer installs the application's
- dependencies and you should never modify any of its contents;
-* ``web/``, stores all the front controller files and all the web assets, such
- as stylesheets, JavaScript files and images.
+structure your applications. It's recommended to keep this structure because it's
+easy to navigate and most directory names are self-explanatory, but you can
+:doc:`override the location of any Symfony directory `:
Application Bundles
~~~~~~~~~~~~~~~~~~~
When Symfony 2.0 was released, most developers naturally adopted the symfony
1.x way of dividing applications into logical modules. That's why many Symfony
-applications use bundles to divide their code into logical features: UserBundle,
+applications used bundles to divide their code into logical features: UserBundle,
ProductBundle, InvoiceBundle, etc.
But a bundle is *meant* to be something that can be reused as a stand-alone
@@ -111,68 +86,16 @@ depends on ProductBundle, then there's no advantage to having two separate bundl
.. best-practice::
- Create only one bundle called AppBundle for your application logic.
-
-Implementing a single AppBundle bundle in your projects will make your code
-more concise and easier to understand.
-
-.. note::
-
- There is no need to prefix the AppBundle with your own vendor (e.g.
- AcmeAppBundle), because this application bundle is never going to be
- shared.
-
-.. note::
-
- Another reason to create a new bundle is when you're overriding something
- in a vendor's bundle (e.g. a controller). See :doc:`/bundles/inheritance`.
-
-All in all, this is the typical directory structure of a Symfony application
-that follows these best practices:
-
-.. code-block:: text
-
- blog/
- ├─ app/
- │ ├─ config/
- │ └─ Resources/
- ├─ bin/
- │ └─ console
- ├─ src/
- │ └─ AppBundle/
- ├─ tests/
- │ └─ AppBundle/
- ├─ var/
- │ ├─ cache/
- │ ├─ logs/
- └─ sessions/
- ├─ vendor/
- └─ web/
- ├─ app.php
- └─ app_dev.php
-
-.. tip::
-
- If your Symfony installation doesn't come with a pre-generated AppBundle,
- you can generate it by hand executing this command:
-
- .. code-block:: terminal
-
- $ php bin/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interaction
-
-Extending the Directory Structure
----------------------------------
+ Don't create any bundle to organize your application logic.
-If your project or infrastructure requires some changes to the default directory
-structure of Symfony, you can
-:doc:`override the location of the main directories `:
-``cache/``, ``logs/`` and ``web/``.
+Symfony applications can still use third-party bundles (installed in ``vendor/``)
+to add features, but you should use PHP namespaces instead of bundles to organize
+your own code.
----
Next: :doc:`/best_practices/configuration`
.. _`Composer`: https://getcomposer.org/
-.. _`Phar extension`: https://php.net/manual/en/intro.phar.php
-.. _`public checksums repository`: https://github.com/sensiolabs/checksums
-.. _`these steps`: http://fabien.potencier.org/signing-project-releases.html
+.. _`Symfony Flex`: https://github.com/symfony/flex
+.. _`Symfony Skeleton`: https://github.com/symfony/skeleton
diff --git a/best_practices/forms.rst b/best_practices/forms.rst
index 4bfd3bf1221..79a95da0be1 100644
--- a/best_practices/forms.rst
+++ b/best_practices/forms.rst
@@ -12,14 +12,14 @@ Building Forms
Define your forms as PHP classes.
-The Form component allows you to build forms right inside your controller
-code. This is perfectly fine if you don't need to reuse the form somewhere else.
-But for organization and reuse, we recommend that you define each
-form in its own PHP class::
+The Form component allows you to build forms right inside your controller code.
+This is perfectly fine if you don't need to reuse the form somewhere else. But
+for organization and reuse, we recommend that you define each form in its own
+PHP class::
- namespace AppBundle\Form;
+ namespace App\Form;
- use AppBundle\Entity\Post;
+ use App\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -50,16 +50,16 @@ form in its own PHP class::
.. best-practice::
- Put the form type classes in the ``AppBundle\Form`` namespace, unless you
+ Put the form type classes in the ``App\Form`` namespace, unless you
use other custom form classes like data transformers.
To use the class, use ``createForm()`` and pass the fully qualified class name::
// ...
- use AppBundle\Form\PostType;
+ use App\Form\PostType;
// ...
- public function newAction(Request $request)
+ public function new(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
@@ -67,14 +67,6 @@ To use the class, use ``createForm()`` and pass the fully qualified class name::
// ...
}
-Registering Forms as Services
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can also :ref:`register your form type as a service `.
-This is only needed if your form type requires some dependencies to be injected
-by the container, otherwise it is unnecessary overhead and therefore *not*
-recommended to do this for all form type classes.
-
Form Button Configuration
-------------------------
@@ -107,25 +99,25 @@ This form *may* have been designed for creating posts, but if you wanted
to reuse it for editing posts, the button label would be wrong. Instead,
some developers configure form buttons in the controller::
- namespace AppBundle\Controller\Admin;
+ namespace App\Controller\Admin;
+ use App\Entity\Post;
+ use App\Form\PostType;
use Symfony\Component\HttpFoundation\Request;
- use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
- use AppBundle\Entity\Post;
- use AppBundle\Form\PostType;
- class PostController extends Controller
+ class PostController extends AbstractController
{
// ...
- public function newAction(Request $request)
+ public function new(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->add('submit', SubmitType::class, [
'label' => 'Create',
- 'attr' => ['class' => 'btn btn-default pull-right'],
+ 'attr' => ['class' => 'btn btn-default pull-right'],
]);
// ...
@@ -142,8 +134,7 @@ view layer:
{{ form_start(form) }}
{{ form_widget(form) }}
-
+
{{ form_end(form) }}
Validation
@@ -188,7 +179,7 @@ all of the fields:
.. code-block:: html+twig
- {{ form_start(form, {'attr': {'class': 'my-form-class'} }) }}
+ {{ form_start(form, {attr: {class: 'my-form-class'} }) }}
{{ form_widget(form) }}
{{ form_end(form) }}
@@ -202,7 +193,7 @@ Handling Form Submits
Handling a form submit usually follows a similar template::
- public function newAction(Request $request)
+ public function new(Request $request)
{
// build the form ...
@@ -213,26 +204,18 @@ Handling a form submit usually follows a similar template::
$entityManager->persist($post);
$entityManager->flush();
- return $this->redirect($this->generateUrl(
- 'admin_post_show',
- ['id' => $post->getId()]
- ));
+ return $this->redirectToRoute('admin_post_show', [
+ 'id' => $post->getId()
+ ]);
}
// render the template
}
-There are really only two notable things here. First, we recommend that you
-use a single action for both rendering the form and handling the form submit.
-For example, you *could* have a ``newAction()`` that *only* renders the form
-and a ``createAction()`` that *only* processes the form submit. Both those
-actions will be almost identical. So it's much simpler to let ``newAction()``
-handle everything.
-
-Second, is it required to call ``$form->isSubmitted()`` in the ``if`` statement
-before calling ``isValid()``. Calling ``isValid()`` with an unsubmitted form
-is deprecated since version 3.2 and will throw an exception in 4.0.
-
-----
+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
+submit. Both those actions will be almost identical. So it's much simpler to let
+``new()`` handle everything.
Next: :doc:`/best_practices/i18n`
diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst
index 06bec2b8cbb..ca4dd0c4c4b 100644
--- a/best_practices/i18n.rst
+++ b/best_practices/i18n.rst
@@ -3,20 +3,18 @@ Internationalization
Internationalization and localization adapt the applications and their contents
to the specific region or language of the users. In Symfony this is an opt-in
-feature that needs to be enabled before using it. To do this, uncomment the
-following ``translator`` configuration option and set your application locale:
+feature that needs to be installed before using it (``composer require symfony/translation``).
-.. code-block:: yaml
+Translation Source File Location
+--------------------------------
+
+.. best-practice::
- # app/config/config.yml
- framework:
- # ...
- translator: { fallbacks: ['%locale%'] }
+ Store the translation files in the ``translations/`` directory at the root
+ of your project.
- # app/config/parameters.yml
- parameters:
- # ...
- locale: en
+Your translators' lives will be much easier if all the application translations
+are in one central location.
Translation Source File Format
------------------------------
@@ -41,21 +39,6 @@ XLIFF notes allow you to define that context.
The `PHP Translation Bundle`_ includes advanced extractors that can read
your project and automatically update the XLIFF files.
-Translation Source File Location
---------------------------------
-
-.. best-practice::
-
- Store the translation files in the ``app/Resources/translations/``
- directory.
-
-Traditionally, Symfony developers have created these files in the
-``Resources/translations/`` directory of each bundle. But since the
-``app/Resources/`` directory is considered the global location for the
-application's resources, storing translations in ``app/Resources/translations/``
-centralizes them *and* gives them priority over any other translation file.
-This let's you override translations defined in third-party bundles.
-
Translation Keys
----------------
@@ -63,8 +46,8 @@ Translation Keys
Always use keys for translations instead of content strings.
-Using keys simplifies the management of the translation files because you
-can change the original contents without having to update all of the translation
+Using keys simplifies the management of the translation files because you can
+change the original contents without having to update all of the translation
files.
Keys should always describe their *purpose* and *not* their location. For
@@ -79,7 +62,7 @@ English in the application would be:
.. code-block:: xml
-
+
diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst
index 1826cfad3b5..c0a2b0cb44d 100644
--- a/best_practices/introduction.rst
+++ b/best_practices/introduction.rst
@@ -49,8 +49,8 @@ quality. It's also a moving target that will continue to improve.
Keep in mind that these are **optional recommendations** that you and your
team may or may not follow to develop Symfony applications. If you want to
-continue using your own best practices and methodologies, you can of course
-do it. Symfony is flexible enough to adapt to your needs. That will never
+continue using your own best practices and methodologies, you can still do
+that. Symfony is flexible enough to adapt to your needs. That will never
change.
Who this Book Is for (Hint: It's not a Tutorial)
@@ -58,8 +58,8 @@ Who this Book Is for (Hint: It's not a Tutorial)
Any Symfony developer, whether you are an expert or a newcomer, can read this
guide. But since this isn't a tutorial, you'll need some basic knowledge of
-Symfony to follow everything. If you are totally new to Symfony, welcome!
-Start with :doc:`The Quick Tour ` tutorial first.
+Symfony to follow everything. If you are totally new to Symfony, welcome! and
+read the :doc:`Getting Started guides ` first.
We've deliberately kept this guide short. We won't repeat explanations that
you can find in the vast Symfony documentation, like discussions about Dependency
@@ -69,14 +69,13 @@ what you already know.
The Application
---------------
-In addition to this guide, a sample application has been developed with all these
-best practices in mind. This project, called the Symfony Demo application, can
-be obtained through the Symfony Installer. First, `download and install`_ the
-installer and then execute this command to download the demo application:
+In addition to this guide, a sample application called `Symfony Demo`_ has been
+developed with all these best practices in mind. Execute this command to download
+the demo application:
.. code-block:: terminal
- $ symfony demo
+ $ composer create-project symfony/symfony-demo
**The demo application is a simple blog engine**, because that will allow us to
focus on the Symfony concepts and features without getting buried in difficult
@@ -87,9 +86,10 @@ Don't Update Your Existing Applications
---------------------------------------
After reading this handbook, some of you may be considering refactoring your
-existing Symfony applications. Our recommendation is sound and clear: **you
-should not refactor your existing applications to comply with these best
-practices**. The reasons for not doing it are various:
+existing Symfony applications. Our recommendation is sound and clear: you may
+use these best practices for **new applications** but **you should not refactor
+your existing applications to comply with these best practices**. The reasons
+for not doing it are various:
* Your existing applications are not wrong, they just follow another set of
guidelines;
@@ -103,4 +103,4 @@ practices**. The reasons for not doing it are various:
Next: :doc:`/best_practices/creating-the-project`
.. _`Fabien Potencier`: https://connect.symfony.com/profile/fabpot
-.. _`download and install`: https://symfony.com/download
+.. _`Symfony Demo`: https://github.com/symfony/demo
diff --git a/best_practices/security.rst b/best_practices/security.rst
index 7eb094ef791..f746303d347 100644
--- a/best_practices/security.rst
+++ b/best_practices/security.rst
@@ -6,10 +6,9 @@ Authentication and Firewalls (i.e. Getting the User's Credentials)
You can configure Symfony to authenticate your users using any method you
want and to load user information from any source. This is a complex topic, but
-the :doc:`Security guide` has a lot of information about
-this.
+the :doc:`Security guide ` has a lot of information about this.
-Regardless of your needs, authentication is configured in ``security.yml``,
+Regardless of your needs, authentication is configured in ``security.yaml``,
primarily under the ``firewalls`` key.
.. best-practice::
@@ -30,9 +29,9 @@ site (or maybe nearly *all* sections), use the ``access_control`` area.
.. best-practice::
- Use the ``bcrypt`` encoder for encoding your users' passwords.
+ Use the ``bcrypt`` encoder for hashing your users' passwords.
-If your users have a password, then we recommend encoding it using the ``bcrypt``
+If your users have a password, then we recommend hashing it using the ``bcrypt``
encoder, instead of the traditional SHA-512 hashing encoder. The main advantages
of ``bcrypt`` are the inclusion of a *salt* value to protect against rainbow
table attacks, and its adaptive nature, which allows to make it slower to
@@ -50,14 +49,14 @@ which uses a login form to load users from the database:
.. code-block:: yaml
- # app/config/security.yml
+ # config/packages/security.yaml
security:
encoders:
- AppBundle\Entity\User: bcrypt
+ App\Entity\User: bcrypt
providers:
database_users:
- entity: { class: AppBundle:User, property: username }
+ entity: { class: App\Entity\User, property: username }
firewalls:
secured_area:
@@ -81,7 +80,7 @@ Authorization (i.e. Denying Access)
-----------------------------------
Symfony gives you several ways to enforce authorization, including the ``access_control``
-configuration in :doc:`security.yml `, the
+configuration in :doc:`security.yaml `, the
:ref:`@Security annotation ` and using
:ref:`isGranted ` on the ``security.authorization_checker``
service directly.
@@ -90,17 +89,15 @@ service directly.
* For protecting broad URL patterns, use ``access_control``;
* Whenever possible, use the ``@Security`` annotation;
- * Check security directly on the ``security.authorization_checker`` service whenever
- you have a more complex situation.
+ * Check security directly on the ``security.authorization_checker`` service
+ whenever you have a more complex situation.
There are also different ways to centralize your authorization logic, like
-with a custom security voter or with ACL.
+with a custom security voter:
.. best-practice::
- * For fine-grained restrictions, define a custom security voter;
- * For restricting access to *any* object by *any* user via an admin
- interface, use the Symfony ACL.
+ Define a custom security voter to implement fine-grained restrictions.
.. _best-practices-security-annotation:
@@ -121,9 +118,9 @@ Using ``@Security``, this looks like::
* Displays a form to create a new Post entity.
*
* @Route("/new", name="admin_post_new")
- * @Security("has_role('ROLE_ADMIN')")
+ * @Security("is_granted('ROLE_ADMIN')")
*/
- public function newAction()
+ public function new()
{
// ...
}
@@ -136,7 +133,7 @@ inside ``@Security``. In the following example, a user can only access the
controller if their email matches the value returned by the ``getAuthorEmail()``
method on the ``Post`` object::
- use AppBundle\Entity\Post;
+ use App\Entity\Post;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Routing\Annotation\Route;
@@ -144,7 +141,7 @@ method on the ``Post`` object::
* @Route("/{id}/edit", name="admin_post_edit")
* @Security("user.getEmail() == post.getAuthorEmail()")
*/
- public function editAction(Post $post)
+ public function edit(Post $post)
{
// ...
}
@@ -167,7 +164,7 @@ need to repeat the expression code using Twig syntax:
A good solution - if your logic is simple enough - can be to add a new method
to the ``Post`` entity that checks if a given user is its author::
- // src/AppBundle/Entity/Post.php
+ // src/Entity/Post.php
// ...
class Post
@@ -187,7 +184,7 @@ to the ``Post`` entity that checks if a given user is its author::
Now you can reuse this method both in the template and in the security expression::
- use AppBundle\Entity\Post;
+ use App\Entity\Post;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Routing\Annotation\Route;
@@ -195,7 +192,7 @@ Now you can reuse this method both in the template and in the security expressio
* @Route("/{id}/edit", name="admin_post_edit")
* @Security("post.isAuthor(user)")
*/
- public function editAction(Post $post)
+ public function edit(Post $post)
{
// ...
}
@@ -221,7 +218,7 @@ more advanced use-case, you can always do the same security check in PHP::
/**
* @Route("/{id}/edit", name="admin_post_edit")
*/
- public function editAction($id)
+ public function edit($id)
{
$post = $this->getDoctrine()
->getRepository(Post::class)
@@ -237,42 +234,47 @@ more advanced use-case, you can always do the same security check in PHP::
// equivalent code without using the "denyAccessUnlessGranted()" shortcut:
//
// use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+ // use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
+ //
+ // ...
+ //
+ // public function __construct(AuthorizationCheckerInterface $authorizationChecker) {
+ // $this->authorizationChecker = $authorizationChecker;
+ // }
+ //
// ...
//
- // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
+ // if (!$this->authorizationChecker->isGranted('edit', $post)) {
// throw $this->createAccessDeniedException();
// }
-
+ //
// ...
}
Security Voters
---------------
-If your security logic is complex and can't be centralized into a method
-like ``isAuthor()``, you should leverage custom voters. These are an order
-of magnitude easier than :doc:`ACLs ` and will give
-you the flexibility you need in almost all cases.
+If your security logic is complex and can't be centralized into a method like
+``isAuthor()``, you should leverage custom voters. These are much easier than
+:doc:`ACLs ` and will give you the flexibility you need in almost
+all cases.
First, create a voter class. The following example shows a voter that implements
the same ``getAuthorEmail()`` logic you used above::
- namespace AppBundle\Security;
+ namespace App\Security;
+ use App\Entity\Post;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
- use AppBundle\Entity\Post;
class PostVoter extends Voter
{
const CREATE = 'create';
const EDIT = 'edit';
- /**
- * @var AccessDecisionManagerInterface
- */
private $decisionManager;
public function __construct(AccessDecisionManagerInterface $decisionManager)
@@ -304,15 +306,16 @@ the same ``getAuthorEmail()`` logic you used above::
}
switch ($attribute) {
+ // if the user is an admin, allow them to create new posts
case self::CREATE:
- // if the user is an admin, allow them to create new posts
if ($this->decisionManager->decide($token, ['ROLE_ADMIN'])) {
return true;
}
break;
+
+ // if the user is the author of the post, allow them to edit the posts
case self::EDIT:
- // if the user is the author of the post, allow them to edit the posts
if ($user->getEmail() === $post->getAuthorEmail()) {
return true;
}
@@ -324,7 +327,7 @@ the same ``getAuthorEmail()`` logic you used above::
}
}
-If you're using the :ref:`default services.yml configuration `,
+If you're using the :ref:`default services.yaml configuration `,
your application will :ref:`autoconfigure ` your security
voter and inject an ``AccessDecisionManagerInterface`` instance into it thanks to
:doc:`autowiring `.
@@ -335,7 +338,7 @@ Now, you can use the voter with the ``@Security`` annotation::
* @Route("/{id}/edit", name="admin_post_edit")
* @Security("is_granted('edit', post)")
*/
- public function editAction(Post $post)
+ public function edit(Post $post)
{
// ...
}
@@ -346,42 +349,30 @@ via the even easier shortcut in a controller::
/**
* @Route("/{id}/edit", name="admin_post_edit")
*/
- public function editAction($id)
+ public function edit($id)
{
$post = ...; // query for the post
$this->denyAccessUnlessGranted('edit', $post);
- // or without the shortcut:
- //
// use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+ // use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
+ //
// ...
//
- // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
+ // public function __construct(AuthorizationCheckerInterface $authorizationChecker) {
+ // $this->authorizationChecker = $authorizationChecker;
+ // }
+ //
+ // ...
+ //
+ // if (!$this->authorizationChecker->isGranted('edit', $post)) {
// throw $this->createAccessDeniedException();
// }
+ //
+ // ...
}
-Learn More
-----------
-
-The `FOSUserBundle`_, developed by the Symfony community, adds support for a
-database-backed user system in Symfony. It also handles common tasks like
-user registration and forgotten password functionality.
-
-Enable the :doc:`Remember Me feature ` to
-allow your users to stay logged in for a long period of time.
-
-When providing customer support, sometimes it's necessary to access the application
-as some *other* user so that you can reproduce the problem. Symfony provides
-the ability to :doc:`impersonate users `.
-
-If your company uses a user login method not supported by Symfony, you can
-develop :doc:`your own user provider ` and
-:doc:`your own authentication provider `.
-
-----
-
Next: :doc:`/best_practices/web-assets`
.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
diff --git a/best_practices/templates.rst b/best_practices/templates.rst
index 4bfe1f05899..e9970b9a8d7 100644
--- a/best_practices/templates.rst
+++ b/best_practices/templates.rst
@@ -9,7 +9,7 @@ languages - like `Twig`_ - were created to make templating even better.
Use Twig templating format for your templates.
-Generally speaking, PHP templates are much more verbose than Twig templates because
+Generally speaking, PHP templates are more verbose than Twig templates because
they lack native support for lots of modern features needed by templates,
like inheritance, automatic escaping and named arguments for filters and
functions.
@@ -18,41 +18,27 @@ Twig is the default templating format in Symfony and has the largest community
support of all non-PHP template engines (it's used in high profile projects
such as Drupal 8).
-In addition, Twig is the only template format with guaranteed support in Symfony
-3.0. As a matter of fact, PHP may be removed from the officially supported
-template engines.
-
Template Locations
------------------
.. best-practice::
- Store all your application's templates in ``app/Resources/views/`` directory.
-
-Traditionally, Symfony developers stored the application templates in the
-``Resources/views/`` directory of each bundle. Then they used the Twig namespaced
-path to refer to them (e.g. ``@AcmeDemo/Default/index.html.twig``).
-
-But for the templates used in your application, it's much more convenient
-to store them in the ``app/Resources/views/`` directory. For starters, this
-drastically simplifies their logical names:
-
-============================================ ==================================
-Templates Stored inside Bundles Templates Stored in ``app/``
-============================================ ==================================
-``@AcmeDemo/index.html.twig`` ``index.html.twig``
-``@AcmeDemo/Default/index.html.twig`` ``default/index.html.twig``
-``@AcmeDemo/Default/subdir/index.html.twig`` ``default/subdir/index.html.twig``
-============================================ ==================================
+ Store the application templates in the ``templates/`` directory at the root
+ of your project.
-Another advantage is that centralizing your templates simplifies the work
-of your designers. They don't need to look for templates in lots of directories
-scattered through lots of bundles.
+Centralizing your templates in a single location simplifies the work of your
+designers. In addition, using this directory simplifies the notation used when
+referring to templates (e.g. ``$this->render('admin/post/show.html.twig')``
+instead of ``$this->render('@SomeTwigNamespace/Admin/Posts/show.html.twig')``).
.. best-practice::
Use lowercased snake_case for directory and template names.
+This recommendation aligns with Twig best practices, where variables and template
+names use lowercased snake_case too (e.g. ``user_profile`` instead of ``userProfile``
+and ``edit_form.html.twig`` instead of ``EditForm.html.twig``).
+
.. best-practice::
Use a prefixed underscore for partial templates in template names.
@@ -67,47 +53,33 @@ Twig Extensions
.. best-practice::
- Define your Twig extensions in the ``AppBundle/Twig/`` directory. Your
+ Define your Twig extensions in the ``src/Twig/`` directory. Your
application will automatically detect them and configure them.
Our application needs a custom ``md2html`` Twig filter so that we can transform
-the Markdown contents of each post into HTML.
-
-To do this, first, install the excellent `Parsedown`_ Markdown parser as
-a new dependency of the project:
-
-.. code-block:: terminal
-
- $ composer require erusev/parsedown
+the Markdown contents of each post into HTML. To do this, create a new
+``Markdown`` class that will be used later by the Twig extension. It needs
+to define one single method to transform Markdown content into HTML::
-Then, create a new ``Markdown`` class that will be used later by the Twig
-extension. It just needs to define one single method to transform
-Markdown content into HTML::
-
- namespace AppBundle\Utils;
+ namespace App\Utils;
class Markdown
{
- private $parser;
-
- public function __construct()
- {
- $this->parser = new \Parsedown();
- }
+ // ...
- public function toHtml($text)
+ public function toHtml(string $text): string
{
return $this->parser->text($text);
}
}
-Next, create a new Twig extension and define a new filter called ``md2html``
-using the ``Twig\TwigFilter`` class. Inject the newly defined ``Markdown``
-class in the constructor of the Twig extension::
+Next, create a new Twig extension and define a filter called ``md2html`` using
+the ``Twig\TwigFilter`` class. Inject the newly defined ``Markdown`` class in the
+constructor of the Twig extension::
- namespace AppBundle\Twig;
+ namespace App\Twig;
- use AppBundle\Utils\Markdown;
+ use App\Utils\Markdown;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
@@ -123,11 +95,10 @@ class in the constructor of the Twig extension::
public function getFilters()
{
return [
- new TwigFilter(
- 'md2html',
- [$this, 'markdownToHtml'],
- ['is_safe' => ['html'], 'pre_escape' => 'html']
- ),
+ new TwigFilter('md2html', [$this, 'markdownToHtml'], [
+ 'is_safe' => ['html'],
+ 'pre_escape' => 'html',
+ ]),
];
}
@@ -135,16 +106,11 @@ class in the constructor of the Twig extension::
{
return $this->parser->toHtml($content);
}
-
- public function getName()
- {
- return 'app_extension';
- }
}
And that's it!
-If you're using the :ref:`default services.yml configuration `,
+If you're using the :ref:`default services.yaml configuration `,
you're done! Symfony will automatically know about your new service and tag it to
be used as a Twig extension.
diff --git a/best_practices/tests.rst b/best_practices/tests.rst
index 4a26ee7139c..2c1230cc452 100644
--- a/best_practices/tests.rst
+++ b/best_practices/tests.rst
@@ -1,10 +1,11 @@
Tests
=====
-Roughly speaking, there are two types of test. Unit testing allows you to
-test the input and output of specific functions. Functional testing allows
-you to command a "browser" where you browse to pages on your site, click
-links, fill out forms and assert that you see certain things on the page.
+Of all the different types of test available, these best practices focus solely
+on unit and functional tests. Unit testing allows you to test the input and
+output of specific functions. Functional testing allows you to command a
+"browser" where you browse to pages on your site, click links, fill out forms
+and assert that you see certain things on the page.
Unit Tests
----------
@@ -26,11 +27,11 @@ functional tests, you can quickly spot any big errors before you deploy them:
Define a functional test that at least checks if your application pages
are successfully loading.
-A functional test like this is simple to implement thanks to
-:ref:`PHPUnit data providers `::
+:ref:`PHPUnit data providers ` help you implement
+functional tests::
- // tests/AppBundle/ApplicationAvailabilityFunctionalTest.php
- namespace Tests\AppBundle;
+ // tests/ApplicationAvailabilityFunctionalTest.php
+ namespace App\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
@@ -49,14 +50,12 @@ A functional test like this is simple to implement thanks to
public function urlProvider()
{
- return [
- ['/'],
- ['/posts'],
- ['/post/fixture-post-1'],
- ['/blog/category/fixture-category'],
- ['/archives'],
- // ...
- ];
+ yield ['/'];
+ yield ['/posts'];
+ yield ['/post/fixture-post-1'];
+ yield ['/blog/category/fixture-category'];
+ yield ['/archives'];
+ // ...
}
}
@@ -83,10 +82,13 @@ generator service:
Consider the following functional test that uses the ``router`` service to
generate the URL of the tested page::
+ // ...
+ private $router; // consider that this holds the Symfony router service
+
public function testBlogArchives()
{
$client = self::createClient();
- $url = $client->getContainer()->get('router')->generate('blog_archives');
+ $url = $this->router->generate('blog_archives');
$client->request('GET', $url);
// ...
@@ -104,7 +106,7 @@ The built-in functional testing client is great, but it can't be used to
test any JavaScript behavior on your pages. If you need to test this, consider
using the `Mink`_ library from within PHPUnit.
-Of course, if you have a heavy JavaScript frontend, you should consider using
+Of course, if you have a heavy JavaScript front-end, you should consider using
pure JavaScript-based testing tools.
Learn More about Functional Tests
diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst
index c567bf36e47..271a1fa3eeb 100644
--- a/best_practices/web-assets.rst
+++ b/best_practices/web-assets.rst
@@ -2,101 +2,33 @@ Web Assets
==========
Web assets are things like CSS, JavaScript and image files that make the
-frontend of your site look and work great. Symfony developers have traditionally
-stored these assets in the ``Resources/public/`` directory of each bundle.
+frontend of your site look and work great.
.. best-practice::
- Store your assets in the ``web/`` directory.
+ Store your assets in the ``assets/`` directory at the root of your project.
-Scattering your web assets across tens of different bundles makes it more
-difficult to manage them. Your designers' lives will be much easier if all
-the application assets are in one location.
-
-Templates also benefit from centralizing your assets, because the links are
-much more concise:
-
-.. code-block:: html+twig
-
-
-
-
- {# ... #}
-
-
-
-
-.. note::
-
- Keep in mind that ``web/`` is a public directory and that anything stored
- here will be publicly accessible, including all the original asset files
- (e.g. Sass, LESS and CoffeeScript files).
-
-Using Assetic
--------------
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-These days, you probably can't create static CSS and JavaScript files and
-include them in your template without much effort. Instead, you'll probably
-want to combine and minify these to improve client-side performance. You may
-also want to use LESS or Sass (for example), which means you'll need some way
-to process these into CSS files.
-
-A lot of tools exist to solve these problems, including pure-frontend (non-PHP)
-tools like GruntJS.
+Your designers' and front-end developers' lives will be much easier if all the
+application assets are in one central location.
.. best-practice::
- Use Assetic to compile, combine and minimize web assets, unless you're
- comfortable with frontend tools like GruntJS.
-
-:doc:`Assetic ` is an asset manager capable
-of compiling assets developed with a lot of different frontend technologies
-like LESS, Sass and CoffeeScript. Combining all your assets with Assetic is a
-matter of wrapping all the assets with a single Twig tag:
-
-.. code-block:: html+twig
-
- {% stylesheets
- 'css/bootstrap.min.css'
- 'css/main.css'
- filter='cssrewrite' output='css/compiled/app.css' %}
-
- {% endstylesheets %}
-
- {# ... #}
-
- {% javascripts
- 'js/jquery.min.js'
- 'js/bootstrap.min.js'
- output='js/compiled/app.js' %}
-
- {% endjavascripts %}
-
-Frontend-Based Applications
----------------------------
-
-Recently, frontend technologies like AngularJS have become pretty popular
-for developing frontend web applications that talk to an API.
-
-If you are developing an application like this, you should use the tools
-that are recommended by the technology, such as Bower and GruntJS. You should
-develop your frontend application separately from your Symfony backend (even
-separating the repositories if you want).
+ Use `Webpack Encore`_ to compile, combine and minimize web assets.
-Learn More about Assetic
-------------------------
+`Webpack`_ is the leading JavaScript module bundler that compiles, transforms
+and packages assets for usage in a browser. Webpack Encore is a JavaScript
+library that gets rid of most of Webpack complexity without hiding any of its
+features or distorting its usage and philosophy.
-Assetic can also minimize CSS and JavaScript assets
-:doc:`using UglifyCSS/UglifyJS ` to speed up your
-websites. You can even :doc:`compress images `
-with Assetic to reduce their size before serving them to the user. Check out
-the `official Assetic documentation`_ to learn more about all the available
+Webpack Encore was designed to bridge the gap between Symfony applications and
+the JavaScript-based tools used in modern web applications. Check out the
+`official Webpack Encore documentation`_ to learn more about all the available
features.
----
Next: :doc:`/best_practices/tests`
-.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic
+.. _`Webpack Encore`: https://github.com/symfony/webpack-encore
+.. _`Webpack`: https://webpack.js.org/
+.. _`official Webpack Encore documentation`: https://symfony.com/doc/current/frontend.html
diff --git a/bundles.rst b/bundles.rst
index 43ccbb10111..2f0fb608c04 100644
--- a/bundles.rst
+++ b/bundles.rst
@@ -8,73 +8,43 @@ The Bundle System
.. caution::
- In Symfony versions prior to 3.4, it was recommended to organize your own
+ In Symfony versions prior to 4.0, it was recommended to organize your own
application code using bundles. This is no longer recommended and bundles
should only be used to share code and features between multiple applications.
-A bundle is similar to a plugin in other software, but even better. The key
-difference is that *everything* is a bundle in Symfony, including both the
-core framework functionality and the code written for your application.
-Bundles are first-class citizens in Symfony. This gives you the flexibility
-to use pre-built features packaged in `third-party bundles`_ or to distribute
-your own bundles. It makes it easy to pick and choose which features to enable
-in your application and to optimize them the way you want.
-
-.. note::
-
- While you'll learn the basics here, an entire article is devoted to the
- organization and best practices of :doc:`bundles `.
-
-A bundle is simply a structured set of files within a directory that implement
-a single feature. You might create a BlogBundle, a ForumBundle or
-a bundle for user management (many of these exist already as open source
-bundles). Each directory contains everything related to that feature, including
-PHP files, templates, stylesheets, JavaScript files, tests and anything else.
-Every aspect of a feature exists in a bundle and every feature lives in a
-bundle.
-
-Bundles used in your applications must be enabled by registering them in
-the ``registerBundles()`` method of the ``AppKernel`` class::
-
- // app/AppKernel.php
- public function registerBundles()
- {
- $bundles = [
- new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
- new Symfony\Bundle\SecurityBundle\SecurityBundle(),
- new Symfony\Bundle\TwigBundle\TwigBundle(),
- new Symfony\Bundle\MonologBundle\MonologBundle(),
- new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
- new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
- new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
- new AppBundle\AppBundle(),
- ];
-
- if (in_array($this->getEnvironment(), ['dev', 'test'])) {
- $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
- $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
- $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
- }
-
- return $bundles;
- }
-
-With the ``registerBundles()`` method, you have total control over which bundles
-are used by your application (including the core Symfony bundles).
+A bundle is similar to a plugin in other software, but even better. The core
+features of Symfony framework are implemented with bundles (FrameworkBundle,
+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``
+file::
+
+ // config/bundles.php
+ return [
+ // 'all' means that the bundle is enabled for any Symfony environment
+ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
+ Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
+ Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
+ Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
+ Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
+ Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+ Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
+ // this bundle is enabled only in 'dev' and 'test', so you can't use it in 'prod'
+ Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
+ ];
.. tip::
- A bundle can live *anywhere* as long as it can be autoloaded (via the
- autoloader configured at ``app/autoload.php``).
+ In a default Symfony application that uses :doc:`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.
Creating a Bundle
-----------------
-`SensioGeneratorBundle`_ is an optional bundle that includes commands to create
-different elements of your application, such as bundles. If you create lots of
-bundles, consider using it. However, this section creates and enables a new
-bundle by hand to show how simple it is to do it.
-
+This section creates and enables a new bundle to show there are only a few steps required.
The new bundle is called AcmeTestBundle, where the ``Acme`` portion is just a
dummy name that should be replaced by some "vendor" name that represents you or
your organization (e.g. ABCTestBundle for some company named ``ABC``).
@@ -83,7 +53,7 @@ Start by creating a ``src/Acme/TestBundle/`` directory and adding a new file
called ``AcmeTestBundle.php``::
// src/Acme/TestBundle/AcmeTestBundle.php
- namespace Acme\TestBundle;
+ namespace App\Acme\TestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -100,49 +70,22 @@ called ``AcmeTestBundle.php``::
This empty class is the only piece you need to create the new bundle. Though
commonly empty, this class is powerful and can be used to customize the behavior
-of the bundle.
-
-Now that you've created the bundle, enable it via the ``AppKernel`` class::
-
- // app/AppKernel.php
- public function registerBundles()
- {
- $bundles = [
- // ...
+of the bundle. Now that you've created the bundle, enable it::
- // register your bundle
- new Acme\TestBundle\AcmeTestBundle(),
- ];
+ // config/bundles.php
+ return [
// ...
-
- return $bundles;
- }
+ App\Acme\TestBundle\AcmeTestBundle::class => ['all' => true],
+ ];
And while it doesn't do anything yet, AcmeTestBundle is now ready to be used.
-And as easy as this is, Symfony also provides a command-line interface for
-generating a basic bundle skeleton:
-
-.. code-block:: terminal
-
- $ php bin/console generate:bundle --namespace=Acme/TestBundle
-
-The bundle skeleton generates a basic controller, template and routing
-resource that can be customized. You'll learn more about Symfony's command-line
-tools later.
-
-.. tip::
-
- Whenever creating a new bundle or using a third-party bundle, always make
- sure the bundle has been enabled in ``registerBundles()``. When using
- the ``generate:bundle`` command, this is done for you.
-
Bundle Directory Structure
--------------------------
-The directory structure of a bundle is simple and flexible. By default, the
-bundle system follows a set of conventions that help to keep code consistent
-between all Symfony bundles. Take a look at AcmeDemoBundle, as it contains some
+The directory structure of a bundle is meant to help to keep code consistent
+between all Symfony bundles. It follows a set of conventions, but is flexible
+to be adjusted if needed. Take a look at AcmeDemoBundle, as it contains some
of the most common elements of a bundle:
``Controller/``
@@ -154,14 +97,14 @@ of the most common elements of a bundle:
necessary).
``Resources/config/``
- Houses configuration, including routing configuration (e.g. ``routing.yml``).
+ Houses configuration, including routing configuration (e.g. ``routing.yaml``).
``Resources/views/``
Holds templates organized by controller name (e.g. ``Random/index.html.twig``).
``Resources/public/``
Contains web assets (images, stylesheets, etc) and is copied or symbolically
- linked into the project ``web/`` directory via the ``assets:install`` console
+ linked into the project ``public/`` directory via the ``assets:install`` console
command.
``Tests/``
@@ -178,11 +121,10 @@ the bundle.
Learn more
----------
-.. toctree::
- :maxdepth: 1
- :glob:
-
- bundles/*
+* :doc:`/bundles/override`
+* :doc:`/bundles/best_practices`
+* :doc:`/bundles/configuration`
+* :doc:`/bundles/extension`
+* :doc:`/bundles/prepend_extension`
.. _`third-party bundles`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories
-.. _`SensioGeneratorBundle`: https://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html
diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst
index f9fbda249a9..2091016cecf 100644
--- a/bundles/best_practices.rst
+++ b/bundles/best_practices.rst
@@ -4,21 +4,10 @@
Best Practices for Reusable Bundles
===================================
-There are two types of bundles:
-
-* Application-specific bundles: only used to build your application;
-* Reusable bundles: meant to be shared across many projects.
-
This article is all about how to structure your **reusable bundles** to be
-configurable and extendable. Many of these recommendations do not
-apply to application bundles because you'll want to keep those as simple
-as possible. For application bundles, just follow the practices shown throughout
-the guides.
-
-.. seealso::
-
- The best practices for application-specific bundles are discussed in
- :doc:`/best_practices/introduction`.
+configurable and extendable. Reusable bundles are those meant to be shared
+privately across many company projects or publicly so any Symfony project can
+install them.
.. index::
pair: Bundle; Naming conventions
@@ -28,13 +17,13 @@ the guides.
Bundle Name
-----------
-A bundle is also a PHP namespace. The namespace must follow the `PSR-0`_ or
-`PSR-4`_ interoperability standards for PHP namespaces and class names: it starts
-with a vendor segment, followed by zero or more category segments, and it ends
-with the namespace short name, which must end with ``Bundle``.
+A bundle is also a PHP namespace. The namespace must follow the `PSR-4`_
+interoperability standard for PHP namespaces and class names: it starts with a
+vendor segment, followed by zero or more category segments, and it ends with the
+namespace short name, which must end with ``Bundle``.
A namespace becomes a bundle as soon as you add a bundle class to it. The
-bundle class name must follow these simple rules:
+bundle class name must follow these rules:
* Use only alphanumeric characters and underscores;
* Use a StudlyCaps name (i.e. camelCase with the first letter uppercased);
@@ -124,8 +113,8 @@ Service Container Extensions ``DependencyInjection/``
Doctrine ORM entities (when not using annotations) ``Entity/``
Doctrine ODM documents (when not using annotations) ``Document/``
Event Listeners ``EventListener/``
-Configuration ``Resources/config/``
-Web Resources (CSS, JS, images) ``Resources/public/``
+Configuration (routes, services, etc.) ``Resources/config/``
+Web Assets (CSS, JS, images) ``Resources/public/``
Translation files ``Resources/translations/``
Validation (when not using annotations) ``Resources/config/validation/``
Serialization (when not using annotations) ``Resources/config/serialization/``
@@ -177,6 +166,92 @@ the ``Tests/`` directory. Tests should follow the following principles:
A test suite must not contain ``AllTests.php`` scripts, but must rely on the
existence of a ``phpunit.xml.dist`` file.
+Continuous Integration
+----------------------
+
+Testing bundle code continuously, including all its commits and pull requests,
+is a good practice called Continuous Integration. There are several services
+providing this feature for free for open source projects. The most popular
+service for Symfony bundles is called `Travis CI`_.
+
+Here is the recommended configuration file (``.travis.yml``) for Symfony bundles,
+which test the two latest :doc:`LTS versions `
+of Symfony and the latest beta release:
+
+.. code-block:: yaml
+
+ language: php
+ sudo: false
+ cache:
+ directories:
+ - $HOME/.composer/cache/files
+ - $HOME/symfony-bridge/.phpunit
+
+ env:
+ global:
+ - PHPUNIT_FLAGS="-v"
+ - SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
+
+ matrix:
+ fast_finish: true
+ include:
+ # Minimum supported dependencies with the latest and oldest PHP version
+ - php: 7.2
+ env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
+ - php: 7.0
+ env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
+
+ # Test the latest stable release
+ - php: 7.0
+ - php: 7.1
+ - php: 7.2
+ env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text"
+
+ # Test LTS versions. This makes sure we do not use Symfony packages with version greater
+ # than 2 or 3 respectively. Read more at https://github.com/symfony/lts
+ - php: 7.2
+ env: DEPENDENCIES="symfony/lts:^2"
+ - php: 7.2
+ env: DEPENDENCIES="symfony/lts:^3"
+
+ # Latest commit to master
+ - php: 7.2
+ env: STABILITY="dev"
+
+ allow_failures:
+ # Dev-master is allowed to fail.
+ - env: STABILITY="dev"
+
+ before_install:
+ - if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
+ - if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi;
+ - if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi;
+
+ install:
+ # To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355
+ - if [[ "$COMPOSER_FLAGS" == *"--prefer-lowest"* ]]; then composer update --prefer-dist --no-interaction --prefer-stable --quiet; fi
+ - composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
+ - ./vendor/bin/simple-phpunit install
+
+ script:
+ - composer validate --strict --no-check-lock
+ # simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and
+ # it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge)
+ - ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS
+
+Consider using the `Travis cron`_ tool to make sure your project is built even if
+there are no new pull requests or commits.
+
+Installation
+------------
+
+Bundles should set ``"type": "symfony-bundle"`` in their ``composer.json`` file.
+With this, :doc:`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
+``.gitignore``, etc), then you should create a `Symfony Flex recipe`_.
+
Documentation
-------------
@@ -203,8 +278,19 @@ following standardized instructions in your ``README.md`` file.
Installation
============
- Step 1: Download the Bundle
- ---------------------------
+ Applications that use Symfony Flex
+ ----------------------------------
+
+ Open a command console, enter your project directory and execute:
+
+ ```console
+ $ composer require
+ ```
+
+ Applications that don't use Symfony Flex
+ ----------------------------------------
+
+ ### Step 1: Download the Bundle
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
@@ -217,8 +303,7 @@ following standardized instructions in your ``README.md`` file.
in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
of the Composer documentation.
- Step 2: Enable the Bundle
- -------------------------
+ ### Step 2: Enable the Bundle
Then, enable the bundle by adding it to the list of registered bundles
in the `app/AppKernel.php` file of your project:
@@ -248,8 +333,20 @@ following standardized instructions in your ``README.md`` file.
Installation
============
+ Applications that use Symfony Flex
+ ----------------------------------
+
+ Open a command console, enter your project directory and execute:
+
+ .. code-block:: bash
+
+ $ composer require
+
+ Applications that don't use Symfony Flex
+ ----------------------------------------
+
Step 1: Download the Bundle
- ---------------------------
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
@@ -262,7 +359,7 @@ following standardized instructions in your ``README.md`` file.
in the `installation chapter`_ of the Composer documentation.
Step 2: Enable the Bundle
- -------------------------
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
Then, enable the bundle by adding it to the list of registered bundles
in the ``app/AppKernel.php`` file of your project::
@@ -338,13 +435,13 @@ The end user can provide values in any configuration file:
.. code-block:: yaml
- # app/config/config.yml
+ # config/services.yaml
parameters:
acme_blog.author.email: 'fabien@example.com'
.. code-block:: xml
-
+
setParameter('acme_blog.author.email', 'fabien@example.com');
Retrieve the configuration parameters in your code from the container::
$container->getParameter('acme_blog.author.email');
-Even if this mechanism is simple enough, you should consider using the more
-advanced :doc:`semantic bundle configuration `.
+While this mechanism requires the least effort, you should consider using the
+more advanced :doc:`semantic bundle configuration ` to
+make your configuration more robust.
Versioning
----------
@@ -418,9 +516,8 @@ The ``composer.json`` file should include at least the following metadata:
a string (or array of strings) with a `valid license identifier`_, such as ``MIT``.
``autoload``
- This information is used by Symfony to load the classes of the bundle. The
- `PSR-4`_ autoload standard is recommended for modern bundles, but `PSR-0`_
- standard is also supported.
+ This information is used by Symfony to load the classes of the bundle. It's
+ recommended to use the `PSR-4`_ autoload standard.
In order to make it easier for developers to find your bundle, register it on
`Packagist`_, the official repository for Composer packages.
@@ -430,7 +527,7 @@ Resources
If the bundle references any resources (config files, translation files, etc.),
don't use physical paths (e.g. ``__DIR__/config/services.xml``) but logical
-paths (e.g. ``@AppBundle/Resources/config/services.xml``).
+paths (e.g. ``@FooBundle/Resources/config/services.xml``).
The logical paths are required because of the bundle overriding mechanism that
lets you override any resource/file of any bundle. See :ref:`http-kernel-resource-locator`
@@ -438,7 +535,7 @@ for more details about transforming physical paths into logical paths.
Beware that templates use a simplified version of the logical path shown above.
For example, an ``index.html.twig`` template located in the ``Resources/views/Default/``
-directory of the AppBundle, is referenced as ``@App/Default/index.html.twig``.
+directory of the FooBundle, is referenced as ``@Foo/Default/index.html.twig``.
Learn more
----------
@@ -446,9 +543,11 @@ Learn more
* :doc:`/bundles/extension`
* :doc:`/bundles/configuration`
-.. _`PSR-0`: https://www.php-fig.org/psr/psr-0/
.. _`PSR-4`: https://www.php-fig.org/psr/psr-4/
+.. _`Symfony Flex recipe`: https://github.com/symfony/recipes
.. _`Semantic Versioning Standard`: https://semver.org/
.. _`Packagist`: https://packagist.org/
.. _`choose any license`: https://choosealicense.com/
.. _`valid license identifier`: https://spdx.org/licenses/
+.. _`Travis CI`: https://travis-ci.org/
+.. _`Travis Cron`: https://docs.travis-ci.com/user/cron-jobs/
diff --git a/bundles/configuration.rst b/bundles/configuration.rst
index 3f111cb382c..b1cdf6af67d 100644
--- a/bundles/configuration.rst
+++ b/bundles/configuration.rst
@@ -5,11 +5,12 @@
How to Create Friendly Configuration for a Bundle
=================================================
-If you open your application configuration file (usually ``app/config/config.yml``),
-you'll see a number of different configuration sections, such as ``framework``,
-``twig`` and ``doctrine``. Each of these configures a specific bundle, allowing
-you to define options at a high level and then let the bundle make all the
-low-level, complex changes based on your settings.
+If you open your main application configuration directory (usually
+``config/packages/``), you'll see a number of different files, such as
+``framework.yaml``, ``twig.yaml`` and ``doctrine.yaml``. Each of these
+configures a specific bundle, allowing you to define options at a high level and
+then let the bundle make all the low-level, complex changes based on your
+settings.
For example, the following configuration tells the FrameworkBundle to enable the
form integration, which involves the definition of quite a few services as well
@@ -30,7 +31,7 @@ as integration of other related components:
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony
- http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
@@ -43,29 +44,18 @@ as integration of other related components:
'form' => true,
]);
-.. sidebar:: Using Parameters to Configure your Bundle
-
- If you don't have plans to share your bundle between projects, it doesn't
- make sense to use this more advanced way of configuration. Since you use
- the bundle only in one project, you can just change the service
- configuration each time.
-
- If you *do* want to be able to configure something from within
- ``config.yml``, you can always create a parameter there and use that
- parameter somewhere else.
-
Using the Bundle Extension
--------------------------
Imagine you are creating a new bundle - AcmeSocialBundle - which provides
-integration with Twitter, etc. To make your bundle easy to use, you want to
-allow users to configure it with some configuration that looks like this:
+integration with Twitter. To make your bundle configurable to the user, you
+can add some configuration that looks like this:
.. configuration-block::
.. code-block:: yaml
- # app/config/config.yml
+ # config/packages/acme_social.yaml
acme_social:
twitter:
client_id: 123
@@ -73,7 +63,7 @@ allow users to configure it with some configuration that looks like this:
.. code-block:: xml
-
+
loadFromExtension('acme_social', [
'client_id' => 123,
'client_secret' => 'your_secret',
@@ -149,20 +139,20 @@ For the configuration example in the previous section, the array passed to your
]
Notice that this is an *array of arrays*, not just a single flat array of the
-configuration values. This is intentional, as it allows Symfony to parse
-several configuration resources. For example, if ``acme_social`` appears in
-another configuration file - say ``config_dev.yml`` - with different values
-beneath it, the incoming array might look like this::
+configuration values. This is intentional, as it allows Symfony to parse several
+configuration resources. For example, if ``acme_social`` appears in another
+configuration file - say ``config/packages/dev/acme_social.yaml`` - with
+different values beneath it, the incoming array might look like this::
[
- // values from config.yml
+ // values from config/packages/acme_social.yaml
[
'twitter' => [
'client_id' => 123,
'client_secret' => 'your_secret',
],
],
- // values from config_dev.yml
+ // values from config/packages/dev/acme_social.yaml
[
'twitter' => [
'client_id' => 456,
@@ -190,10 +180,9 @@ The ``Configuration`` class to handle the sample configuration looks like::
{
public function getConfigTreeBuilder()
{
- $treeBuilder = new TreeBuilder();
- $rootNode = $treeBuilder->root('acme_social');
+ $treeBuilder = new TreeBuilder('acme_social');
- $rootNode
+ $treeBuilder->getRootNode()
->children()
->arrayNode('twitter')
->children()
@@ -208,6 +197,10 @@ The ``Configuration`` class to handle the sample configuration looks like::
}
}
+.. deprecated:: 4.2
+
+ Not passing the root node name to ``TreeBuilder`` was deprecated in Symfony 4.2.
+
.. seealso::
The ``Configuration`` class can be much more complicated than shown here,
@@ -306,7 +299,7 @@ In your extension, you can load this and dynamically set its arguments::
.. sidebar:: Processing the Configuration yourself
Using the Config component is fully optional. The ``load()`` method gets an
- array of configuration values. You can simply parse these arrays yourself
+ array of configuration values. You can instead parse these arrays yourself
(e.g. by overriding configurations and using :phpfunction:`isset` to check
for the existence of a value). Be aware that it'll be very hard to support XML::
@@ -324,12 +317,10 @@ In your extension, you can load this and dynamically set its arguments::
Modifying the Configuration of Another Bundle
---------------------------------------------
-If you have multiple bundles that depend on each other, it may be useful
-to allow one ``Extension`` class to modify the configuration passed to another
-bundle's ``Extension`` class, as if the end-developer has actually placed that
-configuration in their ``app/config/config.yml`` file. This can be achieved
-using a prepend extension. For more details, see
-:doc:`/bundles/prepend_extension`.
+If you have multiple bundles that depend on each other, it may be useful to
+allow one ``Extension`` class to modify the configuration passed to another
+bundle's ``Extension`` class. This can be achieved using a prepend extension.
+For more details, see :doc:`/bundles/prepend_extension`.
Dump the Configuration
----------------------
@@ -401,7 +392,7 @@ namespace is then replaced with the XSD validation base path returned from
method. This namespace is then followed by the rest of the path from the base
path to the file itself.
-By convention, the XSD file lives in the ``Resources/config/schema``, but you
+By convention, the XSD file lives in the ``Resources/config/schema/``, but you
can place it anywhere you like. You should return this path as the base path::
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
@@ -422,7 +413,7 @@ Assuming the XSD file is called ``hello-1.0.xsd``, the schema location will be
.. code-block:: xml
-
+
`
-to return the correct DI alias. The DI alias is the name used to refer to the
-bundle in the container (e.g. in the ``app/config/config.yml`` file). By
+method to return the correct DI alias. The DI alias is the name used to refer to
+the bundle in the container (e.g. in the ``config/packages/`` files). By
default, this is done by removing the ``Extension`` suffix and converting the
class name to underscores (e.g. ``AcmeHelloExtension``'s DI alias is
``acme_hello``).
@@ -87,11 +83,10 @@ container.
In the ``load()`` method, you can use PHP code to register service definitions,
but it is more common if you put these definitions in a configuration file
-(using the Yaml, XML or PHP format). Luckily, you can use the file loaders in
-the extension!
+(using the YAML, XML or PHP format).
For instance, assume you have a file called ``services.xml`` in the
-``Resources/config`` directory of your bundle, your ``load()`` method looks like::
+``Resources/config/`` directory of your bundle, your ``load()`` method looks like::
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
@@ -106,67 +101,37 @@ For instance, assume you have a file called ``services.xml`` in the
$loader->load('services.xml');
}
-Other available loaders are the ``YamlFileLoader``, ``PhpFileLoader`` and
-``IniFileLoader``.
-
-.. note::
-
- The ``IniFileLoader`` can only be used to load parameters and it can only
- load them as strings.
-
-.. caution::
-
- If you removed the default file with service definitions (i.e.
- ``app/config/services.yml``), make sure to also remove it from the
- ``imports`` key in ``app/config/config.yml``.
+The other available loaders are ``YamlFileLoader`` and ``PhpFileLoader``.
Using Configuration to Change the Services
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Extension is also the class that handles the configuration for that
-particular bundle (e.g. the configuration in ``app/config/config.yml``). To
-read more about it, see the ":doc:`/bundles/configuration`" article.
+particular bundle (e.g. the configuration in ``config/packages/.yaml``).
+To read more about it, see the ":doc:`/bundles/configuration`" article.
Adding Classes to Compile
-------------------------
-.. deprecated:: 3.3
-
- This technique is discouraged and the ``addClassesToCompile()`` method was
- deprecated in Symfony 3.3 because modern PHP versions make it unnecessary.
-
-Symfony creates a big ``classes.php`` file in the cache directory to aggregate
-the contents of the PHP classes that are used in every request. This reduces the
-I/O operations and increases the application performance.
+Bundles can hint Symfony about which of their classes contain annotations so
+they are compiled when generating the application cache to improve the overall
+performance. Define the list of annotated classes to compile in the
+``addAnnotatedClassesToCompile()`` method::
-.. versionadded:: 3.2
-
- The ``addAnnotatedClassesToCompile()`` method was added in Symfony 3.2.
-
-Your bundles can also add their own classes into this file thanks to the
-``addClassesToCompile()`` and ``addAnnotatedClassesToCompile()`` methods (both
-work in the same way, but the second one is for classes that contain PHP
-annotations). Define the classes to compile as an array of their fully qualified
-class names::
-
- use AppBundle\Manager\UserManager;
- use AppBundle\Utils\Slugger;
+ use App\Manager\UserManager;
+ use App\Utils\Slugger;
// ...
public function load(array $configs, ContainerBuilder $container)
{
// ...
- // this method can't compile classes that contain PHP annotations
- $this->addClassesToCompile([
- UserManager::class,
- Slugger::class,
- // ...
- ]);
-
- // add here only classes that contain PHP annotations
$this->addAnnotatedClassesToCompile([
- 'AppBundle\\Controller\\DefaultController',
+ // you can define the fully qualified class names...
+ 'App\\Controller\\DefaultController',
+ // ... but glob patterns are also supported:
+ '**Bundle\\Controller\\',
+
// ...
]);
}
@@ -176,28 +141,6 @@ class names::
If some class extends from other classes, all its parents are automatically
included in the list of classes to compile.
-.. versionadded:: 3.2
-
- The option to add classes to compile using patterns was introduced in Symfony 3.2.
-
-The classes to compile can also be added using file path patterns::
-
- // ...
- public function load(array $configs, ContainerBuilder $container)
- {
- // ...
-
- $this->addClassesToCompile([
- '**Bundle\\Manager\\',
- // ...
- ]);
-
- $this->addAnnotatedClassesToCompile([
- '**Bundle\\Controller\\',
- // ...
- ]);
- }
-
Patterns are transformed into the actual class namespaces using the classmap
generated by Composer. Therefore, before using these patterns, you must generate
the full classmap executing the ``dump-autoload`` command of Composer.
diff --git a/bundles/index.rst b/bundles/index.rst
index 87641de5e23..78dd8c6d4fb 100644
--- a/bundles/index.rst
+++ b/bundles/index.rst
@@ -1,14 +1,14 @@
+:orphan:
+
Bundles
=======
.. toctree::
:maxdepth: 2
- installation
- best_practices
- inheritance
override
- remove
- extension
+ inheritance
+ best_practices
configuration
+ extension
prepend_extension
diff --git a/bundles/inheritance.rst b/bundles/inheritance.rst
index 201b5f18fec..d8ce372adb4 100644
--- a/bundles/inheritance.rst
+++ b/bundles/inheritance.rst
@@ -6,107 +6,6 @@ How to Use Bundle Inheritance to Override Parts of a Bundle
.. caution::
- Bundle inheritance is deprecated since Symfony 3.4 and will be removed in
- 4.0.
-
-When working with third-party bundles, you'll probably come across a situation
-where you want to override a file in that third-party bundle with a file
-in one of your own bundles. Symfony gives you a very convenient way to override
-things like controllers, templates, and other files in a bundle's
-``Resources/`` directory.
-
-For example, suppose that you have installed `FOSUserBundle`_, but you want to
-override its base ``layout.html.twig`` template, as well as one of its
-controllers.
-
-First, create a new bundle called UserBundle and enable it in your application.
-Then, register the third-party FOSUserBundle as the "parent" of your bundle::
-
- // src/UserBundle/UserBundle.php
- namespace UserBundle;
-
- use Symfony\Component\HttpKernel\Bundle\Bundle;
-
- class UserBundle extends Bundle
- {
- public function getParent()
- {
- return 'FOSUserBundle';
- }
- }
-
-By making this small change, you can now override several parts of the FOSUserBundle
-by creating a file with the same name.
-
-.. note::
-
- Despite the method name, there is no parent/child relationship between
- the bundles, it is just a way to extend and override an existing bundle.
-
-Overriding Controllers
-~~~~~~~~~~~~~~~~~~~~~~
-
-Suppose you want to add some functionality to the ``registerAction()`` of a
-``RegistrationController`` that lives inside FOSUserBundle. To do so,
-just create your own ``RegistrationController.php`` file, override the bundle's
-original method, and change its functionality::
-
- // src/UserBundle/Controller/RegistrationController.php
- namespace UserBundle\Controller;
-
- use FOS\UserBundle\Controller\RegistrationController as BaseController;
-
- class RegistrationController extends BaseController
- {
- public function registerAction()
- {
- $response = parent::registerAction();
-
- // ... do custom stuff
- return $response;
- }
- }
-
-.. tip::
-
- Depending on how severely you need to change the behavior, you might
- call ``parent::registerAction()`` or completely replace its logic with
- your own.
-
-.. note::
-
- Overriding controllers in this way only works if the bundle refers to
- the controller using the standard ``FOSUserBundle:Registration:register``
- syntax in routes and templates. This is the best practice.
-
-Overriding Resources: Templates, Routing, etc
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Most resources can also be overridden by creating a file in the same location
-as your parent bundle.
-
-For example, it's very common to need to override the FOSUserBundle's
-``layout.html.twig`` template so that it uses your application's base layout.
-Since the file lives at ``Resources/views/layout.html.twig`` in the FOSUserBundle,
-you can create your own file in the same location of UserBundle. Symfony will
-ignore the file that lives inside the FOSUserBundle entirely, and use your file
-instead.
-
-The same goes for routing files and some other resources.
-
-.. note::
-
- The overriding of resources only works when you refer to resources with
- the ``@FOSUserBundle/Resources/config/routing/security.xml`` method.
- You need to use the ``@BundleName`` shortcut when referring to resources
- so they can be successfully overridden (except templates, which are
- overridden in a different way, as explained in :doc:`/templating/overriding`).
-
-.. caution::
-
- Translation and validation files do not work in the same way as described
- above. Read ":ref:`override-translations`" if you want to learn how to
- override translations and see ":ref:`override-validation`" for tricks to
- override the validation.
-
-.. _`FOSUserBundle`: https://github.com/friendsofsymfony/fosuserbundle
+ Bundle inheritance was removed in Symfony 4.0, but you can
+ :doc:`override any part of a bundle ` without
+ using bundle inheritance.
diff --git a/bundles/installation.rst b/bundles/installation.rst
deleted file mode 100644
index c8f3e4ec1d5..00000000000
--- a/bundles/installation.rst
+++ /dev/null
@@ -1,160 +0,0 @@
-.. index::
- single: Bundle; Installation
-
-How to Install 3rd Party Bundles
-================================
-
-Most bundles provide their own installation instructions. However, the
-basic steps for installing a bundle are the same:
-
-* `A) Add Composer Dependencies`_
-* `B) Enable the Bundle`_
-* `C) Configure the Bundle`_
-
-A) Add Composer Dependencies
-----------------------------
-
-Dependencies are managed with Composer, so if Composer is new to you, learn
-some basics in `their documentation`_. This involves two steps:
-
-1) Find out the Name of the Bundle on Packagist
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The README for a bundle (e.g. `FOSUserBundle`_) usually tells you its name
-(e.g. ``friendsofsymfony/user-bundle``). If it doesn't, you can search for
-the bundle on the `Packagist.org`_ site.
-
-.. tip::
-
- Looking for bundles? Try searching for `symfony-bundle topic on GitHub`_.
-
-2) Install the Bundle via Composer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now that you know the package name, you can install it via Composer:
-
-.. code-block:: terminal
-
- $ composer require friendsofsymfony/user-bundle
-
-This will choose the best version for your project, add it to ``composer.json``
-and download its code into the ``vendor/`` directory. If you need a specific
-version, include it as the second argument of the `composer require`_ command:
-
-.. code-block:: terminal
-
- $ composer require friendsofsymfony/user-bundle "~2.0"
-
-B) Enable the Bundle
---------------------
-
-At this point, the bundle is installed in your Symfony project (e.g.
-``vendor/friendsofsymfony/``) and the autoloader recognizes its classes.
-The only thing you need to do now is register the bundle in ``AppKernel``::
-
- // app/AppKernel.php
-
- // ...
- class AppKernel extends Kernel
- {
- // ...
-
- public function registerBundles()
- {
- $bundles = [
- // ...
- new FOS\UserBundle\FOSUserBundle(),
- ];
-
- // ...
- }
- }
-
-In a few rare cases, you may want a bundle to be *only* enabled in the development
-:doc:`environment `. For example,
-the DoctrineFixturesBundle helps to load dummy data - something you probably
-only want to do while developing. To only load this bundle in the ``dev``
-and ``test`` environments, register the bundle in this way::
-
- // app/AppKernel.php
-
- // ...
- class AppKernel extends Kernel
- {
- // ...
-
- public function registerBundles()
- {
- $bundles = [
- // ...
- ];
-
- if (in_array($this->getEnvironment(), ['dev', 'test'])) {
- $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
- }
-
- // ...
- }
- }
-
-C) Configure the Bundle
------------------------
-
-It's pretty common for a bundle to need some additional setup or configuration
-in ``app/config/config.yml``. The bundle's documentation will tell you about
-the configuration, but you can also get a reference of the bundle's configuration
-via the ``config:dump-reference`` command:
-
-.. code-block:: terminal
-
- $ php bin/console config:dump-reference AsseticBundle
-
-Instead of the full bundle name, you can also pass the short name used as the root
-of the bundle's configuration:
-
-.. code-block:: terminal
-
- $ php bin/console config:dump-reference assetic
-
-The output will look like this:
-
-.. code-block:: yaml
-
- assetic:
- debug: '%kernel.debug%'
- use_controller:
- enabled: '%kernel.debug%'
- profiler: false
- read_from: '%kernel.project_dir%/web'
- write_to: '%assetic.read_from%'
- java: /usr/bin/java
- node: /usr/local/bin/node
- node_paths: []
- # ...
-
-.. tip::
-
- For complex bundles that define lots of configuration options, you can pass
- a second optional argument to the ``config:dump-reference`` command to only
- display a section of the entire configuration:
-
- .. code-block:: terminal
-
- $ php bin/console config:dump-reference AsseticBundle use_controller
-
- # Default configuration for "AsseticBundle" at path "use_controller"
- use_controller:
- enabled: '%kernel.debug%'
- profiler: false
-
-Other Setup
------------
-
-At this point, check the ``README`` file of your brand new bundle to see
-what to do next. Have fun!
-
-.. _their documentation: https://getcomposer.org/doc/00-intro.md
-.. _Packagist.org: https://packagist.org
-.. _FOSUserBundle: https://github.com/FriendsOfSymfony/FOSUserBundle
-.. _`composer require`: https://getcomposer.org/doc/03-cli.md#require
-.. _`symfony-bundle topic on GitHub`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories
diff --git a/bundles/override.rst b/bundles/override.rst
index a66c277fe2f..b73b245d13b 100644
--- a/bundles/override.rst
+++ b/bundles/override.rst
@@ -4,33 +4,72 @@
How to Override any Part of a Bundle
====================================
-This document is a quick reference for how to override different parts of
-third-party bundles without using :doc:`/bundles/inheritance`, which is
-deprecated since Symfony 3.4.
+When using a third-party bundle, you might want to customize or override some of
+its features. This document describes ways of overriding the most common
+features of a bundle.
.. tip::
The bundle overriding mechanism means that you cannot use physical paths to
refer to bundle's resources (e.g. ``__DIR__/config/services.xml``). Always
- use logical paths in your bundles (e.g. ``@AppBundle/Resources/config/services.xml``)
+ use logical paths in your bundles (e.g. ``@FooBundle/Resources/config/services.xml``)
and call the :ref:`locateResource() method `
to turn them into physical paths when needed.
+.. _override-templates:
+
Templates
---------
-See :doc:`/templating/overriding`.
+Third-party bundle templates can be overridden in the
+``/templates/bundles//`` directory. The new templates
+must use the same name and path (relative to ``/Resources/views/``) as
+the original templates.
+
+For example, to override the ``Resources/views/Registration/confirmed.html.twig``
+template from the FOSUserBundle, create this template:
+``/templates/bundles/FOSUserBundle/Registration/confirmed.html.twig``
+
+.. caution::
+
+ If you add a template in a new location, you *may* need to clear your
+ cache (``php bin/console cache:clear``), even if you are in debug mode.
+
+Instead of overriding an entire template, you may just want to override one or
+more blocks. However, since you are overriding the template you want to extend
+from, you would end up in an infinite loop error. The solution is to use the
+special ``!`` prefix in the template name to tell Symfony that you want to
+extend from the original template, not from the overridden one:
+
+.. code-block:: twig
+
+ {# templates/bundles/FOSUserBundle/Registration/confirmed.html.twig #}
+ {# the special '!' prefix avoids errors when extending from an overridden template #}
+ {% extends "@!FOSUserBundle/Registration/confirmed.html.twig" %}
+
+ {% block some_block %}
+ ...
+ {% endblock %}
+
+.. _templating-overriding-core-templates:
+
+.. tip::
+
+ Symfony internals use some bundles too, so you can apply the same technique
+ to override the core Symfony templates. For example, you can
+ :doc:`customize error pages ` overriding TwigBundle
+ templates.
Routing
-------
Routing is never automatically imported in Symfony. If you want to include
the routes from any bundle, then they must be manually imported from somewhere
-in your application (e.g. ``app/config/routing.yml``).
+in your application (e.g. ``config/routes.yaml``).
The easiest way to "override" a bundle's routing is to never import it at
-all. Instead of importing a third-party bundle's routing, copy that
-routing file into your application, modify it, and import it instead.
+all. Instead of importing a third-party bundle's routing, copy
+that routing file into your application, modify it, and import it instead.
Controllers
-----------
@@ -86,7 +125,7 @@ to a new validation group:
.. code-block:: yaml
- # src/Acme/UserBundle/Resources/config/validation.yml
+ # config/validator/validation.yaml
FOS\UserBundle\Model\User:
properties:
plainPassword:
@@ -99,7 +138,7 @@ to a new validation group:
.. code-block:: xml
-
+
`.
+Translations are not related to bundles, but to :ref:`translation domains `.
+For this reason, you can override any bundle translation file from the main
+``translations/`` directory, as long as the new file uses the same domain.
+
+For example, to override the translations defined in the
+``Resources/translations/FOSUserBundle.es.yml`` file of the FOSUserBundle,
+create a``/translations/FOSUserBundle.es.yml`` file.
.. _`the Doctrine documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#overrides
diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst
index b9f2d7adc0b..642da040128 100644
--- a/bundles/prepend_extension.rst
+++ b/bundles/prepend_extension.rst
@@ -12,11 +12,10 @@ users to choose to remove functionality they are not using. Creating multiple
bundles has the drawback that configuration becomes more tedious and settings
often need to be repeated for various bundles.
-It is possible to remove the disadvantage of the multiple bundle approach
-by enabling a single Extension to prepend the settings for any bundle.
-It can use the settings defined in the ``app/config/config.yml``
-to prepend settings just as if they had been written explicitly by
-the user in the application configuration.
+It is possible to remove the disadvantage of the multiple bundle approach by
+enabling a single Extension to prepend the settings for any bundle. It can use
+the settings defined in the ``config/*`` files to prepend settings just as if
+they had been written explicitly by the user in the application configuration.
For example, this could be used to configure the entity manager name to use in
multiple bundles. Or it can be used to enable an optional feature that depends
@@ -50,7 +49,7 @@ prepend settings to a bundle extension developers can use the
:method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::prependExtensionConfig`
method on the :class:`Symfony\\Component\\DependencyInjection\\ContainerBuilder`
instance. As this method only prepends settings, any other settings done explicitly
-inside the ``app/config/config.yml`` would override these prepended settings.
+inside the ``config/*`` files would override these prepended settings.
The following example illustrates how to prepend
a configuration setting in multiple bundles as well as disable a flag in multiple bundles
@@ -73,7 +72,7 @@ in case a specific other bundle is not registered::
// acme_something and acme_other
//
// note that if the user manually configured
- // use_acme_goodbye to true in app/config/config.yml
+ // use_acme_goodbye to true in config/services.yaml
// then the setting would in the end be true and not false
$container->prependExtensionConfig($name, $config);
break;
@@ -96,14 +95,15 @@ in case a specific other bundle is not registered::
}
The above would be the equivalent of writing the following into the
-``app/config/config.yml`` in case AcmeGoodbyeBundle is not registered and the
-``entity_manager_name`` setting for ``acme_hello`` is set to ``non_default``:
+``config/packages/acme_something.yaml`` in case AcmeGoodbyeBundle is not
+registered and the ``entity_manager_name`` setting for ``acme_hello`` is set to
+``non_default``:
.. configuration-block::
.. code-block:: yaml
- # app/config/config.yml
+ # config/packages/acme_something.yaml
acme_something:
# ...
use_acme_goodbye: false
@@ -115,7 +115,7 @@ The above would be the equivalent of writing the following into the
.. code-block:: xml
-
+
loadFromExtension('acme_something', [
// ...
'use_acme_goodbye' => false,
diff --git a/bundles/remove.rst b/bundles/remove.rst
deleted file mode 100644
index 0c27eacb68b..00000000000
--- a/bundles/remove.rst
+++ /dev/null
@@ -1,96 +0,0 @@
-.. index::
- single: Bundle; Removing a bundle
-
-How to Remove a Bundle
-======================
-
-1. Unregister the Bundle in the ``AppKernel``
----------------------------------------------
-
-To disconnect the bundle from the framework, you should remove the bundle from
-the ``AppKernel::registerBundles()`` method. The bundle will likely be found in
-the ``$bundles`` array declaration or added to it in a later statement if the
-bundle is only registered in the development environment::
-
- // app/AppKernel.php
-
- // ...
- class AppKernel extends Kernel
- {
- public function registerBundles()
- {
- $bundles = [
- new Acme\DemoBundle\AcmeDemoBundle(),
- ];
-
- if (in_array($this->getEnvironment(), ['dev', 'test'])) {
- // comment or remove this line:
- // $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
- // ...
- }
- }
- }
-
-2. Remove Bundle Configuration
-------------------------------
-
-Now that Symfony doesn't know about the bundle, you need to remove any
-configuration and routing configuration inside the ``app/config`` directory
-that refers to the bundle.
-
-2.1 Remove Bundle Routing
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-*Some* bundles require you to import routing configuration. Check for references
-to the bundle in ``app/config/routing.yml`` and ``app/config/routing_dev.yml``.
-If you find any references, remove them completely.
-
-2.2 Remove Bundle Configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some bundles contain configuration in one of the ``app/config/config*.yml``
-files. Be sure to remove the related configuration from these files. You can
-quickly spot bundle configuration by looking for an ``acme_demo`` (or whatever
-the name of the bundle is, e.g. ``fos_user`` for the FOSUserBundle) string in
-the configuration files.
-
-3. Remove the Bundle from the Filesystem
-----------------------------------------
-
-Now you have removed every reference to the bundle in your application, you
-should remove the bundle from the filesystem. The bundle will be located in
-`src/` for example the ``src/Acme/DemoBundle`` directory. You should remove this
-directory, and any parent directories that are now empty (e.g. ``src/Acme/``).
-
-.. tip::
-
- If you don't know the location of a bundle, you can use the
- :method:`Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface::getPath` method
- to get the path of the bundle::
-
- dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath());
- die();
-
-3.1 Remove Bundle Assets
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Remove the assets of the bundle in the web/ directory (e.g.
-``web/bundles/acmedemo`` for the AcmeDemoBundle).
-
-4. Remove Integration in other Bundles
---------------------------------------
-
-Some bundles rely on other bundles, if you remove one of the two, the other
-will probably not work. Be sure that no other bundles, third party or self-made,
-rely on the bundle you are about to remove.
-
-.. tip::
-
- If one bundle relies on another, in most cases it means that it uses
- some services from the bundle. Searching for the bundle alias string may
- help you spot them (e.g. ``acme_demo`` for bundles depending on AcmeDemoBundle).
-
-.. tip::
-
- If a third party bundle relies on another bundle, you can find that bundle
- mentioned in the ``composer.json`` file included in the bundle directory.
diff --git a/components/asset.rst b/components/asset.rst
index b550f2ed036..474a1d03704 100644
--- a/components/asset.rst
+++ b/components/asset.rst
@@ -155,7 +155,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;
@@ -282,8 +282,8 @@ You can also pass a schema-agnostic URL::
// result: //static.example.com/images/logo.png?v1
This is useful because assets will automatically be requested via HTTPS if
-a visitor is viewing your site in https. Just make sure that your CDN host
-supports https.
+a visitor is viewing your site in https. If you want to use this, make sure
+that your CDN host supports HTTPS.
In case you serve assets from more than one domain to improve application
performance, pass an array of URLs as the first argument to the ``UrlPackage``
@@ -372,6 +372,32 @@ document inside a template::
echo $packages->getUrl('resume.pdf', 'doc');
// result: /somewhere/deep/for/documents/resume.pdf?v1
+Local Files and Other Protocols
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In addition to HTTP this component supports other protocols (such as ``file://``
+and ``ftp://``). This allows for example to serve local files in order to
+improve performance::
+
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $localPackage = new UrlPackage(
+ 'file:///path/to/images/',
+ new EmptyVersionStrategy()
+ );
+
+ $ftpPackage = new UrlPackage(
+ 'ftp://example.com/images/',
+ new EmptyVersionStrategy()
+ );
+
+ echo $localPackage->getUrl('/logo.png');
+ // result: file:///path/to/images/logo.png
+
+ echo $ftpPackage->getUrl('/logo.png');
+ // result: ftp://example.com/images/logo.png
+
Learn more
----------
diff --git a/components/browser_kit.rst b/components/browser_kit.rst
index 81bd04f01b6..ce1cbec9077 100644
--- a/components/browser_kit.rst
+++ b/components/browser_kit.rst
@@ -81,17 +81,35 @@ The value returned by the ``request()`` method is an instance of the
:doc:`DomCrawler component `, which allows accessing
and traversing HTML elements programmatically.
+The :method:`Symfony\\Component\\BrowserKit\\Client::xmlHttpRequest` method,
+which defines the same arguments as the ``request()`` method, is a shortcut to
+make AJAX requests::
+
+ use Acme\Client;
+
+ $client = new Client();
+ // the required HTTP_X_REQUESTED_WITH header is added automatically
+ $crawler = $client->xmlHttpRequest('GET', '/');
+
Clicking Links
~~~~~~~~~~~~~~
-The ``Crawler`` object is capable of simulating link clicks. First, pass the
-text content of the link to the ``selectLink()`` method, which returns a
-``Link`` object. Then, pass this object to the ``click()`` method, which
-performs the needed HTTP GET request to simulate the link click::
+The ``Client`` object is capable of simulating link clicks. Pass the text
+content of the link and the client will perform the needed HTTP GET request to
+simulate the link click::
use Acme\Client;
$client = new Client();
+ $client->request('GET', '/product/123');
+
+ $crawler = $client->clickLink('Go elsewhere...');
+
+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:
+
+ // ...
$crawler = $client->request('GET', '/product/123');
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link);
@@ -99,27 +117,48 @@ performs the needed HTTP GET request to simulate the link click::
Submitting Forms
~~~~~~~~~~~~~~~~
-The ``Crawler`` object is also capable of selecting forms. First, select any of
-the form's buttons with the ``selectButton()`` method. Then, use the ``form()``
-method to select the form which the button belongs to.
-
-After selecting the form, fill in its data and send it using the ``submit()``
-method (which makes the needed HTTP POST request to submit the form contents)::
+The ``Client`` object is also capable of submitting forms. First, select the
+form using any of its buttons and then override any of its properties (method,
+field values, etc.) before submitting it::
use Acme\Client;
- // make a real request to an external site
$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');
+ // find the form with the 'Log in' button and submit it
+ // 'Log in' can be the text content, id, value or name of a