diff --git a/book/propel.rst b/book/propel.rst index e28fb6c5d40..410656289cf 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -4,529 +4,16 @@ Databases and Propel ==================== -One of the most common and challenging tasks for any application -involves persisting and reading information to and from a database. Symfony -does not come integrated with any ORMs but the Propel integration is easy. -To install Propel, read `Working With Symfony2`_ on the Propel documentation. - -A Simple Example: A Product ---------------------------- - -In this section, you'll configure your database, create a ``Product`` object, -persist it to the database and fetch it back out. - -Configuring the Database -~~~~~~~~~~~~~~~~~~~~~~~~ - -Before you can start, you'll need to configure your database connection -information. By convention, this information is usually configured in an -``app/config/parameters.yml`` file: - -.. code-block:: yaml - - # app/config/parameters.yml - parameters: - database_driver: mysql - database_host: localhost - database_name: test_project - database_user: root - database_password: password - database_charset: UTF8 - -These parameters defined in ``parameters.yml`` can now be included in the -configuration file (``config.yml``): - -.. code-block:: yaml - - propel: - dbal: - driver: "%database_driver%" - user: "%database_user%" - password: "%database_password%" - dsn: "%database_driver%:host=%database_host%;dbname=%database_name%;charset=%database_charset%" - -.. note:: - - Defining the configuration via ``parameters.yml`` is a - :ref:`Symfony Framework Best Practice `, - feel free to do it differently if that suits your application better. - -Now that Propel knows about your database, it can create the database for -you: - -.. code-block:: bash - - $ php app/console propel:database:create - -.. note:: - - In this example, you have one configured connection, named ``default``. If - you want to configure more than one connection, read the - `PropelBundle configuration section`_. - -Creating a Model Class -~~~~~~~~~~~~~~~~~~~~~~ - -In the Propel world, ActiveRecord classes are known as **models** because classes -generated by Propel contain some business logic. - -.. note:: - - For people who use Symfony with Doctrine2, **models** are equivalent to - **entities**. - -Suppose you're building an application where products need to be displayed. -First, create a ``schema.xml`` file inside the ``Resources/config`` directory -of your AppBundle: - -.. code-block:: xml - - - - - - - - - - - - -
-
- -Building the Model -~~~~~~~~~~~~~~~~~~ - -After creating your ``schema.xml``, generate your model from it by running: - -.. code-block:: bash - - $ php app/console propel:model:build - -This generates each model class to quickly develop your application in the -``Model/`` directory of the AppBundle bundle. - -Creating the Database Tables/Schema -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now you have a usable ``Product`` class and all you need to persist it. Of -course, you don't yet have the corresponding ``product`` table in your -database. Fortunately, Propel can automatically create all the database tables -needed for every known model in your application. To do this, run: - -.. code-block:: bash - - $ php app/console propel:sql:build - $ php app/console propel:sql:insert --force - -Your database now has a fully-functional ``product`` table with columns that -match the schema you've specified. - -.. tip:: - - You can run the last three commands combined by using the following - command: - - .. code-block:: bash - - $ php app/console propel:build --insert-sql - -Persisting Objects to the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that you have a ``Product`` object and corresponding ``product`` table, -you're ready to persist data to the database. From inside a controller, this -is pretty easy. Add the following method to the ``ProductController`` of the -bundle:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createAction() - { - $product = new Product(); - $product->setName('A Foo Bar'); - $product->setPrice(19.99); - $product->setDescription('Lorem ipsum dolor'); - - $product->save(); - - return new Response('Created product id '.$product->getId()); - } - } - -In this piece of code, you instantiate and work with the ``$product`` object. -When you call the ``save()`` method on it, you persist it to the database. No -need to use other services, the object knows how to persist itself. - -.. note:: - - If you're following along with this example, you'll need to create a - :doc:`route ` that points to this action to see it in action. - -Fetching Objects from the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Fetching an object back from the database is even easier. For example, suppose -you've configured a route to display a specific ``Product`` based on its ``id`` -value:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function showAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - // ... do something, like pass the $product object into a template - } - } - -Updating an Object -~~~~~~~~~~~~~~~~~~ - -Once you've fetched an object from Propel, updating it is easy. Suppose you -have a route that maps a product id to an update action in a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function updateAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - $product->setName('New product name!'); - $product->save(); - - return $this->redirectToRoute('homepage'); - } - } - -Updating an object involves just three steps: - -#. fetching the object from Propel (line 12 - 18); -#. modifying the object (line 20); -#. saving it (line 21). - -Deleting an Object -~~~~~~~~~~~~~~~~~~ - -Deleting an object is very similar to updating, but requires a call to the -``delete()`` method on the object:: - - $product->delete(); - -Querying for Objects --------------------- - -Propel provides generated ``Query`` classes to run both basic and complex queries -without any work:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create()->findPk($id); - - ProductQuery::create() - ->filterByName('Foo') - ->findOne(); - -Imagine that you want to query for products which cost more than 19.99, ordered -from cheapest to most expensive. From inside a controller, do the following:: - - use AppBundle\Model\ProductQuery; - // ... - - $products = ProductQuery::create() - ->filterByPrice(array('min' => 19.99)) - ->orderByPrice() - ->find(); - -In one line, you get your products in a powerful object-oriented way. No need -to waste your time with SQL or whatever, Symfony offers fully object-oriented -programming and Propel respects the same philosophy by providing an awesome -abstraction layer. - -If you want to reuse some queries, you can add your own methods to the -``ProductQuery`` class:: - - // src/AppBundle/Model/ProductQuery.php - - // ... - class ProductQuery extends BaseProductQuery - { - public function filterByExpensivePrice() - { - return $this->filterByPrice(array( - 'min' => 1000, - )); - } - } - -However, note that Propel generates a lot of methods for you and a simple -``findAllOrderedByName()`` can be written without any effort:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create() - ->orderByName() - ->find(); - -Relationships/Associations --------------------------- - -Suppose that the products in your application all belong to exactly one -"category". In this case, you'll need a ``Category`` object and a way to relate -a ``Product`` object to a ``Category`` object. - -Start by adding the ``category`` definition in your ``schema.xml``: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - -
- - - - - -
-
- -Create the classes: - -.. code-block:: bash - - $ php app/console propel:model:build - -Assuming you have products in your database, you don't want to lose them. Thanks to -migrations, Propel will be able to update your database without losing existing -data. - -.. code-block:: bash - - $ php app/console propel:migration:generate-diff - $ php app/console propel:migration:migrate - -Your database has been updated, you can continue writing your application. - -Saving Related Objects -~~~~~~~~~~~~~~~~~~~~~~ - -Now, try the code in action. Imagine you're inside a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Category; - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createProductAction() - { - $category = new Category(); - $category->setName('Main Products'); - - $product = new Product(); - $product->setName('Foo'); - $product->setPrice(19.99); - // relate this product to the category - $product->setCategory($category); - - // save the whole - $product->save(); - - return new Response( - 'Created product id: '.$product->getId().' and category id: '.$category->getId() - ); - } - } - -Now, a single row is added to both the ``category`` and ``product`` tables. The -``product.category_id`` column for the new product is set to whatever the id is -of the new category. Propel manages the persistence of this relationship for -you. - -Fetching Related Objects -~~~~~~~~~~~~~~~~~~~~~~~~ - -When you need to fetch associated objects, your workflow looks just like it did -before: Fetch a ``$product`` object and then access its related ``Category``:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - public function showAction($id) - { - $product = ProductQuery::create() - ->joinWithCategory() - ->findPk($id); - - $categoryName = $product->getCategory()->getName(); - - // ... - } - } - -Note, in the above example, only one query was made. - -More Information on Associations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You will find more information on relations by reading the dedicated chapter on -`Relationships`_. - -Lifecycle Callbacks -------------------- - -Sometimes, you need to perform an action right before or after an object is -inserted, updated, or deleted. These types of actions are known as "lifecycle" -callbacks or "hooks", as they're callback methods that you need to execute -during different stages of the lifecycle of an object (e.g. the object is -inserted, updated, deleted, etc). - -To add a hook, just add a new method to the object class:: - - // src/AppBundle/Model/Product.php - - // ... - class Product extends BaseProduct - { - public function preInsert(\PropelPDO $con = null) - { - // ... do something before the object is inserted - } - } - -Propel provides the following hooks: - -``preInsert()`` - Code executed before insertion of a new object. -``postInsert()`` - Code executed after insertion of a new object. -``preUpdate()`` - Code executed before update of an existing object. -``postUpdate()`` - Code executed after update of an existing object. -``preSave()`` - Code executed before saving an object (new or existing). -``postSave()`` - Code executed after saving an object (new or existing). -``preDelete()`` - Code executed before deleting an object. -``postDelete()`` - Code executed after deleting an object. - -Behaviors ---------- - -All bundled behaviors in Propel are working with Symfony. To get more -information about how to use Propel behaviors, look at the -`Behaviors reference section`_. - -Commands --------- - -You should read the dedicated section for `Propel commands in Symfony2`_. - -.. _`Working With Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#installation -.. _`PropelBundle configuration section`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#configuration -.. _`Relationships`: http://propelorm.org/Propel/documentation/04-relationships.html -.. _`Behaviors reference section`: http://propelorm.org/Propel/documentation/#behaviors-reference -.. _`Propel commands in Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2#the-commands +Propel is an open-source Object-Relational Mapping (ORM) for PHP which +implements the `ActiveRecord pattern`_. It allows you to access your database +using a set of objects, providing a simple API for storing and retrieving data. +Propel uses PDO as an abstraction layer and code generation to remove the +burden of runtime introspection. + +A few years ago, Propel was a very popular alternative to Doctrine. However, its +popularity has rapidly declined and that's why the Symfony book no longer includes +the Propel documentation. Read the `official PropelBundle documentation`_ to learn +how to integrate Propel into your Symfony projects. + +.. _`ActiveRecord pattern`: https://en.wikipedia.org/wiki/Active_record_pattern +.. _`official PropelBundle documentation`: https://github.com/propelorm/PropelBundle/blob/1.4/Resources/doc/index.markdown diff --git a/components/serializer.rst b/components/serializer.rst index 8895f7c9459..14512852b91 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -33,8 +33,12 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/serializer`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/Serializer). + .. include:: /components/require_autoload.rst.inc +To use the ``ObjectNormalizer``, the :doc:`PropertyAccess component ` +must also be installed. + Usage ----- @@ -45,18 +49,18 @@ which Encoders and Normalizer are going to be available:: use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $encoders = array(new XmlEncoder(), new JsonEncoder()); - $normalizers = array(new GetSetMethodNormalizer()); + $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); -There are several normalizers available, e.g. the -:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` or -the :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`. +The preferred normalizer is the +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`, but other +normalizers are available. To read more about them, refer to the `Normalizers`_ section of this page. All -the examples shown below use the ``GetSetMethodNormalizer``. +the examples shown below use the ``ObjectNormalizer``. Serializing an Object --------------------- @@ -147,6 +151,30 @@ needs three parameters: #. The name of the class this information will be decoded to #. The encoder used to convert that information into an array +Deserializing in an Existing Object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The serializer can also be used to update an existing object:: + + $person = new Acme\Person(); + $person->setName('bar'); + $person->setAge(99); + $person->setSportsman(true); + + $data = << + foo + 69 + + EOF; + + $serializer->deserialize($data, 'Acme\Person', 'xml', array('object_to_populate' => $person)); + // $obj2 = Acme\Person(name: 'foo', age: '99', sportsman: true) + +This is a common need when working with an ORM. + +.. _component-serializer-attributes-groups: + Attributes Groups ----------------- @@ -200,6 +228,8 @@ like the following:: // For YAML // $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yml')); +.. _component-serializer-attributes-groups-annotations: + Then, create your groups definition: .. configuration-block:: @@ -285,8 +315,13 @@ You are now able to serialize only attributes in the groups you want:: Ignoring Attributes ------------------- +.. note:: + + Using attribute groups instead of the :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` + method is considered best practice. + .. versionadded:: 2.3 - The :method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` + The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` method was introduced in Symfony 2.3. .. versionadded:: 2.7 @@ -295,14 +330,14 @@ Ignoring Attributes As an option, there's a way to ignore attributes from the origin object. To remove those attributes use the -:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` +:method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes` method on the normalizer definition:: use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - $normalizer = new GetSetMethodNormalizer(); + $normalizer = new ObjectNormalizer(); $normalizer->setIgnoredAttributes(array('age')); $encoder = new JsonEncoder(); @@ -359,11 +394,11 @@ including :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormal and :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`:: use Symfony\Component\Serializer\Encoder\JsonEncoder - use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; $nameConverter = new OrgPrefixNameConverter(); - $normalizer = new PropertyNormalizer(null, $nameConverter); + $normalizer = new ObjectNormalizer(null, $nameConverter); $serializer = new Serializer(array($normalizer), array(new JsonEncoder())); @@ -394,9 +429,9 @@ snake_case and CamelCased styles during serialization and deserialization processes:: use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - $normalizer = new GetSetMethodNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); + $normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); class Person { @@ -427,6 +462,9 @@ If you are using isser methods (methods prefixed by ``is``, like ``Acme\Person::isSportsman()``), the Serializer component will automatically detect and use it to serialize related attributes. +The ``ObjectNormalizer`` also takes care of methods starting with ``has``, ``add`` +and ``remove``. + Using Callbacks to Serialize Properties with Object Instances ------------------------------------------------------------- @@ -463,23 +501,42 @@ Normalizers There are several types of normalizers available: +:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + This normalizer leverages the :doc:`PropertyAccess Component ` + to read and write in the object. It means that it can access to properties + directly and through getters, setters, hassers, adders and removers. It supports + calling the constructor during the denormalization process. + + Objects are normalized to a map of property names (method name stripped of + the "get"/"set"/"has"/"remove" prefix and converted to lower case) to property + values. + + The ``ObjectNormalizer`` is the most powerful normalizer. It is a configured + by default when using the Symfony Standard Edition with the serializer enabled. + :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` This normalizer reads the content of the class by calling the "getters" (public methods starting with "get"). It will denormalize data by calling the constructor and the "setters" (public methods starting with "set"). - Objects are serialized to a map of property names (method name stripped of + Objects are normalized to a map of property names (method name stripped of the "get" prefix and converted to lower case) to property values. :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` This normalizer directly reads and writes public properties as well as - **private and protected** properties. Objects are normalized to a map of - property names to property values. + **private and protected** properties. It supports calling the constructor + during the denormalization process. + + Objects are normalized to a map of property names to property values. -.. versionadded:: 2.6 The - :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` +.. versionadded:: 2.6 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` class was introduced in Symfony 2.6. +.. versionadded:: 2.7 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + class was introduced in Symfony 2.7. + Handling Circular References ---------------------------- @@ -565,7 +622,7 @@ by custom callables. This is especially useful when serializing entities having unique identifiers:: $encoder = new JsonEncoder(); - $normalizer = new GetSetMethodNormalizer(); + $normalizer = new ObjectNormalizer(); $normalizer->setCircularReferenceHandler(function ($object) { return $object->getName(); diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 48864f6992b..2168129d7fa 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -102,6 +102,10 @@ Structure * Always use `identical comparison`_ unless you need type juggling; +* Use `Yoda conditions`_ when checking a variable against an expression to avoid + an accidental assignment inside the condition statement (this applies to ``==``, + ``!=``, ``===``, and ``!==``); + * Add a comma after each array item in a multi-line array, even after the last one; @@ -189,3 +193,4 @@ License .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`PSR-4`: http://www.php-fig.org/psr/psr-4/ .. _`identical comparison`: https://php.net/manual/en/language.operators.comparison.php +.. _`Yoda conditions`: https://en.wikipedia.org/wiki/Yoda_conditions diff --git a/cookbook/request/load_balancer_reverse_proxy.rst b/cookbook/request/load_balancer_reverse_proxy.rst index 60c8d23ff0d..a57bf88f0f2 100644 --- a/cookbook/request/load_balancer_reverse_proxy.rst +++ b/cookbook/request/load_balancer_reverse_proxy.rst @@ -7,9 +7,14 @@ an AWS Elastic Load Balancer) or a reverse proxy (e.g. Varnish for For the most part, this doesn't cause any problems with Symfony. But, when a request passes through a proxy, certain request information is sent using -special ``X-Forwarded-*`` headers. For example, instead of reading the ``REMOTE_ADDR`` -header (which will now be the IP address of your reverse proxy), the user's -true IP will be stored in an ``X-Forwarded-For`` header. +either the standard ``Forwarded`` header or non-standard special ``X-Forwarded-*`` +headers. For example, instead of reading the ``REMOTE_ADDR`` header (which +will now be the IP address of your reverse proxy), the user's true IP will be +stored in a standard ``Forwarded: for="..."`` header or a non standard +``X-Forwarded-For`` header. + +.. versionadded:: 2.7 + ``Forwarded`` header support was introduced in Symfony 2.7. If you don't configure Symfony to look for these headers, you'll get incorrect information about the client's IP address, whether or not the client is connecting @@ -57,9 +62,9 @@ the IP address ``192.0.0.1`` or matches the range of IP addresses that use the CIDR notation ``10.0.0.0/8``. For more details, see the :ref:`framework.trusted_proxies ` option. -That's it! Symfony will now look for the correct ``X-Forwarded-*`` headers -to get information like the client's IP address, host, port and whether or -not the request is using HTTPS. +That's it! Symfony will now look for the correct headers to get information +like the client's IP address, host, port and whether the request is +using HTTPS. But what if the IP of my Reverse Proxy Changes Constantly! ---------------------------------------------------------- @@ -93,9 +98,14 @@ other information. My Reverse Proxy Uses Non-Standard (not X-Forwarded) Headers ------------------------------------------------------------ -Most reverse proxies store information on specific ``X-Forwarded-*`` headers. -But if your reverse proxy uses non-standard header names, you can configure +Although `RFC 7239`_ recently defined a standard ``Forwarded`` header to disclose +all proxy information, most reverse proxies store information in non-standard +``X-Forwarded-*`` headers. + +But if your reverse proxy uses other non-standard header names, you can configure these (see ":doc:`/components/http_foundation/trusting_proxies`"). + The code for doing this will need to live in your front controller (e.g. ``web/app.php``). .. _`security groups`: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-security-groups.html +.. _`RFC 7239`: http://tools.ietf.org/html/rfc7239 diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index 2e6775b6c22..505d19d408e 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -152,3 +152,63 @@ setting: ), ), )); + +Events +------ + +The firewall dispatches the ``security.switch_user`` event right after the impersonation +is completed. The :class:`Symfony\\Component\\Security\\Http\\Event\\SwitchUserEvent` is +passed to the listener, and you can use this to get the user that you are now impersonating. + +The cookbook article about +:doc:`Making the Locale "Sticky" during a User's Session ` +does not update the locale when you impersonate a user. The following code sample will show +how to change the sticky locale: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + app.switch_user_listener: + class: AppBundle\EventListener\SwitchUserListener + tags: + - { name: kernel.event_listener, event: security.switch_user, method: onSwitchUser } + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/services.php + $container + ->register('app.switch_user_listener', 'AppBundle\EventListener\SwitchUserListener') + ->addTag('kernel.event_listener', array('event' => 'security.switch_user', 'method' => 'onSwitchUser')) + ; + +.. caution:: + + The listener implementation assumes your ``User`` entity has a ``getLocale()`` method. + +.. code-block:: php + + // src/AppBundle/EventListener/SwitchUserListener.pnp + namespace AppBundle\EventListener; + + use Symfony\Component\Security\Http\Event\SwitchUserEvent; + + class SwitchUserListener + { + public function onSwitchUser(SwitchUserEvent $event) + { + $event->getRequest()->getSession()->set( + '_locale', + $event->getTargetUser()->getLocale() + ); + } + } diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 8ba2b68a0da..4c0d40f5554 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -16,11 +16,16 @@ the session lasts using a cookie with the ``remember_me`` firewall option: # app/config/security.yml firewalls: - main: + default: + # ... remember_me: key: "%secret%" lifetime: 604800 # 1 week in seconds path: / + # by default, the feature is enabled by checking a + # checkbox in the login form (see below), uncomment the + # below lines to always enable it. + #always_remember_me: true .. code-block:: xml @@ -33,12 +38,16 @@ the session lasts using a cookie with the ``remember_me`` firewall option: http://symfony.com/schema/dic/services/services-1.0.xsd"> - - + + + + + path = "/" /> @@ -49,11 +58,16 @@ the session lasts using a cookie with the ``remember_me`` firewall option: // app/config/security.php $container->loadFromExtension('security', array( 'firewalls' => array( - 'main' => array( + 'default' => array( + // ... 'remember_me' => array( 'key' => '%secret%', 'lifetime' => 604800, // 1 week in seconds 'path' => '/', + // by default, the feature is enabled by checking a + // checkbox in the login form (see below), uncomment + // the below lines to always enable it. + //'always_remember_me' => true, ), ), ), @@ -103,21 +117,30 @@ The ``remember_me`` firewall defines the following configuration options: "Remember Me" feature is always enabled, regardless of the desire of the end user. +``token_provider`` (default value: ``null``) + Defines the service id of a token provider to use. By default, tokens are + stored in a cookie. For example, you might want to store the token in a + database, to not have a (hashed) version of the password in a cookie. The + DoctrineBridge comes with a + ``Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider`` that + you can use. + Forcing the User to Opt-Out of the Remember Me Feature ------------------------------------------------------ It's a good idea to provide the user with the option to use or not use the remember me functionality, as it will not always be appropriate. The usual way of doing this is to add a checkbox to the login form. By giving the checkbox -the name ``_remember_me``, the cookie will automatically be set when the checkbox -is checked and the user successfully logs in. So, your specific login form -might ultimately look like this: +the name ``_remember_me`` (or the name you configured using ``remember_me_parameter``), +the cookie will automatically be set when the checkbox is checked and the user +successfully logs in. So, your specific login form might ultimately look like +this: .. configuration-block:: .. code-block:: html+jinja - {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} + {# app/Resources/views/security/login.html.twig #} {% if error %}
{{ error.message }}
{% endif %} @@ -137,7 +160,7 @@ might ultimately look like this: .. code-block:: html+php - +
getMessage() ?>
@@ -159,7 +182,7 @@ might ultimately look like this: The user will then automatically be logged in on subsequent visits while the cookie remains valid. -Forcing the User to Re-authenticate before Accessing certain Resources +Forcing the User to Re-Authenticate before Accessing certain Resources ---------------------------------------------------------------------- When the user returns to your site, they are authenticated automatically based diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst index 1f578cd3738..2e12c6cf641 100644 --- a/cookbook/serializer.rst +++ b/cookbook/serializer.rst @@ -11,8 +11,6 @@ tools that you can leverage for your solution. In fact, before you start, get familiar with the serializer, normalizers and encoders by reading the :doc:`Serializer Component`. -You should also check out the `JMSSerializerBundle`_, which expands on the -functionality offered by Symfony's core serializer. Activating the Serializer ------------------------- @@ -48,23 +46,48 @@ it in your configuration: $container->loadFromExtension('framework', array( // ... 'serializer' => array( - 'enabled' => true + 'enabled' => true, ), )); +Using the Serializer Service +---------------------------- + +Once enabled, the ``serializer`` service can be injected in any service where +you need it or it can be used in a controller like the following:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + class DefaultController extends Controller + { + public function indexAction() + { + $serializer = $this->get('serializer'); + + // ... + } + } + Adding Normalizers and Encoders ------------------------------- +.. versionadded:: 2.7 + The :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` + is enabled by default in Symfony 2.7. In prior versions, you needed to load + your own normalizer. + Once enabled, the ``serializer`` service will be available in the container and will be loaded with two :ref:`encoders` (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and -:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) -but no :ref:`normalizers`, meaning you'll -need to load your own. +:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) and the +:ref:`ObjectNormalizer normalizer `. You can load normalizers and/or encoders by tagging them as -:ref:`serializer.normalizer` and -:ref:`serializer.encoder`. It's also +:ref:`serializer.normalizer ` and +:ref:`serializer.encoder `. It's also possible to set the priority of the tag in order to decide the matching order. Here is an example on how to load the @@ -101,4 +124,104 @@ Here is an example on how to load the $definition->addTag('serializer.normalizer'); $container->setDefinition('get_set_method_normalizer', $definition); -.. _JMSSerializerBundle: http://jmsyst.com/bundles/JMSSerializerBundle +Using Serialization Groups Annotations +-------------------------------------- + +.. versionadded:: 2.7 + Support for serialization groups was introduced in Symfony 2.7. + +Enable :ref:`serialization groups annotation ` +with the following configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + # ... + serializer: + enable_annotations: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'enable_annotations' => true, + ), + )); + +Next, add the :ref:`@Groups annotations ` +to your class and choose which groups to use when serializing:: + + $serializer = $this->get('serializer'); + $json = $serializer->serialize( + $someObject, + 'json', array('groups' => array('group1') + ); + +Enabling the Metadata Cache +--------------------------- + +.. versionadded:: 2.7 + Serializer was introduced in Symfony 2.7. + +Metadata used by the Serializer component such as groups can be cached to +enhance application performance. Any service implementing the ``Doctrine\Common\Cache\Cache`` +interface can be used. + +A service leveraging `APCu`_ (and APC for PHP < 5.5) is built-in. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config_prod.yml + framework: + # ... + serializer: + cache: serializer.mapping.cache.apc + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config_prod.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'cache' => 'serializer.mapping.cache.apc', + ), + )); + +Going Further with the Serializer Component +------------------------------------------- + +`DunglasApiBundle`_ provides an API system supporting `JSON-LD`_ and `Hydra Core Vocabulary`_ +hypermedia formats. It is built on top of the Symfony Framework and its Serializer +component. It provides custom normalizers and a custom encoder, custom metadata +and a caching system. + +If you want to leverage the full power of the Symfony Serializer component, +take a look at how this bundle works. + +.. _`APCu`: https://github.com/krakjoe/apcu +.. _`DunglasApiBundle`: https://github.com/dunglas/DunglasApiBundle +.. _`JSON-LD`: http://json-ld.org +.. _`Hydra Core Vocabulary`: http://hydra-cg.com diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 68179e35ef0..b1c3d8ceff4 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -262,7 +262,7 @@ you use PHPstorm on the Mac OS platform, you will do something like: .. tip:: - If you're on a Windows PC, you can install the `PhpStormOpener`_ to + If you're on a Windows PC, you can install the `PhpStormProtocol`_ to be able to use this. Of course, since every developer uses a different IDE, it's better to set @@ -1627,5 +1627,5 @@ Full Default Configuration .. _`HTTP Host header attacks`: http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html .. _`Security Advisory Blog post`: http://symfony.com/blog/security-releases-symfony-2-0-24-2-1-12-2-2-5-and-2-3-3-released#cve-2013-4752-request-gethost-poisoning .. _`Doctrine Cache`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html -.. _`PhpStormOpener`: https://github.com/pinepain/PhpStormOpener .. _`egulias/email-validator`: https://github.com/egulias/EmailValidator +.. _`PhpStormProtocol`: https://github.com/aik099/PhpStormProtocol diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst index b1066c6b1f4..bf47027494d 100644 --- a/reference/constraints/GreaterThan.rst +++ b/reference/constraints/GreaterThan.rst @@ -4,8 +4,8 @@ GreaterThan .. versionadded:: 2.3 The ``GreaterThan`` constraint was introduced in Symfony 2.3. -Validates that a value is greater than another value, defined in the options. -To force that a value is greater than or equal to another value, see +Validates that a value is greater than another value, defined in the options. To +force that a value is greater than or equal to another value, see :doc:`/reference/constraints/GreaterThanOrEqual`. To force a value is less than another value, see :doc:`/reference/constraints/LessThan`. @@ -24,8 +24,8 @@ than another value, see :doc:`/reference/constraints/LessThan`. Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is greater -than ``18``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is greater than +``18``, you could do the following: .. configuration-block:: @@ -90,6 +90,191 @@ than ``18``, you could do the following: } } +Comparing Dates +--------------- + +.. versionadded:: 2.6 + The feature to compare dates was introduced in Symfony 2.6. + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must at least be the next day: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("today") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today + + .. code-block:: xml + + + + + + + + today + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("today UTC") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: today UTC + + .. code-block:: xml + + + + + + + + today UTC + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that the above delivery date starts at least five hours after the +current time: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThan("+5 hours") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/Acme/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThan: +5 hours + + .. code-block:: xml + + + + + + + + +5 hours + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThan('+5 hours')); + } + } + Options ------- @@ -100,7 +285,9 @@ message **type**: ``string`` **default**: ``This value should be greater than {{ compared_value }}.`` -This is the message that will be shown if the value is not greater than -the comparison value. +This is the message that will be shown if the value is not greater than the +comparison value. .. include:: /reference/constraints/_payload-option.rst.inc + +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst index cc868390490..4d61dd5b077 100644 --- a/reference/constraints/GreaterThanOrEqual.rst +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -4,8 +4,8 @@ GreaterThanOrEqual .. versionadded:: 2.3 The ``GreaterThanOrEqual`` constraint was introduced in Symfony 2.3. -Validates that a value is greater than or equal to another value, defined -in the options. To force that a value is greater than another value, see +Validates that a value is greater than or equal to another value, defined in +the options. To force that a value is greater than another value, see :doc:`/reference/constraints/GreaterThan`. +----------------+----------------------------------------------------------------------------------+ @@ -23,8 +23,8 @@ in the options. To force that a value is greater than another value, see Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is greater -than or equal to ``18``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is greater than +or equal to ``18``, you could do the following: .. configuration-block:: @@ -89,6 +89,191 @@ than or equal to ``18``, you could do the following: } } +Comparing Dates +--------------- + +.. versionadded:: 2.6 + The feature to compare dates was introduced in Symfony 2.6. + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must at least be the current day: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("today") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today + + .. code-block:: xml + + + + + + + + today + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("today UTC") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: today UTC + + .. code-block:: xml + + + + + + + + today UTC + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that the above delivery date starts at least five hours after the +current time: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\GreaterThanOrEqual("+5 hours") + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - GreaterThanOrEqual: +5 hours + + .. code-block:: xml + + + + + + + + +5 hours + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\GreaterThanOrEqual('+5 hours')); + } + } + Options ------- @@ -99,7 +284,9 @@ message **type**: ``string`` **default**: ``This value should be greater than or equal to {{ compared_value }}.`` -This is the message that will be shown if the value is not greater than -or equal to the comparison value. +This is the message that will be shown if the value is not greater than or equal +to the comparison value. .. include:: /reference/constraints/_payload-option.rst.inc + +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst index d14e289fd0c..772f6e629ee 100644 --- a/reference/constraints/LessThan.rst +++ b/reference/constraints/LessThan.rst @@ -4,8 +4,8 @@ LessThan .. versionadded:: 2.3 The ``LessThan`` constraint was introduced in Symfony 2.3. -Validates that a value is less than another value, defined in the options. -To force that a value is less than or equal to another value, see +Validates that a value is less than another value, defined in the options. To +force that a value is less than or equal to another value, see :doc:`/reference/constraints/LessThanOrEqual`. To force a value is greater than another value, see :doc:`/reference/constraints/GreaterThan`. @@ -90,6 +90,190 @@ If you want to ensure that the ``age`` of a ``Person`` class is less than } } +Comparing Dates +--------------- + +.. versionadded:: 2.6 + The feature to compare dates was introduced in Symfony 2.6. + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must be in the past like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("today") + */ + protected $dateOfBirth; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: today + + .. code-block:: xml + + + + + + + + today + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('dateOfBirth', new Assert\LessThan('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("today UTC") + */ + protected $dateOfBirth; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: today UTC + + .. code-block:: xml + + + + + + + + today UTC + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThan('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a person must be at least 18 years old like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan("-18 years") + */ + protected $dateOfBirth; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + dateOfBirth: + - LessThan: -18 years + + .. code-block:: xml + + + + + + + + -18 years + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('dateOfBirth', new Assert\LessThan('-18 years')); + } + } + Options ------- @@ -104,3 +288,5 @@ This is the message that will be shown if the value is not less than the comparison value. .. include:: /reference/constraints/_payload-option.rst.inc + +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst index c75266bbc52..22d0201a429 100644 --- a/reference/constraints/LessThanOrEqual.rst +++ b/reference/constraints/LessThanOrEqual.rst @@ -4,8 +4,8 @@ LessThanOrEqual .. versionadded:: 2.3 The ``LessThanOrEqual`` constraint was introduced in Symfony 2.3. -Validates that a value is less than or equal to another value, defined in -the options. To force that a value is less than another value, see +Validates that a value is less than or equal to another value, defined in the +options. To force that a value is less than another value, see :doc:`/reference/constraints/LessThan`. +----------------+-------------------------------------------------------------------------------+ @@ -23,8 +23,8 @@ the options. To force that a value is less than another value, see Basic Usage ----------- -If you want to ensure that the ``age`` of a ``Person`` class is less than -or equal to ``80``, you could do the following: +If you want to ensure that the ``age`` of a ``Person`` class is less than or +equal to ``80``, you could do the following: .. configuration-block:: @@ -89,6 +89,190 @@ or equal to ``80``, you could do the following: } } +Comparing Dates +--------------- + +.. versionadded:: 2.6 + The feature to compare dates was introduced in Symfony 2.6. + +This constraint can be used to compare ``DateTime`` objects against any date +string `accepted by the DateTime constructor`_. For example, you could check +that a date must be today or in the past like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("today") + */ + protected $age; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: today + + .. code-block:: xml + + + + + + + + today + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('today')); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("today UTC") + */ + protected $age; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: today UTC + + .. code-block:: xml + + + + + + + + today UTC + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('today UTC')); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a person must be at least 18 years old like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual("-18 years") + */ + protected $age; + } + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: -18 years + + .. code-block:: xml + + + + + + + + -18 years + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual('-18 years')); + } + } + Options ------- @@ -99,7 +283,9 @@ message **type**: ``string`` **default**: ``This value should be less than or equal to {{ compared_value }}.`` -This is the message that will be shown if the value is not less than or -equal to the comparison value. +This is the message that will be shown if the value is not less than or equal +to the comparison value. .. include:: /reference/constraints/_payload-option.rst.inc + +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index b588cc31cca..6f4dea3339d 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -99,6 +99,221 @@ you might add the following: } } +Date Ranges +----------- + +This constraint can be used to compare ``DateTime`` objects against date ranges. +The minimum and maximum date of the range should be given as any date string +`accepted by the DateTime constructor`_. For example, you could check that a +date must lie within the current year like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Event.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + /** + * @Assert\Range( + * min = "first day of January", + * max = "first day of January next year" + * ) + */ + protected $startDate; + } + + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January + max: first day of January next year + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/EventBundle/Entity/Event.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('startDate', new Assert\Range(array( + 'min' => 'first day of January', + 'max' => 'first day of January next year', + ))); + } + } + +Be aware that PHP will use the server's configured timezone to interpret these +dates. If you want to fix the timezone, append it to the date string: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Event.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + /** + * @Assert\Range( + * min = "first day of January UTC", + * max = "first day of January next year UTC" + * ) + */ + protected $startDate; + } + + .. code-block:: yaml + + # src/EventBundle/Resources/config/validation.yml + Acme\EventBundle\Entity\Event: + properties: + startDate: + - Range: + min: first day of January UTC + max: first day of January next year UTC + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/EventBundle/Entity/Person.php + namespace Acme\EventBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Event + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('startDate', new Assert\Range(array( + 'min' => 'first day of January UTC', + 'max' => 'first day of January next year UTC', + ))); + } + } + +The ``DateTime`` class also accepts relative dates or times. For example, you +can check that a delivery date starts within the next five hours like this: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\Range( + * min = "now", + * max = "+5 hours" + * ) + */ + protected $deliveryDate; + } + + .. code-block:: yaml + + # src/OrderBundle/Resources/config/validation.yml + Acme\OrderBundle\Entity\Order: + properties: + deliveryDate: + - Range: + min: now + max: +5 hours + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/OrderBundle/Entity/Order.php + namespace Acme\OrderBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('deliveryDate', new Assert\Range(array( + 'min' => 'now', + 'max' => '+5 hours', + ))); + } + } + Options ------- @@ -145,3 +360,4 @@ the `is_numeric`_ PHP function). .. include:: /reference/constraints/_payload-option.rst.inc .. _`is_numeric`: http://www.php.net/manual/en/function.is-numeric.php +.. _`accepted by the DateTime constructor`: http://www.php.net/manual/en/datetime.formats.php