diff --git a/book/doctrine.rst b/book/doctrine.rst index e9d09d5b784..4db46fe69dd 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -363,6 +363,8 @@ see the :ref:`book-doctrine-field-types` section. class Product // ... +.. _book-doctrine-generating-getters-and-setters: + Generating Getters and Setters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -425,6 +427,8 @@ mapping information) of a bundle or an entire namespace: The getters and setters are generated here only because you'll need them to interact with your PHP object. +.. _book-doctrine-creating-the-database-tables-schema: + Creating the Database Tables/Schema ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst index 04301af5048..1145dd7b603 100644 --- a/components/console/helpers/tablehelper.rst +++ b/components/console/helpers/tablehelper.rst @@ -48,8 +48,8 @@ You can also control table rendering by setting custom rendering option values: * :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPaddingChar` * :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setHorizontalBorderChar` * :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVerticalBorderChar` -* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVrossingChar` -* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVellHeaderFormat` -* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVellRowFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCrossingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCellHeaderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCellRowFormat` * :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setBorderFormat` * :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPadType` diff --git a/components/intl.rst b/components/intl.rst index 8bc29c168c5..b19a3d20080 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -72,7 +72,7 @@ expose them manually by adding the following lines to your autoload code:: but usually Composer does this for you automatically: * 1.0.*: when the intl extension is not available - * 1.1.*: when intl is compiled with ICU 4.0 or higher + * 1.1.*: when intl is compiled with ICU 3.8 or higher * 1.2.*: when intl is compiled with ICU 4.4 or higher These versions are important when you deploy your application to a **server with diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 80fc38ef3b6..9825a01e7a5 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -75,18 +75,30 @@ Below is the schedule for the first few versions that use this release model: This results in very predictable dates and maintenance periods: + ++---------+---------+---------------------+-------------+ | Version | Release | End of Maintenance | End of Life | -| ------- | ------- | ------------------- | ----------- | ++=========+=========+=====================+=============+ | 2.0 | 07/2011 | 03/2013 (20 months) | 09/2013 | ++---------+---------+---------------------+-------------+ | 2.1 | 09/2012 | 05/2013 (9 months) | 11/2013 | ++---------+---------+---------------------+-------------+ | 2.2 | 03/2013 | 11/2013 (8 months) | 05/2014 | ++---------+---------+---------------------+-------------+ | **2.3** | 05/2013 | 05/2016 (36 months) | 05/2017 | ++---------+---------+---------------------+-------------+ | 2.4 | 11/2013 | 07/2014 (8 months) | 01/2015 | ++---------+---------+---------------------+-------------+ | 2.5 | 05/2014 | 01/2015 (8 months) | 07/2016 | ++---------+---------+---------------------+-------------+ | 2.6 | 11/2014 | 07/2015 (8 months) | 01/2016 | ++---------+---------+---------------------+-------------+ | **2.7** | 05/2015 | 05/2018 (36 months) | 05/2019 | ++---------+---------+---------------------+-------------+ | 2.8 | 11/2015 | 07/2016 (8 months) | 01/2017 | ++---------+---------+---------------------+-------------+ | ... | ... | ... | ... | ++---------+---------+---------------------+-------------+ .. tip:: diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index be9f36f897b..a1ecdd6e91e 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -167,21 +167,6 @@ interface forces the class to implement the five following methods: For more details on each of these, see :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`. -.. code-block:: php - - // src/Acme/UserBundle/Entity/User.php - - namespace Acme\UserBundle\Entity; - - use Symfony\Component\Security\Core\User\EquatableInterface; - - // ... - - public function isEqualTo(UserInterface $user) - { - return $this->id === $user->getId(); - } - .. note:: The :phpclass:`Serializable` interface and its ``serialize`` and ``unserialize`` @@ -191,24 +176,32 @@ For more details on each of these, see :class:`Symfony\\Component\\Security\\Cor because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` method reloads the user on each request by using the ``id``. -Below is an export of my ``User`` table from MySQL. For details on how to -create user records and encode their password, see :ref:`book-security-encoding-user-password`. +.. tip:: + + To generate missing setters and getters for your ``User`` entity, you + can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``. + For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`. + +Below is an export of my ``User`` table from MySQL with user `admin` +and password `admin`. For details on how to create user records and +encode their password, see :ref:`book-security-encoding-user-password`. .. code-block:: bash - $ mysql> select * from user; - +----+----------+----------------------------------+------------------------------------------+--------------------+-----------+ - | id | username | salt | password | email | is_active | - +----+----------+----------------------------------+------------------------------------------+--------------------+-----------+ - | 1 | hhamon | 7308e59b97f6957fb42d66f894793079 | 09610f61637408828a35d7debee5b38a8350eebe | hhamon@example.com | 1 | - | 2 | jsmith | ce617a6cca9126bf4036ca0c02e82dee | 8390105917f3a3d533815250ed7c64b4594d7ebf | jsmith@example.com | 1 | - | 3 | maxime | cd01749bb995dc658fa56ed45458d807 | 9764731e5f7fb944de5fd8efad4949b995b72a3c | maxime@example.com | 0 | - | 4 | donald | 6683c2bfd90c0426088402930cadd0f8 | 5c3bcec385f59edcc04490d1db95fdb8673bf612 | donald@example.com | 1 | - +----+----------+----------------------------------+------------------------------------------+--------------------+-----------+ - 4 rows in set (0.00 sec) - -The database now contains four users with different usernames, emails and -statuses. The next part will focus on how to authenticate one of these users + $ mysql> select * from acme_users; + +----+----------+------+------------------------------------------+--------------------+-----------+ + | id | username | salt | password | email | is_active | + +----+----------+------+------------------------------------------+--------------------+-----------+ + | 1 | admin | | d033e22ae348aeb5660fc2140aec35850c4da997 | admin@example.com | 1 | + +----+----------+------+------------------------------------------+--------------------+-----------+ + +.. tip:: + + To generate database table from your ``User`` entity, you can run + ``php app/console doctrine:schema:update --force``. + For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`. + +The next part will focus on how to authenticate one of these users thanks to the Doctrine entity user provider and a couple of lines of configuration. @@ -323,9 +316,8 @@ entity user provider to load User entity objects from the database by using the ``username`` unique field. In other words, this tells Symfony how to fetch the user from the database before checking the password validity. -This code and configuration works but it's not enough to secure the application -for **active** users. As of now, you can still authenticate with ``maxime``. The -next section explains how to forbid non active users. +This code is not enough to secure the application for **active** users. +The next section explains how to forbid non active users. Forbid non Active Users ----------------------- @@ -355,10 +347,10 @@ For this example, the first three methods will return ``true`` whereas the // src/Acme/UserBundle/Entity/User.php namespace Acme\UserBundle\Entity; - // ... + use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\AdvancedUserInterface; - class User implements AdvancedUserInterface + class User implements AdvancedUserInterface, \Serializable { // ... @@ -383,10 +375,8 @@ For this example, the first three methods will return ``true`` whereas the } } -If you try to authenticate as ``maxime``, the access is now forbidden as this -user does not have an enabled account. The next session will focus on how -to write a custom entity provider to authenticate a user with his username -or his email address. +The next session will focus on how to write a custom entity provider +to authenticate a user with his username or his email address. Authenticating Someone with a Custom Entity Provider ---------------------------------------------------- @@ -428,8 +418,7 @@ The code below shows the implementation of the ->where('u.username = :username OR u.email = :email') ->setParameter('username', $username) ->setParameter('email', $username) - ->getQuery() - ; + ->getQuery(); try { // The Query::getSingleResult() method throws an exception @@ -537,10 +526,11 @@ about in this section. authenticated at all. In this example, the ``AcmeUserBundle:User`` entity class defines a -many-to-many relationship with a ``AcmeUserBundle:Group`` entity class. A user -can be related to several groups and a group can be composed of one or -more users. As a group is also a role, the previous ``getRoles()`` method now -returns the list of related groups:: +many-to-many relationship with a ``AcmeUserBundle:Role`` entity class. +A user can be related to several roles and a role can be composed of +one or more users. The previous ``getRoles()`` method now returns +the list of related roles. +Notice that methods ``__construcotor()`` and ``getRoles()`` had changed:: // src/Acme/UserBundle/Entity/User.php namespace Acme\UserBundle\Entity; @@ -550,63 +540,46 @@ returns the list of related groups:: class User implements AdvancedUserInterface, \Serializable { + //... + /** - * @ORM\ManyToMany(targetEntity="Group", inversedBy="users") + * @ORM\ManyToMany(targetEntity="Role", inversedBy="users") * */ - private $groups; + private $roles; public function __construct() { - $this->groups = new ArrayCollection(); + $this->roles = new ArrayCollection(); } - // ... - public function getRoles() { - return $this->groups->toArray(); - } - - /** - * @see \Serializable::serialize() - */ - public function serialize() - { - return serialize(array( - $this->id, - )); + return $this->roles->toArray(); } + + // ... - /** - * @see \Serializable::unserialize() - */ - public function unserialize($serialized) - { - list ( - $this->id, - ) = unserialize($serialized); - } } -The ``AcmeUserBundle:Group`` entity class defines three table fields (``id``, +The ``AcmeUserBundle:Role`` entity class defines three table fields (``id``, ``name`` and ``role``). The unique ``role`` field contains the role name used by the Symfony security layer to secure parts of the application. The most -important thing to notice is that the ``AcmeUserBundle:Group`` entity class +important thing to notice is that the ``AcmeUserBundle:Role`` entity class extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`:: - // src/Acme/Bundle/UserBundle/Entity/Group.php + // src/Acme/Bundle/UserBundle/Entity/Role.php namespace Acme\UserBundle\Entity; - use Symfony\Component\Security\Core\Role\Role; + use Symfony\Component\Security\Core\Role\RoleInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Table(name="acme_groups") + * @ORM\Table(name="acme_roles") * @ORM\Entity() */ - class Group extends Role + class Role implements RoleInterface { /** * @ORM\Column(name="id", type="integer") @@ -626,7 +599,7 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`:: private $role; /** - * @ORM\ManyToMany(targetEntity="User", mappedBy="groups") + * @ORM\ManyToMany(targetEntity="User", mappedBy="roles") */ private $users; @@ -635,8 +608,6 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`:: $this->users = new ArrayCollection(); } - // ... getters and setters for each property - /** * @see RoleInterface */ @@ -644,12 +615,20 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`:: { return $this->role; } + + // ... getters and setters for each property } -To improve performances and avoid lazy loading of groups when retrieving a user -from the custom entity provider, the best solution is to join the groups +.. tip:: + + To generate missing setters and getters for your ``Role`` entity, you + can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``. + For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`. + +To improve performances and avoid lazy loading of roles when retrieving a user +from the custom entity provider, the best solution is to join the roles relationship in the ``UserRepository::loadUserByUsername()`` method. This will -fetch the user and his associated roles / groups with a single query:: +fetch the user and his associated roles with a single query:: // src/Acme/UserBundle/Entity/UserRepository.php namespace Acme\UserBundle\Entity; @@ -662,8 +641,8 @@ fetch the user and his associated roles / groups with a single query:: { $q = $this ->createQueryBuilder('u') - ->select('u, g') - ->leftJoin('u.groups', 'g') + ->select('u, r') + ->leftJoin('u.roles', 'r') ->where('u.username = :username OR u.email = :email') ->setParameter('username', $username) ->setParameter('email', $username) @@ -675,6 +654,29 @@ fetch the user and his associated roles / groups with a single query:: // ... } -The ``QueryBuilder::leftJoin()`` method joins and fetches related groups from +The ``QueryBuilder::leftJoin()`` method joins and fetches related roles from the ``AcmeUserBundle:User`` model class when a user is retrieved with his email address or username. + +To re-generate all database tables, you can run ``php app/console doctrine:schema:update --force``. +This will also create additional table ``user_role`` what holds +relations between users and roles. +For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`. + +Below is an export of my ``Roles`` and ``user_role`` tables from MySQL: + +.. code-block:: bash + + $ mysql> select * from acme_users; + +----+-------+------------+ + | id | name | role | + +----+-------+------------+ + | 1 | admin | ROLE_ADMIN | + +----+-------+------------+ + + mysql> select * from user_role; + +---------+---------+ + | user_id | role_id | + +---------+---------+ + | 1 | 1 | + +---------+---------+