Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Updated the best practices article for reusable bundles #5014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions components/dependency_injection/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Advanced Container Configuration
================================

.. _container-private-services:

Marking Services as public / private
------------------------------------

Expand Down
233 changes: 136 additions & 97 deletions cookbook/bundles/best_practices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ This article is all about how to structure your **reusable bundles** so that
they're easy to configure and extend. 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 :doc:`book </book/index>`, the :doc:`cookbook </cookbook/index>` and the
:doc:`best practices </best_practices/index>` book.
the book and cookbook.

.. seealso::

The best practices for application-specific bundles are discussed in
:doc:`/best_practices/introduction`.

.. index::
pair: Bundle; Naming conventions
Expand All @@ -24,8 +28,8 @@ the :doc:`book </book/index>`, the :doc:`cookbook </cookbook/index>` and the
Bundle Name
-----------

A bundle is also a PHP namespace. The namespace must follow the technical
interoperability `standards`_ for PHP namespaces and class names: it starts
A bundle is also a PHP namespace. The namespace must follow the `PSR-0`_ or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bundle has a namespace?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, bundle is in fact just a namespace or a name to refer to a location on the filesystem

`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 a ``Bundle`` suffix.

Expand All @@ -41,15 +45,12 @@ bundle class name must follow these simple rules:

Here are some valid bundle namespaces and class names:

+-----------------------------------+--------------------------+
| Namespace | Bundle Class Name |
+===================================+==========================+
| ``Acme\Bundle\BlogBundle`` | ``AcmeBlogBundle`` |
+-----------------------------------+--------------------------+
| ``Acme\Bundle\Social\BlogBundle`` | ``AcmeSocialBlogBundle`` |
+-----------------------------------+--------------------------+
| ``Acme\BlogBundle`` | ``AcmeBlogBundle`` |
+-----------------------------------+--------------------------+
========================== ==================
Namespace Bundle Class Name
========================== ==================
``Acme\Bundle\BlogBundle`` ``AcmeBlogBundle``
``Acme\BlogBundle`` ``AcmeBlogBundle``
========================== ==================

By convention, the ``getName()`` method of the bundle class should return the
class name.
Expand All @@ -67,49 +68,47 @@ class name.
:class:`Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle`.

Each bundle has an alias, which is the lower-cased short version of the bundle
name using underscores (``acme_hello`` for ``AcmeHelloBundle``, or
``acme_social_blog`` for ``Acme\Social\BlogBundle`` for instance). This alias
is used to enforce uniqueness within a bundle (see below for some usage
examples).
name using underscores (``acme_blog`` for ``AcmeBlogBundle``). This alias
is used to enforce uniqueness within a project and for defining bundle's
configuration options (see below for some usage examples).

Directory Structure
-------------------

The basic directory structure of a HelloBundle must read as follows:
The basic directory structure of an AcmeBlogBundle must read as follows:

.. code-block:: text

XXX/...
HelloBundle/
HelloBundle.php
Controller/
Resources/
meta/
LICENSE
config/
doc/
index.rst
translations/
views/
public/
Tests/

The ``XXX`` directory(ies) reflects the namespace structure of the bundle.

The following files are mandatory:

* ``HelloBundle.php``;
* ``Resources/meta/LICENSE``: The full license for the code;
<your-bundle>/
├─ AcmeBlogBundle.php
├─ Controller/
├─ README.md
├─ Resources/
│ ├─ meta/
│ │ └─ LICENSE
│ ├─ config/
│ ├─ doc/
│ │ └─ index.rst
│ ├─ translations/
│ ├─ views/
│ └─ public/
└─ Tests/

**The following files are mandatory**, because they ensure a structure convention
that automated tools can rely on:

* ``AcmeBlogBundle.php``: This is the class that transforms a plain directory
into a Symfony bundle;
* ``README.md``: This file contains the basic description of the bundle and it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should allow to use other formats supported by Github for the readme (or whatever project hosting you use).
Otherwise some SensioLabs bundles would have to be considered invalid when they use README.rst, while there is no reason for it. The requirement is to provide a readme for the project, but writing it in Markdown is not mandatory

usually shows some basic examples and links to its full documentation (it
can use any of the markup formats supported by GitHub, such as ``README.rst``);
* ``Resources/meta/LICENSE``: The full license for the code. The license file
can also be stored in the bundle's root directory to follow the generic
conventions about packages;
* ``Resources/doc/index.rst``: The root file for the Bundle documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add a README file (no shared project should be allowed to omit the readme)


.. note::

These conventions ensure that automated tools can rely on this default
structure to work.

The depth of sub-directories should be kept to the minimal for most used
classes and files (two levels at a maximum). More levels can be defined for
non-strategic, less-used files.
The depth of sub-directories should be kept to the minimum for most used
classes and files (two levels maximum).

The bundle directory is read-only. If you need to write temporary files, store
them under the ``cache/`` or ``log/`` directory of the host application. Tools
Expand All @@ -118,41 +117,31 @@ files are going to be part of the repository.

The following classes and files have specific emplacements:

+------------------------------+-----------------------------+
| Type | Directory |
+==============================+=============================+
| Commands | ``Command/`` |
+------------------------------+-----------------------------+
| Controllers | ``Controller/`` |
+------------------------------+-----------------------------+
| Service Container Extensions | ``DependencyInjection/`` |
+------------------------------+-----------------------------+
| Event Listeners | ``EventListener/`` |
+------------------------------+-----------------------------+
| Configuration | ``Resources/config/`` |
+------------------------------+-----------------------------+
| Web Resources | ``Resources/public/`` |
+------------------------------+-----------------------------+
| Translation files | ``Resources/translations/`` |
+------------------------------+-----------------------------+
| Templates | ``Resources/views/`` |
+------------------------------+-----------------------------+
| Unit and Functional Tests | ``Tests/`` |
+------------------------------+-----------------------------+

.. note::

When building a reusable bundle, model classes should be placed in the
``Model`` namespace. See :doc:`/cookbook/doctrine/mapping_model_classes` for
how to handle the mapping with a compiler pass.
=============================== =============================
Type Directory
=============================== =============================
Commands ``Command/``
Controllers ``Controller/``
Service Container Extensions ``DependencyInjection/``
Event Listeners ``EventListener/``
Model classes [1] ``Model/``
Configuration ``Resources/config/``
Web Resources (CSS, JS, images) ``Resources/public/``
Translation files ``Resources/translations/``
Templates ``Resources/views/``
Unit and Functional Tests ``Tests/``
=============================== =============================

[1] See :doc:`/cookbook/doctrine/mapping_model_classes` for how to handle the
mapping with a compiler pass.

Classes
-------

The bundle directory structure is used as the namespace hierarchy. For
instance, a ``HelloController`` controller is stored in
``Bundle/HelloBundle/Controller/HelloController.php`` and the fully qualified
class name is ``Bundle\HelloBundle\Controller\HelloController``.
instance, a ``ContentController`` controller is stored in
``Acme/BlogBundle/Controller/ContentController.php`` and the fully qualified
class name is ``Acme\BlogBundle\Controller\ContentController``.

All classes and files must follow the :doc:`Symfony coding standards </contributing/code/standards>`.

Expand All @@ -162,7 +151,7 @@ Commands, Helpers, Listeners, and Controllers.
Classes that connect to the event dispatcher should be suffixed with
``Listener``.

Exception classes should be stored in an ``Exception`` sub-namespace.
Exceptions classes should be stored in an ``Exception`` sub-namespace.

Vendors
-------
Expand All @@ -177,7 +166,7 @@ Tests
-----

A bundle should come with a test suite written with PHPUnit and stored under
the ``Tests/`` directory. Tests should follow these principles:
the ``Tests/`` directory. Tests should follow the following principles:

* The test suite must be executable with a simple ``phpunit`` command run from
a sample application;
Expand All @@ -186,14 +175,13 @@ the ``Tests/`` directory. Tests should follow these principles:
* The tests should cover at least 95% of the code base.

.. note::

A test suite must not contain ``AllTests.php`` scripts, but must rely on the
existence of a ``phpunit.xml.dist`` file.

Documentation
-------------

All classes and functions must be fully documented using `PHPDoc`_ tags.
All classes and functions must come with full PHPDoc.

Extensive documentation should also be provided in the
:doc:`reStructuredText </contributing/documentation/format>` format, under
Expand Down Expand Up @@ -317,8 +305,8 @@ Routing
-------

If the bundle provides routes, they must be prefixed with the bundle alias.
For an AcmeBlogBundle for instance, all routes must be prefixed with
``acme_blog_``.
For example, if your bundle is called AcmeBlogBundle, all its routes must be
prefixed with ``acme_blog_``.

Templates
---------
Expand All @@ -330,8 +318,7 @@ Translation Files
-----------------

If a bundle provides message translations, they must be defined in the XLIFF
format; the :ref:`translation domain <using-message-domains>` should be named
after the bundle name (``bundle.hello``).
format; the domain should be named after the bundle name (``acme_blog``).

A bundle must not override existing messages from another bundle.

Expand All @@ -346,7 +333,7 @@ the Symfony configuration. Symfony parameters are simple key/value pairs; a
value being any valid PHP value. Each parameter name should start with the
bundle alias, though this is just a best-practice suggestion. The rest of the
parameter name will use a period (``.``) to separate different parts (e.g.
``acme_hello.email.from``).
``acme_blog.author.email``).

The end user can provide values in any configuration file:

Expand All @@ -356,31 +343,83 @@ The end user can provide values in any configuration file:

# app/config/config.yml
parameters:
acme_hello.email.from: [email protected]
acme_blog.author.email: [email protected]

.. code-block:: xml

<!-- app/config/config.xml -->
<parameters>
<parameter key="acme_hello.email.from">[email protected]</parameter>
<parameter key="acme_blog.author.email">[email protected]</parameter>
</parameters>

.. code-block:: php

// app/config/config.php
$container->setParameter('acme_hello.email.from', '[email protected]');
$container->setParameter('acme_blog.author.email', '[email protected]');

Retrieve the configuration parameters in your code from the container::

$container->getParameter('acme_hello.email.from');
$container->getParameter('acme_blog.author.email');

Even if this mechanism is simple enough, you are highly encouraged to use the
:doc:`semantic bundle configuration </cookbook/bundles/extension>` instead.
Even if this mechanism is simple enough, you should consider using the more
advanced :doc:`semantic bundle configuration </cookbook/bundles/configuration>`.

.. note::
Versioning
----------

Bundles must be versioned following the `Semantic Versioning Standard`_.

Services
--------

If the bundle defines services, they must be prefixed with the bundle alias.
For example, AcmeBlogBundle services must be prefixed with ``acme_blog``.

In addition, services not meant to be used by the application directly, should
be :ref:`defined as private <container-private-services>`.

.. seealso::

You can learn much more about service loading in bundles reading this article:
:doc:`How to Load Service Configuration inside a Bundle </cookbook/bundles/extension>`.

Composer Metadata
-----------------

The ``composer.json`` file should include at least the following metadata:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add the autoload section. Symfony relies on the composer autoloader so shared bundles should register themselves in the autoloader


``name``
Consists of the vendor and the short bundle name. If you are releasing the
bundle on your own instead of on behalf of a company, use your personal name
(e.g. ``johnsmith/blog-bundle``). The bundle short name excludes the vendor
name and separates each word with an hyphen. For example: ``AcmeBlogBundle``
is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` is
transformed into ``social-connect-bundle``.

``description``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it render properly without a blank line between the entries?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know ... so I've added a blank line as suggested. Thanks.

A brief explanation of the purpose of the bundle.

``type``
Use the ``symfony-bundle`` value.

``license``
``MIT`` is the preferred license for Symfony bundles, but you can use any
other license.

``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.

In order to make it easier for developers to find your bundle, register it on
`Packagist`_, the official repository for Composer packages.

Learn more from the Cookbook
----------------------------

If you are defining services, they should also be prefixed with the bundle
alias.
* :doc:`/cookbook/bundles/extension`

.. _`standards`: http://www.php-fig.org/psr/psr-0/
.. _`PHPDoc`: https://en.wikipedia.org/wiki/PHPDoc
.. _`PSR-0`: http://www.php-fig.org/psr/psr-0/
.. _`PSR-4`: http://www.php-fig.org/psr/psr-4/
.. _`Semantic Versioning Standard`: http://semver.org/
.. _`Packagist`: https://packagist.org/