Skip to content

Commit

Permalink
Merge branch '2.8'
Browse files Browse the repository at this point in the history
* 2.8: (35 commits)
  [#5922] Tweaks thanks to reviewers
  Many tweaks thanks to a great review
  language tweak
  several other tweaks
  Completely updating the form type reference section for the text -> TextType changes
  Use path() and url() PHP templating helpers
  Update voter section of best practices
  Added minimal cookbook article about shared
  Document deprecation of supports{Attribute,Class}() methods
  Document Security key to secret renamings
  Use new Simple{Form,Pre}AuthenticatorInterface namespaces
  tweaks thanks to Javier
  adding a section about calling isGranted() from within a voter
  Reworking the voter article for the new Voter class
  fixing build error
  Another huge batch of changes for the form type changes in 2.8
  minor tweaks #5834
  Fixing bad merge
  removing link to an old, non-existent article
  Many fixes thanks to great review from ogizanagi, javiereguiluz and others
  ...
  • Loading branch information
weaverryan committed Nov 30, 2015
2 parents af97ce1 + 41670b0 commit 7bb2785
Show file tree
Hide file tree
Showing 91 changed files with 2,268 additions and 939 deletions.
45 changes: 13 additions & 32 deletions best_practices/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ form in its own PHP class::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;

class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('summary', 'textarea')
->add('content', 'textarea')
->add('authorEmail', 'email')
->add('publishedAt', 'datetime')
->add('summary', TextareaType::class)
->add('content', TextareaType::class)
->add('authorEmail', EmailType::class)
->add('publishedAt', DateTimeType::class)
;
}

Expand All @@ -42,22 +45,17 @@ form in its own PHP class::
'data_class' => 'AppBundle\Entity\Post'
));
}

public function getName()
{
return 'post';
}
}

To use the class, use ``createForm`` and instantiate the new class::
To use the class, use ``createForm`` and pass the fully qualified class name::

use AppBundle\Form\PostType;
// ...

public function newAction(Request $request)
{
$post = new Post();
$form = $this->createForm(new PostType(), $post);
$form = $this->createForm(PostType::class, $post);

// ...
}
Expand Down Expand Up @@ -97,7 +95,7 @@ directly in your form class, this would effectively limit the scope of that form
{
$builder
// ...
->add('save', 'submit', array('label' => 'Create Post'))
->add('save', SubmitType::class, array('label' => 'Create Post'))
;
}
Expand All @@ -112,6 +110,7 @@ some developers configure form buttons in the controller::

use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use AppBundle\Entity\Post;
use AppBundle\Form\PostType;

Expand All @@ -122,8 +121,8 @@ some developers configure form buttons in the controller::
public function newAction(Request $request)
{
$post = new Post();
$form = $this->createForm(new PostType(), $post);
$form->add('submit', 'submit', array(
$form = $this->createForm(PostType::class, $post);
$form->add('submit', SubmitType::class, array(
'label' => 'Create',
'attr' => array('class' => 'btn btn-default pull-right')
));
Expand Down Expand Up @@ -207,21 +206,3 @@ Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement
for clarity. This isn't technically needed, since ``isValid()`` first calls
``isSubmitted()``. But without this, the flow doesn't read well as it *looks*
like the form is *always* processed (even on the GET request).

Custom Form Field Types
-----------------------

.. best-practice::

Add the ``app_`` prefix to your custom form field types to avoid collisions.

Custom form field types inherit from the ``AbstractType`` class, which defines the
``getName()`` method to configure the name of that form type. These names must
be unique in the application.

If a custom form type uses the same name as any of the Symfony's built-in form
types, it will override it. The same happens when the custom form type matches
any of the types defined by the third-party bundles installed in your application.

Add the ``app_`` prefix to your custom form field types to avoid name collisions
that can lead to hard to debug errors.
60 changes: 45 additions & 15 deletions best_practices/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,37 +264,66 @@ the same ``getAuthorEmail`` logic you used above:
namespace AppBundle\Security;
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
use AppBundle\Entity\Post;
// AbstractVoter class requires Symfony 2.6 or higher version
class PostVoter extends AbstractVoter
// Voter class requires Symfony 2.8 or higher version
class PostVoter extends Voter
{
const CREATE = 'create';
const EDIT = 'edit';
protected function getSupportedAttributes()
/**
* @var AccessDecisionManagerInterface
*/
private $decisionManager;
public function __construct(AccessDecisionManagerInterface $decisionManager)
{
return array(self::CREATE, self::EDIT);
$this->decisionManager = $decisionManager;
}
protected function getSupportedClasses()
protected function supports($attribute, $subject)
{
return array('AppBundle\Entity\Post');
if (!in_array($attribute, array(self::CREATE, self::EDIT))) {
return false;
}
if (!$subject instanceof Post) {
return false;
}
return true;
}
protected function isGranted($attribute, $post, $user = null)
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
/** @var Post */
$post = $subject; // $subject must be a Post instance, thanks to the supports method
if (!$user instanceof UserInterface) {
return false;
}
if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) {
return true;
}
if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) {
return true;
switch ($attribute) {
case self::CREATE:
// if the user is an admin, allow them to create new posts
if ($this->decisionManager->decide($token, array('ROLE_ADMIN'))) {
return true;
}
break;
case self::EDIT:
// if the user is the author of the post, allow them to edit the posts
if ($user->getEmail() === $post->getAuthorEmail()) {
return true;
}
break;
}
return false;
Expand All @@ -310,6 +339,7 @@ To enable the security voter in the application, define a new service:
# ...
post_voter:
class: AppBundle\Security\PostVoter
arguments: ['@security.access.decision_manager']
public: false
tags:
- { name: security.voter }
Expand Down Expand Up @@ -337,7 +367,7 @@ via the even easier shortcut in a controller:
*/
public function editAction($id)
{
$post = // query for the post ...
$post = ...; // query for the post
$this->denyAccessUnlessGranted('edit', $post);
Expand Down
Loading

0 comments on commit 7bb2785

Please sign in to comment.