Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8903e23

Browse files
committedMar 4, 2014
Merge branch '2.4'
2 parents cadca3b + 1065855 commit 8903e23

25 files changed

+689
-769
lines changed
 

‎book/doctrine.rst‎

Lines changed: 71 additions & 278 deletions
Large diffs are not rendered by default.

‎book/from_flat_php_to_symfony2.rst‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,8 @@ them for you. Here's the same sample application, now built in Symfony2::
553553
{
554554
public function listAction()
555555
{
556-
$posts = $this->get('doctrine')->getManager()
556+
$posts = $this->get('doctrine')
557+
->getManager()
557558
->createQuery('SELECT p FROM AcmeBlogBundle:Post p')
558559
->execute();
559560

@@ -568,8 +569,7 @@ them for you. Here's the same sample application, now built in Symfony2::
568569
$post = $this->get('doctrine')
569570
->getManager()
570571
->getRepository('AcmeBlogBundle:Post')
571-
->find($id)
572-
;
572+
->find($id);
573573

574574
if (!$post) {
575575
// cause the 404 page not found to be displayed

‎book/http_fundamentals.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ regardless of how your project is developed. To name a few:
542542
* `Security`_ - A powerful library for handling all types of security inside
543543
an application;
544544

545-
* `Translation`_ A framework for translating strings in your application.
545+
* `Translation`_ - A framework for translating strings in your application.
546546

547547
Each and every one of these components is decoupled and can be used in *any*
548548
PHP project, regardless of whether or not you use the Symfony2 framework.

‎cookbook/doctrine/console.rst‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.. index::
2+
single: Doctrine; ORM console commands
3+
single: CLI; Doctrine ORM
4+
5+
Console Commands
6+
----------------
7+
8+
The Doctrine2 ORM integration offers several console commands under the
9+
``doctrine`` namespace. To view the command list you can use the ``list``
10+
command:
11+
12+
.. code-block:: bash
13+
14+
$ php app/console list doctrine
15+
16+
A list of available commands will print out. You can find out more information
17+
about any of these commands (or any Symfony command) by running the ``help``
18+
command. For example, to get details about the ``doctrine:database:create``
19+
task, run:
20+
21+
.. code-block:: bash
22+
23+
$ php app/console help doctrine:database:create
24+
25+
Some notable or interesting tasks include:
26+
27+
* ``doctrine:ensure-production-settings`` - checks to see if the current
28+
environment is configured efficiently for production. This should always
29+
be run in the ``prod`` environment:
30+
31+
.. code-block:: bash
32+
33+
$ php app/console doctrine:ensure-production-settings --env=prod
34+
35+
* ``doctrine:mapping:import`` - allows Doctrine to introspect an existing
36+
database and create mapping information. For more information, see
37+
:doc:`/cookbook/doctrine/reverse_engineering`.
38+
39+
* ``doctrine:mapping:info`` - tells you all of the entities that Doctrine
40+
is aware of and whether or not there are any basic errors with the mapping.
41+
42+
* ``doctrine:query:dql`` and ``doctrine:query:sql`` - allow you to execute
43+
DQL or SQL queries directly from the command line.

‎cookbook/doctrine/index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ Doctrine
1414
resolve_target_entity
1515
mapping_model_classes
1616
registration_form
17+
console

‎cookbook/doctrine/registration_form.rst‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ Next, create the form for this ``Registration`` model::
216216
'checkbox',
217217
array('property_path' => 'termsAccepted')
218218
);
219+
$builder->add('Register', 'submit');
219220
}
220221

221222
public function getName()
@@ -239,7 +240,6 @@ controller for displaying the registration form::
239240
namespace Acme\AccountBundle\Controller;
240241

241242
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
242-
use Symfony\Component\HttpFoundation\Response;
243243

244244
use Acme\AccountBundle\Form\Type\RegistrationType;
245245
use Acme\AccountBundle\Form\Model\Registration;
@@ -270,6 +270,9 @@ And its template:
270270
Next, create the controller which handles the form submission. This performs
271271
the validation and saves the data into the database::
272272

273+
use Symfony\Component\HttpFoundation\Request;
274+
// ...
275+
273276
public function createAction(Request $request)
274277
{
275278
$em = $this->getDoctrine()->getManager();

‎cookbook/map.rst.inc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
* :doc:`/cookbook/doctrine/resolve_target_entity`
6363
* :doc:`/cookbook/doctrine/mapping_model_classes`
6464
* :doc:`/cookbook/doctrine/registration_form`
65+
* :doc:`/cookbook/doctrine/console`
6566

6667
* :doc:`/cookbook/email/index`
6768

@@ -132,6 +133,7 @@
132133
* :doc:`/cookbook/security/remember_me`
133134
* :doc:`/cookbook/security/impersonating_user`
134135
* :doc:`/cookbook/security/voters`
136+
* :doc:`/cookbook/security/voters_data_permission`
135137
* :doc:`/cookbook/security/acl`
136138
* :doc:`/cookbook/security/acl_advanced`
137139
* :doc:`/cookbook/security/force_https`

‎cookbook/security/acl.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ the ACL system comes in.
1414
Using ACL's isn't trivial, and for simpler use cases, it may be overkill.
1515
If your permission logic could be described by just writing some code (e.g.
1616
to check if a Blog is owned by the current User), then consider using
17-
:doc:`voters </cookbook/security/voters>`. A voter is passed the object
17+
:doc:`voters </cookbook/security/voters_data_permission>`. A voter is passed the object
1818
being voted on, which you can use to make complex decisions and effectively
1919
implement your own ACL. Enforcing authorization (e.g. the ``isGranted``
2020
part) will look similar to what you see in this entry, but your voter

‎cookbook/security/index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Security
88
remember_me
99
impersonating_user
1010
voters
11+
voters_data_permission
1112
acl
1213
acl_advanced
1314
force_https
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.. code-block:: php
2+
3+
interface VoterInterface
4+
{
5+
public function supportsAttribute($attribute);
6+
public function supportsClass($class);
7+
public function vote(TokenInterface $token, $post, array $attributes);
8+
}
9+
10+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsAttribute`
11+
method is used to check if the voter supports the given user attribute (i.e:
12+
a role like ``ROLE_USER``, an ACL ``EDIT``, etc.).
13+
14+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::supportsClass`
15+
method is used to check if the voter supports the class of the object whose
16+
access is being checked.
17+
18+
The :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::vote`
19+
method must implement the business logic that verifies whether or not the
20+
user has access. This method must return one of the following values:
21+
22+
* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter;
23+
* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted;
24+
* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter.

‎cookbook/security/voters.rst‎

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,7 @@ A custom voter must implement
2121
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
2222
which requires the following three methods:
2323

24-
.. code-block:: php
25-
26-
interface VoterInterface
27-
{
28-
public function supportsAttribute($attribute);
29-
public function supportsClass($class);
30-
public function vote(TokenInterface $token, $object, array $attributes);
31-
}
32-
33-
The ``supportsAttribute()`` method is used to check if the voter supports
34-
the given user attribute (i.e: a role, an ACL, etc.).
35-
36-
The ``supportsClass()`` method is used to check if the voter supports the
37-
class of the object whose access is being checked (doesn't apply to this entry).
38-
39-
The ``vote()`` method must implement the business logic that verifies whether
40-
or not the user is granted access. This method must return one of the following
41-
values:
42-
43-
* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter;
44-
* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted;
45-
* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter.
24+
.. include:: /cookbook/security/voter_interface.rst.inc
4625

4726
In this example, you'll check if the user's IP address matches against a list of
4827
blacklisted addresses and "something" will be the application. If the user's IP is blacklisted, you'll return
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
.. index::
2+
single: Security; Data Permission Voters
3+
4+
How to Use Voters to Check User Permissions
5+
===========================================
6+
7+
In Symfony2 you can check the permission to access data by using the
8+
:doc:`ACL module </cookbook/security/acl>`, which is a bit overwhelming
9+
for many applications. A much easier solution is to work with custom voters,
10+
which are like simple conditional statements.
11+
12+
.. seealso::
13+
14+
Voters can also be used in other ways, like, for example, blacklisting IP
15+
addresses from the entire application: :doc:`/cookbook/security/voters`.
16+
17+
.. tip::
18+
19+
Take a look at the
20+
:doc:`authorization </components/security/authorization>`
21+
chapter for an even deeper understanding on voters.
22+
23+
How Symfony Uses Voters
24+
-----------------------
25+
26+
In order to use voters, you have to understand how Symfony works with them.
27+
All voters are called each time you use the ``isGranted()`` method on Symfony's
28+
security context (i.e. the ``security.context`` service). Each one decides
29+
if the current user should have access to some resource.
30+
31+
Ultimately, Symfony uses one of three different approaches on what to do
32+
with the feedback from all voters: affirmative, consensus and unanimous.
33+
34+
For more information take a look at
35+
:ref:`the section about access decision managers <components-security-access-decision-manager>`.
36+
37+
The Voter Interface
38+
-------------------
39+
40+
A custom voter must implement
41+
:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
42+
which has this structure:
43+
44+
.. include:: /cookbook/security/voter_interface.rst.inc
45+
46+
In this example, the voter will check if the user has access to a specific
47+
object according to your custom conditions (e.g. they must be the owner of
48+
the object). If the condition fails, you'll return
49+
``VoterInterface::ACCESS_DENIED``, otherwise you'll return
50+
``VoterInterface::ACCESS_GRANTED``. In case the responsibility for this decision
51+
does not belong to this voter, it will return ``VoterInterface::ACCESS_ABSTAIN``.
52+
53+
Creating the Custom Voter
54+
-------------------------
55+
56+
The goal is to create a voter that checks if a user has access to view or
57+
edit a particular object. Here's an example implementation:
58+
59+
// src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php
60+
namespace Acme\DemoBundle\Security\Authorization\Voter;
61+
62+
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
63+
use Symfony\Component\DependencyInjection\ContainerInterface;
64+
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
65+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
66+
use Symfony\Component\Security\Core\User\UserInterface;
67+
use Acme\DemoBundle\Entity\Post;
68+
69+
class PostVoter implements VoterInterface
70+
{
71+
const VIEW = 'view';
72+
const EDIT = 'edit';
73+
74+
public function supportsAttribute($attribute)
75+
{
76+
return in_array($attribute, array(
77+
self::VIEW,
78+
self::EDIT,
79+
));
80+
}
81+
82+
public function supportsClass($obj)
83+
{
84+
return $obj instanceof Post;
85+
}
86+
87+
/**
88+
* @var \Acme\DemoBundle\Entity\Post $post
89+
*/
90+
public function vote(TokenInterface $token, $post, array $attributes)
91+
{
92+
// check if class of this object is supported by this voter
93+
if (!$this->supportsClass($post)) {
94+
return VoterInterface::ACCESS_ABSTAIN;
95+
}
96+
97+
// check if the voter is used correct, only allow one attribute
98+
// this isn't a requirement, it's just one easy way for you to
99+
// design your voter
100+
if(1 !== count($attributes)) {
101+
throw new InvalidArgumentException(
102+
'Only one attribute is allowed for VIEW or EDIT'
103+
);
104+
}
105+
106+
// set the attribute to check against
107+
$attribute = $attributes[0];
108+
109+
// get current logged in user
110+
$user = $token->getUser();
111+
112+
// check if the given attribute is covered by this voter
113+
if (!$this->supportsAttribute($attribute)) {
114+
return VoterInterface::ACCESS_ABSTAIN;
115+
}
116+
117+
// make sure there is a user object (i.e. that the user is logged in)
118+
if (!$user instanceof UserInterface) {
119+
return VoterInterface::ACCESS_DENIED;
120+
}
121+
122+
switch($attribute) {
123+
case 'view':
124+
// the data object could have for example a method isPrivate()
125+
// which checks the Boolean attribute $private
126+
if (!$post->isPrivate()) {
127+
return VoterInterface::ACCESS_GRANTED;
128+
}
129+
break;
130+
131+
case 'edit':
132+
// we assume that our data object has a method getOwner() to
133+
// get the current owner user entity for this data object
134+
if ($user->getId() === $post->getOwner()->getId()) {
135+
return VoterInterface::ACCESS_GRANTED;
136+
}
137+
break;
138+
}
139+
}
140+
}
141+
142+
That's it! The voter is done. The next step is to inject the voter into
143+
the security layer.
144+
145+
Declaring the Voter as a Service
146+
--------------------------------
147+
148+
To inject the voter into the security layer, you must declare it as a service
149+
and tag it with ``security.voter``:
150+
151+
.. configuration-block::
152+
153+
.. code-block:: yaml
154+
155+
# src/Acme/DemoBundle/Resources/config/services.yml
156+
services:
157+
security.access.post_voter:
158+
class: Acme\DemoBundle\Security\Authorization\Voter\PostVoter
159+
public: false
160+
tags:
161+
- { name: security.voter }
162+
163+
.. code-block:: xml
164+
165+
<!-- src/Acme/DemoBundle/Resources/config/services.xml -->
166+
<?xml version="1.0" encoding="UTF-8" ?>
167+
<container xmlns="http://symfony.com/schema/dic/services"
168+
xsi:schemaLocation="http://symfony.com/schema/dic/services
169+
http://symfony.com/schema/dic/services/services-1.0.xsd">
170+
<services>
171+
<service id="security.access.post_document_voter"
172+
class="Acme\DemoBundle\Security\Authorization\Voter\PostVoter"
173+
public="false">
174+
<tag name="security.voter" />
175+
</service>
176+
</services>
177+
</container>
178+
179+
.. code-block:: php
180+
181+
// src/Acme/DemoBundle/Resources/config/services.php
182+
$container
183+
->register(
184+
'security.access.post_document_voter',
185+
'Acme\DemoBundle\Security\Authorization\Voter\PostVoter'
186+
)
187+
->addTag('security.voter')
188+
;
189+
190+
How to Use the Voter in a Controller
191+
------------------------------------
192+
193+
The registered voter will then always be asked as soon as the method ``isGranted()``
194+
from the security context is called.
195+
196+
.. code-block:: php
197+
198+
// src/Acme/DemoBundle/Controller/PostController.php
199+
namespace Acme\DemoBundle\Controller;
200+
201+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
202+
use Symfony\Component\HttpFoundation\Response;
203+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
204+
205+
class PostController extends Controller
206+
{
207+
public function showAction()
208+
{
209+
// get a Post instance
210+
$post = ...;
211+
212+
// keep in mind, this will call all registered security voters
213+
if (false === $this->get('security.context')->isGranted('view', $post)) {
214+
throw new AccessDeniedException('Unauthorised access!');
215+
}
216+
217+
$product = $this->getDoctrine()
218+
->getRepository('AcmeStoreBundle:Post')
219+
->find($id);
220+
221+
return new Response('<h1>'.$post->getName().'</h1>');
222+
}
223+
}
224+
225+
It's that easy!

‎images/quick_tour/hello_fabien.png‎

41 KB
Loading

‎images/quick_tour/profiler.png‎

804 KB
Loading
39.4 KB
Loading

‎images/quick_tour/welcome.png‎

66.3 KB
Loading

‎quick_tour/the_architecture.rst‎

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ Understanding the Directory Structure
1111
-------------------------------------
1212

1313
The directory structure of a Symfony2 :term:`application` is rather flexible,
14-
but the directory structure of the *Standard Edition* distribution reflects
15-
the typical and recommended structure of a Symfony2 application:
14+
but the recommended structure is as follows:
1615

17-
* ``app/``: The application configuration;
18-
* ``src/``: The project's PHP code;
19-
* ``vendor/``: The third-party dependencies;
20-
* ``web/``: The web root directory.
16+
* ``app/``: the application configuration;
17+
* ``src/``: the project's PHP code;
18+
* ``vendor/``: the third-party dependencies;
19+
* ``web/``: the web root directory.
2120

2221
The ``web/`` Directory
2322
~~~~~~~~~~~~~~~~~~~~~~
@@ -36,11 +35,10 @@ lives::
3635
$kernel->loadClassCache();
3736
$kernel->handle(Request::createFromGlobals())->send();
3837

39-
The kernel first requires the ``bootstrap.php.cache`` file, which bootstraps
40-
the framework and registers the autoloader (see below).
41-
42-
Like any front controller, ``app.php`` uses a Kernel Class, ``AppKernel``, to
43-
bootstrap the application.
38+
The controller first bootstraps the application using a kernel class (``AppKernel``
39+
in this case). Then, it creates the ``Request`` object using the PHP's global
40+
variables and passes it to the kernel. The last step is to send the response
41+
contents returned by the kernel back to the user.
4442

4543
.. _the-app-dir:
4644

@@ -59,17 +57,11 @@ This class must implement two methods:
5957
(more on this later).
6058

6159
Autoloading is handled automatically via `Composer`_, which means that you
62-
can use any PHP classes without doing anything at all! If you need more flexibility,
63-
you can extend the autoloader in the ``app/autoload.php`` file. All dependencies
60+
can use any PHP class without doing anything at all! All dependencies
6461
are stored under the ``vendor/`` directory, but this is just a convention.
6562
You can store them wherever you want, globally on your server or locally
6663
in your projects.
6764

68-
.. note::
69-
70-
If you want to learn more about Composer's autoloader, read `Composer-Autoloader`_.
71-
Symfony also has an autoloading component - read ":doc:`/components/class_loader/class_loader`".
72-
7365
Understanding the Bundle System
7466
-------------------------------
7567

@@ -79,12 +71,13 @@ Symfony2, the :term:`bundle` system.
7971
A bundle is kind of like a plugin in other software. So why is it called a
8072
*bundle* and not a *plugin*? This is because *everything* is a bundle in
8173
Symfony2, from the core framework features to the code you write for your
82-
application. Bundles are first-class citizens in Symfony2. This gives you
83-
the flexibility to use pre-built features packaged in third-party bundles
84-
or to distribute your own bundles. It makes it easy to pick and choose which
85-
features to enable in your application and optimize them the way you want.
86-
And at the end of the day, your application code is just as *important* as
87-
the core framework itself.
74+
application.
75+
76+
Bundles are first-class citizens in Symfony2. This gives you the flexibility
77+
to use pre-built features packaged in third-party bundles or to distribute your
78+
own bundles. It makes it easy to pick and choose which features to enable in
79+
your application and optimize them the way you want. And at the end of the day,
80+
your application code is just as *important* as the core framework itself.
8881

8982
Registering a Bundle
9083
~~~~~~~~~~~~~~~~~~~~
@@ -119,14 +112,14 @@ a single ``Bundle`` class that describes it::
119112

120113
In addition to the AcmeDemoBundle that was already talked about, notice
121114
that the kernel also enables other bundles such as the FrameworkBundle,
122-
DoctrineBundle, SwiftmailerBundle, and AsseticBundle bundle.
123-
They are all part of the core framework.
115+
DoctrineBundle, SwiftmailerBundle and AsseticBundle bundle. They are all part
116+
of the core framework.
124117

125118
Configuring a Bundle
126119
~~~~~~~~~~~~~~~~~~~~
127120

128121
Each bundle can be customized via configuration files written in YAML, XML, or
129-
PHP. Have a look at the default configuration:
122+
PHP. Have a look at the default Symfony configuration:
130123

131124
.. code-block:: yaml
132125
@@ -191,9 +184,9 @@ PHP. Have a look at the default configuration:
191184
password: "%mailer_password%"
192185
spool: { type: memory }
193186
194-
Each entry like ``framework`` defines the configuration for a specific bundle.
195-
For example, ``framework`` configures the FrameworkBundle while ``swiftmailer``
196-
configures the SwiftmailerBundle.
187+
Each first level entry like ``framework``, ``twig`` or ``doctrine`` defines the
188+
configuration for a specific bundle. For example, ``framework`` configures the
189+
FrameworkBundle while ``swiftmailer`` configures the SwiftmailerBundle.
197190

198191
Each :term:`environment` can override the default configuration by providing a
199192
specific configuration file. For example, the ``dev`` environment loads the
@@ -268,7 +261,7 @@ Extending Bundles
268261

269262
If you follow these conventions, then you can use :doc:`bundle inheritance</cookbook/bundles/inheritance>`
270263
to "override" files, controllers or templates. For example, you can create
271-
a bundle - ``AcmeNewBundle`` - and specify that it overrides AcmeDemoBundle.
264+
a bundle - AcmeNewBundle - and specify that it overrides AcmeDemoBundle.
272265
When Symfony loads the ``AcmeDemoBundle:Welcome:index`` controller, it will
273266
first look for the ``WelcomeController`` class in AcmeNewBundle and, if
274267
it doesn't exist, then look inside AcmeDemoBundle. This means that one bundle
@@ -296,8 +289,9 @@ each request? The speed is partly due to its cache system. The application
296289
configuration is only parsed for the very first request and then compiled down
297290
to plain PHP code stored in the ``app/cache/`` directory. In the development
298291
environment, Symfony2 is smart enough to flush the cache when you change a
299-
file. But in the production environment, it is your responsibility to clear
300-
the cache when you update your code or change its configuration.
292+
file. But in the production environment, to speed things up, it is your
293+
responsibility to clear the cache when you update your code or change its
294+
configuration.
301295

302296
When developing a web application, things can go wrong in many ways. The log
303297
files in the ``app/logs/`` directory tell you everything about the requests
@@ -336,4 +330,3 @@ topics now? Look no further - go to the official :doc:`/book/index` and pick
336330
any topic you want.
337331

338332
.. _Composer: http://getcomposer.org
339-
.. _`Composer-Autoloader`: http://getcomposer.org/doc/01-basic-usage.md#autoloading

‎quick_tour/the_big_picture.rst‎

Lines changed: 114 additions & 232 deletions
Large diffs are not rendered by default.

‎quick_tour/the_controller.rst‎

Lines changed: 45 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ in Symfony2 is straightforward. Tweak the route by adding a default value of
2828
return array('name' => $name);
2929
}
3030

31-
By using the request format (as defined by the ``_format`` value), Symfony2
32-
automatically selects the right template, here ``hello.xml.twig``:
31+
By using the request format (as defined by the special ``_format`` variable),
32+
Symfony2 automatically selects the right template, here ``hello.xml.twig``:
3333

3434
.. code-block:: xml+php
3535

@@ -50,18 +50,23 @@ placeholder in the route path instead::
5050
// ...
5151

5252
/**
53-
* @Route("/hello/{name}.{_format}", defaults={"_format"="html"}, requirements={"_format"="html|xml|json"}, name="_demo_hello")
53+
* @Route(
54+
* "/hello/{name}.{_format}",
55+
* defaults = { "_format" = "html" },
56+
* requirements = { "_format" = "html|xml|json" },
57+
* name = "_demo_hello"
58+
* )
5459
* @Template()
5560
*/
5661
public function helloAction($name)
5762
{
5863
return array('name' => $name);
5964
}
6065

61-
The controller will now be called for URLs like ``/demo/hello/Fabien.xml`` or
66+
The controller will now match URLs like ``/demo/hello/Fabien.xml`` or
6267
``/demo/hello/Fabien.json``.
6368

64-
The ``requirements`` entry defines regular expressions that placeholders must
69+
The ``requirements`` entry defines regular expressions that variables must
6570
match. In this example, if you try to request the ``/demo/hello/Fabien.js``
6671
resource, you will get a 404 HTTP error, as it does not match the ``_format``
6772
requirement.
@@ -78,21 +83,33 @@ The ``generateUrl()`` is the same method as the ``path()`` function used in the
7883
templates. It takes the route name and an array of parameters as arguments and
7984
returns the associated friendly URL.
8085

81-
You can also easily forward the action to another one with the ``forward()``
82-
method. Internally, Symfony makes a "sub-request", and returns the ``Response``
83-
object from that sub-request::
86+
You can also internally forward the action to another using the ``forward()``
87+
method::
88+
89+
return $this->forward('AcmeDemoBundle:Hello:fancy', array(
90+
'name' => $name,
91+
'color' => 'green'
92+
));
93+
94+
Displaying Error Pages
95+
----------------------
96+
97+
Errors will inevitably happen during the execution of every web application.
98+
In the case of ``404`` errors, Symfony includes a handy shortcut that you can
99+
use in your controllers::
84100

85-
$response = $this->forward('AcmeDemoBundle:Hello:fancy', array('name' => $name, 'color' => 'green'));
101+
throw $this->createNotFoundException();
86102

87-
// ... do something with the response or return it directly
103+
For ``500`` errors, just throw a regular PHP exception inside the controller and
104+
Symfony will transform it into a proper ``500`` error page::
105+
106+
throw new \Exception('Something went wrong!');
88107

89108
Getting information from the Request
90109
------------------------------------
91110

92-
Besides the values of the routing placeholders, the controller also has access
93-
to the ``Request`` object. The framework injects the ``Request`` object in the
94-
controller if a variable is type hinted with
95-
`Symfony\Component\HttpFoundation\Request`::
111+
Symfony automatically injects the ``Request`` object when the controller has an
112+
argument that's type hinted with ``Symfony\Component\HttpFoundation\Request`::
96113

97114
use Symfony\Component\HttpFoundation\Request;
98115

@@ -102,7 +119,7 @@ controller if a variable is type hinted with
102119

103120
$request->getPreferredLanguage(array('en', 'fr'));
104121

105-
$request->query->get('page'); // get a $_GET parameter
122+
$request->query->get('page'); // get a $_GET parameter
106123

107124
$request->request->get('page'); // get a $_POST parameter
108125
}
@@ -136,94 +153,24 @@ from any controller::
136153
// store an attribute for reuse during a later user request
137154
$session->set('foo', 'bar');
138155

139-
// in another controller for another request
156+
// get the value of a session attribute
140157
$foo = $session->get('foo');
141158

142-
// use a default value if the key doesn't exist
143-
$filters = $session->get('filters', array());
159+
// use a default value if the attribute doesn't exist
160+
$foo = $session->get('foo', 'default_value');
144161
}
145162

146-
You can also store small messages that will only be available for the very
147-
next request::
163+
You can also store "flash messages" that will auto-delete after the next request.
164+
They are useful when you need to set a success message before redirecting the
165+
user to another page (which will then show the message)::
148166

149167
// store a message for the very next request (in a controller)
150168
$session->getFlashBag()->add('notice', 'Congratulations, your action succeeded!');
151169

152-
// display any messages back in the next request (in a template)
153-
154-
{% for flashMessage in app.session.flashbag.get('notice') %}
155-
<div>{{ flashMessage }}</div>
156-
{% endfor %}
157-
158-
This is useful when you need to set a success message before redirecting
159-
the user to another page (which will then show the message). Please note that
160-
when you use has() instead of get(), the flash message will not be cleared and
161-
thus remains available during the following requests.
162-
163-
Securing Resources
164-
------------------
165-
166-
The Symfony Standard Edition comes with a simple security configuration that
167-
fits most common needs:
168-
169-
.. code-block:: yaml
170-
171-
# app/config/security.yml
172-
security:
173-
encoders:
174-
Symfony\Component\Security\Core\User\User: plaintext
175-
176-
role_hierarchy:
177-
ROLE_ADMIN: ROLE_USER
178-
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
179-
180-
providers:
181-
in_memory:
182-
memory:
183-
users:
184-
user: { password: userpass, roles: [ 'ROLE_USER' ] }
185-
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
186-
187-
firewalls:
188-
dev:
189-
pattern: ^/(_(profiler|wdt)|css|images|js)/
190-
security: false
191-
192-
login:
193-
pattern: ^/demo/secured/login$
194-
security: false
195-
196-
secured_area:
197-
pattern: ^/demo/secured/
198-
form_login:
199-
check_path: /demo/secured/login_check
200-
login_path: /demo/secured/login
201-
logout:
202-
path: /demo/secured/logout
203-
target: /demo/
204-
205-
This configuration requires users to log in for any URL starting with
206-
``/demo/secured/`` and defines two valid users: ``user`` and ``admin``.
207-
Moreover, the ``admin`` user has a ``ROLE_ADMIN`` role, which includes the
208-
``ROLE_USER`` role as well (see the ``role_hierarchy`` setting).
209-
210-
.. tip::
211-
212-
For readability, passwords are stored in clear text in this simple
213-
configuration, but you can use any hashing algorithm by tweaking the
214-
``encoders`` section.
215-
216-
Going to the ``http://localhost/app_dev.php/demo/secured/hello``
217-
URL will automatically redirect you to the login form because this resource is
218-
protected by a ``firewall``.
219-
220-
.. note::
170+
.. code-block:: html+jinja
221171

222-
The Symfony2 security layer is very flexible and comes with many different
223-
user providers (like one for the Doctrine ORM) and authentication providers
224-
(like HTTP basic, HTTP digest, or X509 certificates). Read the
225-
":doc:`/book/security`" chapter of the book for more information
226-
on how to use and configure them.
172+
{# display the flash message in the template #}
173+
<div>{{ app.session.flashbag.get('notice') }}</div>
227174

228175
Caching Resources
229176
-----------------
@@ -247,19 +194,10 @@ convenient ``@Cache()`` annotation::
247194
return array('name' => $name);
248195
}
249196

250-
In this example, the resource will be cached for a day. But you can also use
251-
validation instead of expiration or a combination of both if that fits your
252-
needs better.
253-
254-
Resource caching is managed by the Symfony2 built-in reverse proxy. But because
255-
caching is managed using regular HTTP cache headers, you can replace the
256-
built-in reverse proxy with Varnish or Squid and easily scale your application.
257-
258-
.. note::
259-
260-
But what if you cannot cache whole pages? Symfony2 still has the solution
261-
via Edge Side Includes (ESI), which are supported natively. Learn more by
262-
reading the ":doc:`/book/http_cache`" chapter of the book.
197+
In this example, the resource will be cached for a day (``86400`` seconds).
198+
Resource caching is managed by Symfony2 itself. But because caching is managed
199+
using standard HTTP cache headers, you can use Varnish or Squid without having
200+
to modify a single line of code in your application.
263201

264202
Final Thoughts
265203
--------------

‎quick_tour/the_view.rst‎

Lines changed: 77 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,28 @@ The View
22
========
33

44
After reading the first part of this tutorial, you have decided that Symfony2
5-
was worth another 10 minutes. Great choice! In this second part, you will
6-
learn more about the Symfony2 template engine, `Twig`_. Twig is a flexible,
7-
fast, and secure template engine for PHP. It makes your templates more
8-
readable and concise; it also makes them more friendly for web designers.
9-
10-
.. note::
11-
12-
Instead of Twig, you can also use :doc:`PHP </cookbook/templating/PHP>`
13-
for your templates. Both template engines are supported by Symfony2.
5+
was worth another 10 minutes. In this second part, you will learn more about
6+
`Twig`_, the fast, flexible, and secure template engine for PHP. Twig makes your
7+
templates more readable and concise; it also makes them more friendly for web
8+
designers.
149

1510
Getting familiar with Twig
1611
--------------------------
1712

18-
.. tip::
13+
The official `Twig documentation`_ is the best resource to learn everything
14+
about this new template engine. This section just gives you a quick overview of
15+
its main concepts.
1916

20-
If you want to learn Twig, it's highly recommended you read its official
21-
`documentation`_. This section is just a quick overview of the main
22-
concepts.
17+
A Twig template is a text file that can generate any type of content (HTML, CSS,
18+
JavaScript, XML, CSV, LaTeX, ...). Twig elements are separated from the rest of
19+
the template contents using any of these delimiters:
2320

24-
A Twig template is a text file that can generate any type of content (HTML,
25-
XML, CSV, LaTeX, ...). Twig defines two kinds of delimiters:
21+
* ``{{ ... }}``: prints the content of a variable or the result of an expression;
2622

27-
* ``{{ ... }}``: Prints a variable or the result of an expression;
23+
* ``{% ... %}``: controls the logic of the template; it is used for example to
24+
execute ``for`` loops and ``if`` statements;
2825

29-
* ``{% ... %}``: Controls the logic of the template; it is used to execute
30-
``for`` loops and ``if`` statements, for example.
26+
* ``{# ... #}``: allows including comments inside templates.
3127

3228
Below is a minimal template that illustrates a few basics, using two variables
3329
``page_title`` and ``navigation``, which would be passed into the template:
@@ -37,74 +33,65 @@ Below is a minimal template that illustrates a few basics, using two variables
3733
<!DOCTYPE html>
3834
<html>
3935
<head>
40-
<title>My Webpage</title>
36+
<title>{{ page_title }}</title>
4137
</head>
4238
<body>
4339
<h1>{{ page_title }}</h1>
4440

4541
<ul id="navigation">
4642
{% for item in navigation %}
47-
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
43+
<li><a href="{{ item.url }}">{{ item.label }}</a></li>
4844
{% endfor %}
4945
</ul>
5046
</body>
5147
</html>
5248

53-
.. tip::
54-
55-
Comments can be included inside templates using the ``{# ... #}`` delimiter.
56-
5749
To render a template in Symfony, use the ``render`` method from within a controller
58-
and pass it any variables needed in the template::
50+
and pass the variables needed as an array using the optional second argument::
5951

6052
$this->render('AcmeDemoBundle:Demo:hello.html.twig', array(
6153
'name' => $name,
6254
));
6355

6456
Variables passed to a template can be strings, arrays, or even objects. Twig
6557
abstracts the difference between them and lets you access "attributes" of a
66-
variable with the dot (``.``) notation:
58+
variable with the dot (``.``) notation. The following code listing shows how to
59+
display the content of a variable depending on the type of the variable passed
60+
by the controller:
6761

6862
.. code-block:: jinja
6963
64+
{# 1. Simple variables #}
7065
{# array('name' => 'Fabien') #}
7166
{{ name }}
7267
68+
{# 2. Arrays #}
7369
{# array('user' => array('name' => 'Fabien')) #}
7470
{{ user.name }}
7571
76-
{# force array lookup #}
72+
{# alternative syntax for arrays #}
7773
{{ user['name'] }}
7874
75+
{# 3. Objects #}
7976
{# array('user' => new User('Fabien')) #}
8077
{{ user.name }}
8178
{{ user.getName }}
8279
83-
{# force method name lookup #}
80+
{# alternative syntax for objects #}
8481
{{ user.name() }}
8582
{{ user.getName() }}
8683
87-
{# pass arguments to a method #}
88-
{{ user.date('Y-m-d') }}
89-
90-
.. note::
91-
92-
It's important to know that the curly braces are not part of the variable
93-
but the print statement. If you access variables inside tags don't put the
94-
braces around.
95-
9684
Decorating Templates
9785
--------------------
9886

9987
More often than not, templates in a project share common elements, like the
100-
well-known header and footer. In Symfony2, you think about this problem
101-
differently: a template can be decorated by another one. This works exactly
102-
the same as PHP classes: template inheritance allows you to build a base
103-
"layout" template that contains all the common elements of your site and
104-
defines "blocks" that child templates can override.
88+
well-known header and footer. Twig solves this problem elegantly with a concept
89+
called "template inheritance". This feature allows you to build a base "layout"
90+
template that contains all the common elements of your site and defines "blocks"
91+
that child templates can override.
10592

106-
The ``hello.html.twig`` template inherits from ``layout.html.twig``, thanks to
107-
the ``extends`` tag:
93+
The ``hello.html.twig`` template uses the ``extends`` tag to indicate that it
94+
inherits from the common ``layout.html.twig`` template:
10895

10996
.. code-block:: html+jinja
11097

@@ -120,40 +107,50 @@ the ``extends`` tag:
120107
The ``AcmeDemoBundle::layout.html.twig`` notation sounds familiar, doesn't it?
121108
It is the same notation used to reference a regular template. The ``::`` part
122109
simply means that the controller element is empty, so the corresponding file
123-
is directly stored under the ``Resources/views/`` directory.
110+
is directly stored under the ``Resources/views/`` directory of the bundle.
124111

125112
Now, simplify the ``layout.html.twig`` template:
126113

127114
.. code-block:: jinja
128115
129116
{# src/Acme/DemoBundle/Resources/views/layout.html.twig #}
130-
<div class="symfony-content">
117+
<div>
131118
{% block content %}
132119
{% endblock %}
133120
</div>
134121
135-
The ``{% block %}`` tags define blocks that child templates can fill in. All
136-
the block tag does is to tell the template engine that a child template may
137-
override those portions of the template.
138-
139-
In this example, the ``hello.html.twig`` template overrides the ``content``
140-
block, meaning that the "Hello Fabien" text is rendered inside the ``div.symfony-content``
141-
element.
122+
The ``{% block %}`` tags tell the template engine that a child template may
123+
override those portions of the template. In this example, the ``hello.html.twig``
124+
template overrides the ``content`` block, meaning that the "Hello Fabien" text
125+
is rendered inside the ``<div>`` element.
142126

143127
Using Tags, Filters, and Functions
144128
----------------------------------
145129

146130
One of the best feature of Twig is its extensibility via tags, filters, and
147-
functions. Symfony2 comes bundled with many of these built-in to ease the
148-
work of the template designer.
131+
functions. Take a look at the following sample template that uses filters
132+
extensively to modify the information before displaying it to the user:
133+
134+
.. code-block:: jinja
135+
136+
<h1>{{ article.title|trim|capitalize }}</h1>
137+
138+
<p>{{ article.content|striptags|slice(0, 1024) }}</p>
139+
140+
<p>Tags: {{ article.tags|sort|join(", ") }}</p>
141+
142+
<p>Next article will be published on {{ 'next Monday'|date('M j, Y')}}</p>
143+
144+
Don't forget to check out the official `Twig documentation`_ to learn everything
145+
about filters, functions and tags.
149146

150147
Including other Templates
151148
~~~~~~~~~~~~~~~~~~~~~~~~~
152149

153-
The best way to share a snippet of code between several distinct templates is
154-
to create a new template that can then be included from other templates.
150+
The best way to share a snippet of code between several templates is to create a
151+
new template fragment that can then be included from other templates.
155152

156-
Create an ``embedded.html.twig`` template:
153+
First, create an ``embedded.html.twig`` template:
157154

158155
.. code-block:: jinja
159156
@@ -179,32 +176,31 @@ And what if you want to embed the result of another controller in a template?
179176
That's very useful when working with Ajax, or when the embedded template needs
180177
some variable not available in the main template.
181178

182-
Suppose you've created a ``fancyAction`` controller method, and you want to
183-
"render" it inside the ``index`` template, which means including the result
184-
(e.g. ``HTML``) of the controller. To do this, use the ``render`` function:
179+
Suppose you've created a ``topArticlesAction`` controller method to display the
180+
most popular articles of your website. If you want to "render" the result of
181+
that method (e.g. ``HTML``) inside the ``index`` template, use the ``render``
182+
function:
185183

186184
.. code-block:: jinja
187185
188186
{# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #}
189-
{{ render(controller("AcmeDemoBundle:Demo:fancy", {'name': name, 'color': 'green'})) }}
187+
{{ render(controller("AcmeDemoBundle:Demo:topArticles", {'num': 10})) }}
190188
191-
Here, the ``AcmeDemoBundle:Demo:fancy`` string refers to the ``fancy`` action
192-
of the ``Demo`` controller. The arguments (``name`` and ``color``) act like
193-
simulated request variables (as if the ``fancyAction`` were handling a whole
194-
new request) and are made available to the controller::
189+
Here, the ``AcmeDemoBundle:Demo:topArticles`` string refers to the
190+
``topArticlesAction`` action of the ``Demo`` controller, and the ``num``
191+
argument is made available to the controller::
195192

196193
// src/Acme/DemoBundle/Controller/DemoController.php
197194

198195
class DemoController extends Controller
199196
{
200-
public function fancyAction($name, $color)
197+
public function topArticlesAction($num)
201198
{
202-
// create some object, based on the $color variable
203-
$object = ...;
199+
// look for the $num most popular articles in the database
200+
$articles = ...;
204201

205-
return $this->render('AcmeDemoBundle:Demo:fancy.html.twig', array(
206-
'name' => $name,
207-
'object' => $object,
202+
return $this->render('AcmeDemoBundle:Demo:topArticles.html.twig', array(
203+
'articles' => $articles,
208204
));
209205
}
210206

@@ -214,8 +210,8 @@ new request) and are made available to the controller::
214210
Creating Links between Pages
215211
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216212

217-
Speaking of web applications, creating links between pages is a must. Instead
218-
of hardcoding URLs in templates, the ``path`` function knows how to generate
213+
Creating links between pages is a must for web applications. Instead of
214+
hardcoding URLs in templates, the ``path`` function knows how to generate
219215
URLs based on the routing configuration. That way, all your URLs can be easily
220216
updated by just changing the configuration:
221217

@@ -224,9 +220,8 @@ updated by just changing the configuration:
224220
<a href="{{ path('_demo_hello', { 'name': 'Thomas' }) }}">Greet Thomas!</a>
225221

226222
The ``path`` function takes the route name and an array of parameters as
227-
arguments. The route name is the main key under which routes are referenced
228-
and the parameters are the values of the placeholders defined in the route
229-
pattern::
223+
arguments. The route name is the key under which routes are defined and the
224+
parameters are the values of the variables defined in the route pattern::
230225

231226
// src/Acme/DemoBundle/Controller/DemoController.php
232227
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
@@ -245,8 +240,9 @@ pattern::
245240

246241
.. tip::
247242

248-
The ``url`` function generates *absolute* URLs: ``{{ url('_demo_hello', {
249-
'name': 'Thomas'}) }}``.
243+
The ``url`` function is very similar to the ``path`` function, but generates
244+
*absolute* URLs, which is very handy when rendering emails and RSS files:
245+
``{{ url('_demo_hello', {'name': 'Thomas'}) }}``.
250246

251247
Including Assets: images, JavaScripts, and stylesheets
252248
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -265,13 +261,6 @@ Thanks to this function, you can move the application root directory anywhere
265261
under your web root directory without changing anything in your template's
266262
code.
267263

268-
Escaping Variables
269-
------------------
270-
271-
Twig is configured to automatically escape all output by default. Read Twig
272-
`documentation`_ to learn more about output escaping and the Escaper
273-
extension.
274-
275264
Final Thoughts
276265
--------------
277266

@@ -289,5 +278,5 @@ But I'm getting ahead of myself. First, you need to learn more about the control
289278
and that's exactly the topic of the :doc:`next part of this tutorial <the_controller>`.
290279
Ready for another 10 minutes with Symfony2?
291280

292-
.. _Twig: http://twig.sensiolabs.org/
293-
.. _documentation: http://twig.sensiolabs.org/documentation
281+
.. _Twig: http://twig.sensiolabs.org/
282+
.. _Twig documentation: http://twig.sensiolabs.org/documentation

‎reference/constraints/Callback.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ your validation function is ``Vendor\Package\Validator::validate()``::
160160

161161
class Validator
162162
{
163-
public function validate($object, ExecutionContextInterface $context)
163+
public static function validate($object, ExecutionContextInterface $context)
164164
{
165165
// ...
166166
}

‎reference/forms/types/form.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ on all fields.
6262
.. include:: /reference/forms/types/options/post_max_size_message.rst.inc
6363

6464
.. include:: /reference/forms/types/options/pattern.rst.inc
65+
66+
.. include:: /reference/forms/types/options/action.rst.inc
67+
68+
.. include:: /reference/forms/types/options/method.rst.inc
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.. versionadded:: 2.3
2+
The ``action`` option was introduced in Symfony 2.3.
3+
4+
action
5+
~~~~~~
6+
7+
**type**: ``string`` **default**: empty string
8+
9+
This option specifies where to send the form's data on submission (usually an
10+
URI). Its value is rendered as the ``action`` attribute of the ``form``
11+
element. An empty value is considered a same-document reference, i.e. the form
12+
will be submitted to the same URI that rendered the form.

‎reference/forms/types/options/checkbox_compound.rst.inc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ compound
44
**type**: ``boolean`` **default**: ``false``
55

66
This option specifies if a form is compound. As it's not the
7-
case for checkbox, by fefault the value is overriden with
7+
case for checkbox, by default the value is overridden with
88
``false`` value.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. versionadded:: 2.3
2+
The ``method`` option was introduced in Symfony 2.3.
3+
4+
method
5+
~~~~~~
6+
7+
**type**: ``string`` **default**: ``POST``
8+
9+
This option specifies the HTTP method used to submit the form's data. Its
10+
value is rendered as the ``method`` attribute of the ``form`` element and is
11+
used to decide whether to process the form submission in the
12+
``handleRequest()`` method after submission. Possible values are:
13+
14+
* POST
15+
* GET
16+
* PUT
17+
* DELETE
18+
* PATCH
19+
20+
.. note:
21+
22+
When the method is PUT, PATCH, or DELETE, Symfony will automatically
23+
render a ``_method`` hidden field in your form. This is used to "fake"
24+
these HTTP methods, as they're not supported on standard browsers. For
25+
more information, see :doc:`/cookbook/routing/method_parameters`.
26+
27+
.. note:
28+
29+
Only the PATCH method allows submitting partial data without that missing
30+
fields are set to ``null`` in the underlying data (preserving default
31+
values, if any).

0 commit comments

Comments
 (0)
Please sign in to comment.