Skip to content

Added shortcut methods for controllers #4109

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
30 changes: 21 additions & 9 deletions book/controller.rst
Original file line number Diff line number Diff line change
Expand Up @@ -430,24 +430,34 @@ Redirecting
~~~~~~~~~~~

If you want to redirect the user to another page, use the
:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::redirect`
:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::redirectToRoute`
method::

public function indexAction()
{
return $this->redirect($this->generateUrl('homepage'));
return $this->redirectToRoute('homepage');
Copy link
Member

Choose a reason for hiding this comment

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

Ah, actually, since this is the first example showing redirectToRoute, we should put the commented-out example showing redirect() and generateUrl() (the code block I added in my comment here: https://github.com/symfony/symfony-docs/pull/4109/files#r22320539) to this code block, not the one below.


// redirectToRoute is equivalent to using redirect() and generateUrl() together:
// return $this->redirect($this->generateUrl('homepage'), 301);
}

The ``generateUrl()`` method is just a helper function that generates the URL
for a given route. For more information, see the :doc:`Routing </book/routing>`
chapter.
.. versionadded:: 2.6
The ``redirectToRoute()`` method was added in Symfony 2.6. Previously (and still now), you
could use ``redirect()`` and ``generateUrl()`` together for this (see the example below).
Copy link
Member

Choose a reason for hiding this comment

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

(see the example above)


Or, if you want to redirect externally, just use ``redirect()`` and pass it the URL::

public function indexAction()
Copy link
Contributor

Choose a reason for hiding this comment

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

if we remove this, we have to document the generateUrl and the redirect method? redirectToRoute won't replace both?

Copy link
Member

Choose a reason for hiding this comment

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

redirect() is now only useful when you know the URL you want to redirect to and don't have to generate it (redirecting to an external URL for example). I'm not really sure if there really is a use case for generateUrl() any more.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should show both, the second one commented out. So, something like:

public function indexAction()
{
    // redirectToRoute is equivalent to using redirect() and generateUrl() together:
    // return $this->redirect($this->generateUrl('homepage'), 301);

    return $this->redirectToRoute('homepage', 301);
}

And then we can add one more thing right below this code block

Or, if you want to redirect externally, just use ``redirect()`` and pass it the URL::

    public function indexAction()
    {
        return $this->redirect('http://symfony.com/doc');
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

there is no reason to not document generateUrl and redirect.

{
return $this->redirect('http://symfony.com/doc');
}

By default, the ``redirect()`` method performs a 302 (temporary) redirect. To
By default, the ``redirectToRoute()`` method performs a 302 (temporary) redirect. To
perform a 301 (permanent) redirect, modify the second argument::

public function indexAction()
{
return $this->redirect($this->generateUrl('homepage'), 301);
return $this->redirectToRoute('homepage', 301);
}

.. tip::
Expand Down Expand Up @@ -623,12 +633,14 @@ For example, imagine you're processing a form submit::
if ($form->isValid()) {
// do some sort of processing

$request->getSession()->getFlashBag()->add(
$this->addFlash(
'notice',
'Your changes were saved!'
);
Copy link
Member

Choose a reason for hiding this comment

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

Same thing as with redirectToRoute, I think we should show the "old" (or manual) way of doing this commented-out. This will help users know what's really going on and how they might add a flash message if they were not in a controller.


return $this->redirect($this->generateUrl(...));
// $this->addFlash is equivalent to $this->get('session')->getFlashBag()->add

return $this->redirectToRoute(...);
}

return $this->render(...);
Expand Down
2 changes: 1 addition & 1 deletion book/doctrine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ you have a route that maps a product id to an update action in a controller::
$product->setName('New product name!');
$em->flush();

return $this->redirect($this->generateUrl('homepage'));
return $this->redirectToRoute('homepage');
}

Updating an object involves just three steps:
Expand Down
6 changes: 3 additions & 3 deletions book/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ controller::
if ($form->isValid()) {
// perform some action, such as saving the task to the database

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}

// ...
Expand Down Expand Up @@ -319,7 +319,7 @@ querying if the "Save and add" button was clicked::
? 'task_new'
: 'task_success';

return $this->redirect($this->generateUrl($nextAction));
return $this->redirectToRoute($nextAction);
}

.. index::
Expand Down Expand Up @@ -1233,7 +1233,7 @@ it after a form submission can be done when the form is valid::
$em->persist($task);
$em->flush();

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}

If, for some reason, you don't have access to your original ``$task`` object,
Expand Down
2 changes: 1 addition & 1 deletion book/propel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ have a route that maps a product id to an update action in a controller::
$product->setName('New product name!');
$product->save();

return $this->redirect($this->generateUrl('homepage'));
return $this->redirectToRoute('homepage');
}

Updating an object involves just three steps:
Expand Down
79 changes: 76 additions & 3 deletions book/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,77 @@ user to be logged in to access this URL:

.. code-block:: php

'access_control' => array(
array(
'path' => '^/cart/checkout',
'role' => 'IS_AUTHENTICATED_ANONYMOUSLY',
'requires_channel' => 'https',
),
),

.. _book-security-securing-controller:

Securing a Controller
~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 2.6
The ``denyAccessUnlessGranted()`` method was introduced in Symfony 2.6. Previously (and
still now), you could check access directly and throw the ``AccessDeniedException`` as shown
in the example below).
Copy link
Member

Choose a reason for hiding this comment

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

I would move it above the paragraph (right below the "Securing a Controller" headline).


Protecting your application based on URL patterns is easy, but may not be
fine-grained enough in certain cases. When necessary, you can easily force
authorization from inside a controller::

// ...

public function helloAction($name)
{
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
Copy link
Member

Choose a reason for hiding this comment

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

Same thing - I'd like to show the "old" way (which would now use the security.authorization_checker service in 2.6) in a comment.

Copy link
Member

Choose a reason for hiding this comment

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

And we need a versionadded note above this:

.. versionadded:: 2.6
    The ``denyAccessUnlessGranted()`` method was introduced in Symfony 2.6. Previously (and
    still now), you could check access directly and throw the ``AccessDeniedException`` as shown
    in the example below).

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should add a brief explanation of the weird null middle argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right.


// The second parameter is used to specify on what object the role is tested.
//
// Old way :
// if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
// throw $this->createAccessDeniedException('Unable to access this page!');
// }

// ...
}

.. _book-security-securing-controller-annotations:

.. versionadded:: 2.5
The ``createAccessDeniedException`` method was introduced in Symfony 2.5.

The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException`
method creates a special :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`
object, which ultimately triggers a 403 HTTP response inside Symfony.

Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations::

// ...
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

/**
* @Security("has_role('ROLE_ADMIN')")
*/
public function helloAction($name)
{
// ...
}

For more information, see the
:doc:`FrameworkExtraBundle documentation </bundles/SensioFrameworkExtraBundle/annotations/security>`.

Securing other Services
~~~~~~~~~~~~~~~~~~~~~~~

In fact, anything in Symfony can be protected using a strategy similar to
the one seen in the previous section. For example, suppose you have a service
(i.e. a PHP class) whose job is to send emails from one user to another.
You can restrict use of this class - no matter where it's being used from -
to users that have a specific role.
Copy link
Member

Choose a reason for hiding this comment

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

The following code block doesn't really belong to this paragraph.

// app/config/security.php
$container->loadFromExtension('security', array(
// ...
Expand Down Expand Up @@ -798,9 +869,7 @@ You can easily deny access from inside a controller::

public function helloAction($name)
{
if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
throw $this->createAccessDeniedException();
}
$this->denyAccessUnlessGranted('ROLE_ADMIN');

// ...
}
Expand Down Expand Up @@ -833,6 +902,10 @@ using annotations::
*/
public function helloAction($name)
{
$this->denyAccessUnlessGranted(new Expression(
'"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'
));

// ...
}

Expand Down
2 changes: 1 addition & 1 deletion book/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ workflow looks like the following from inside a controller::
if ($form->isValid()) {
// the validation passed, do something with the $author object

return $this->redirect($this->generateUrl(...));
return $this->redirectToRoute(...);
}

return $this->render('BlogBundle:Author:form.html.twig', array(
Expand Down
2 changes: 1 addition & 1 deletion components/form/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ method:

// ... perform some action, such as saving the data to the database

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}

// ...
Expand Down
6 changes: 3 additions & 3 deletions cookbook/doctrine/file_uploads.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ The following controller shows you how to handle the entire process::
$em->persist($document);
$em->flush();

return $this->redirect($this->generateUrl(...));
return $this->redirectToRoute(...);
}

return array('form' => $form->createView());
Expand All @@ -267,7 +267,7 @@ in a moment to handle the file upload::
$em->persist($document);
$em->flush();

return $this->redirect(...);
return $this->redirectToRoute(...);
}

The ``upload()`` method will take advantage of the :class:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile`
Expand Down Expand Up @@ -432,7 +432,7 @@ call to ``$document->upload()`` should be removed from the controller::
$em->persist($document);
$em->flush();

return $this->redirect(...);
return $this->redirectToRoute(...);
}

.. note::
Expand Down
2 changes: 1 addition & 1 deletion cookbook/doctrine/registration_form.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ the validation and saves the data into the database::
$em->persist($registration->getUser());
$em->flush();

return $this->redirect(...);
return $this->redirectToRoute(...);
}

return $this->render(
Expand Down
6 changes: 3 additions & 3 deletions cookbook/form/direct_submit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ submissions::
if ($form->isValid()) {
// perform some action...

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}

return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
Expand Down Expand Up @@ -66,7 +66,7 @@ method, pass the submitted data directly to
if ($form->isValid()) {
// perform some action...

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}
}

Expand Down Expand Up @@ -111,7 +111,7 @@ a convenient shortcut to the previous example::
if ($form->isValid()) {
// perform some action...

return $this->redirect($this->generateUrl('task_success'));
return $this->redirectToRoute('task_success');
}
}

Expand Down
2 changes: 1 addition & 1 deletion cookbook/form/form_collections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ the relationship between the removed ``Tag`` and ``Task`` object.
$em->flush();

// redirect back to some edit page
return $this->redirect($this->generateUrl('task_edit', array('id' => $id)));
return $this->redirectToRoute('task_edit', array('id' => $id));
}

// render some form template
Expand Down
6 changes: 1 addition & 5 deletions cookbook/security/remember_me.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,7 @@ In the following example, the action is only allowed if the user has the

public function editAction()
{
if (false === $this->get('security.authorization_checker')->isGranted(
'IS_AUTHENTICATED_FULLY'
)) {
throw new AccessDeniedException();
}
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

// ...
}
Expand Down
4 changes: 1 addition & 3 deletions cookbook/security/securing_services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ and checking the current user's role::

public function helloAction($name)
{
if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
$this->denyAccessUnlessGranted('ROLE_ADMIN');

// ...
}
Expand Down
12 changes: 6 additions & 6 deletions quick_tour/the_controller.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,13 +328,13 @@ You can also store "flash messages" that will auto-delete after the next request
They are useful when you need to set a success message before redirecting the
user to another page (which will then show the message)::

public function indexAction(Request $request)
{
// ...
// store a message for the very next request (in a controller)
$this->addFlash('notice', 'Congratulations, your action succeeded!');

// store a message for the very next request
$this->addFlash('notice', 'Congratulations, your action succeeded!');
}
.. code-block:: html+jinja

{# display the flash message in the template #}
<div>{{ app.session.flashbag.get('notice') }}</div>

And you can display the flash message in the template like this:

Expand Down