为多个实体使用一个 class 选民
Using one class Voter for many entities
这是 class Voter
中的函数 supportsClass
http://symfony.com/doc/2.5/cookbook/security/voters_data_permission.html
public function supportsClass($class)
{
$supportedClass = 'AppBundle\Entity\Post';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
我想知道是否可以对同一捆绑包中的多个实体使用一个 class 选民,或者我必须为每个实体创建一个选民?
编辑
我找到了这个解决方案:
public function supportsClass($class)
{
$classes = array(
'Project\AgenceBundle\Entity\Agence',
'Project\AgenceBundle\Entity\SubAgence',
'Project\AgenceBundle\Entity\Subscription'
);
$ok = false;
foreach($classes as $supportedClass)
$ok = $ok || $supportedClass === $class || is_subclass_of($class, $supportedClass);
return $ok;
}
简而言之,您可以随心所欲地重复使用您的选民。例如,您的选民可以针对界面工作。
但是你不应该为了节省几行代码而使用投票器来判断太多的东西。可能如果选民可以判断一组不是派生的对象 class 但它们有一些共同点。这反过来又是接口和特征的好地方。因此选民应该针对该界面工作。
这就是接口的用途,给你合同。
如果您在 supportsClass
中有 classes 数组,并且将来您可以更改其中一个中的某些内容。您可能会为 class 破坏 Voter,但由于它不受接口约束,因此没有静态分析或 PHP 解释器会捕获它。这是一个很大的问题。
如您所见,Voter
由 3 个部分组成。
- supportsClass 告诉 Symfony 这个
Voter
是否可以决定某些 class. 的对象
- supportsAttribute 告诉 Symfony 这个
Voter
是否可以决定这个动作。
- 投票 根据传递的对象决定是否 yes/no/dunno
这不完全它是如何工作的。但它应该让你知道投票者的目的是什么。
你:
//You in controller
if (!$this->get('security.context')->isGranted('edit', $object)) {
throw new AuthenticationException('Not a step furher chap!');
}
框架:
//security.context
//again it is rough idea what it does for real implementation check Symfoy github
public function isGranted($action, $object) {
//There it goes trough all voters from all bundles!
foreach ($this->voters as $voter) {
if (!$voter->supportsClass(get_class($object))) {
//this voter doesn't care about this object
continue;
}
if (!$voter->supportsAttribute($action)) {
//this voter does care about this object but not about this action on it
continue;
}
//This voter is there to handle this object and action, so lest se what it has to say about it
$answer = $voter->vote(..);
...some more logic
}
}
我脑海中的奇怪例子:
interface Owneable {
public function getOwnerId();
}
trait Owned {
/**
* @ORM....
*/
protected $ownerId;
public function getOwnerId() {
return $this->ownerId;
}
public function setOwnerId($id) {
$this->ownerId = $id;
}
}
class Post implements Owneable {
use Owned;
}
class Comment implements Owneable {
use Owned;
}
class OwnedVoter implements VoterInterface
{
public function supportsAttribute($attribute)
{
return $attribute === 'edit';
}
public function supportsClass($class)
{
//same as return is_subclass_of($class, 'Owneable');
$interfaces = class_implements($class);
return in_array('Owneable' , $interfaces);
}
public function vote(TokenInterface $token, $ownedObject, array $attributes)
{
if (!$this->supportsClass(get_class($ownedObject))) {
return VoterInterface::ACCESS_ABSTAIN;
}
if (!$this->supportsAttribute($attributes[0])) {
return VoterInterface::ACCESS_ABSTAIN;
}
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
$userOwnsObject = $user->getId() === $ownedObject->getOwnerId();
if ($userOwnsObject) {
return VoterInterface::ACCESS_GRANTED;
}
return VoterInterface::ACCESS_DENIED;
}
}
提示:选民只是 class 和其他任何人一样,继承和抽象 class 等东西在这里也适用!
提示 2:选民已注册为服务,您可以将 security.context
或任何其他服务传递给它。所以你可以很好地重用你的代码
这是 class Voter
中的函数 supportsClasshttp://symfony.com/doc/2.5/cookbook/security/voters_data_permission.html
public function supportsClass($class)
{
$supportedClass = 'AppBundle\Entity\Post';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
我想知道是否可以对同一捆绑包中的多个实体使用一个 class 选民,或者我必须为每个实体创建一个选民?
编辑 我找到了这个解决方案:
public function supportsClass($class)
{
$classes = array(
'Project\AgenceBundle\Entity\Agence',
'Project\AgenceBundle\Entity\SubAgence',
'Project\AgenceBundle\Entity\Subscription'
);
$ok = false;
foreach($classes as $supportedClass)
$ok = $ok || $supportedClass === $class || is_subclass_of($class, $supportedClass);
return $ok;
}
简而言之,您可以随心所欲地重复使用您的选民。例如,您的选民可以针对界面工作。
但是你不应该为了节省几行代码而使用投票器来判断太多的东西。可能如果选民可以判断一组不是派生的对象 class 但它们有一些共同点。这反过来又是接口和特征的好地方。因此选民应该针对该界面工作。 这就是接口的用途,给你合同。
如果您在 supportsClass
中有 classes 数组,并且将来您可以更改其中一个中的某些内容。您可能会为 class 破坏 Voter,但由于它不受接口约束,因此没有静态分析或 PHP 解释器会捕获它。这是一个很大的问题。
如您所见,Voter
由 3 个部分组成。
- supportsClass 告诉 Symfony 这个
Voter
是否可以决定某些 class. 的对象
- supportsAttribute 告诉 Symfony 这个
Voter
是否可以决定这个动作。 - 投票 根据传递的对象决定是否 yes/no/dunno
这不完全它是如何工作的。但它应该让你知道投票者的目的是什么。
你:
//You in controller
if (!$this->get('security.context')->isGranted('edit', $object)) {
throw new AuthenticationException('Not a step furher chap!');
}
框架:
//security.context
//again it is rough idea what it does for real implementation check Symfoy github
public function isGranted($action, $object) {
//There it goes trough all voters from all bundles!
foreach ($this->voters as $voter) {
if (!$voter->supportsClass(get_class($object))) {
//this voter doesn't care about this object
continue;
}
if (!$voter->supportsAttribute($action)) {
//this voter does care about this object but not about this action on it
continue;
}
//This voter is there to handle this object and action, so lest se what it has to say about it
$answer = $voter->vote(..);
...some more logic
}
}
我脑海中的奇怪例子:
interface Owneable {
public function getOwnerId();
}
trait Owned {
/**
* @ORM....
*/
protected $ownerId;
public function getOwnerId() {
return $this->ownerId;
}
public function setOwnerId($id) {
$this->ownerId = $id;
}
}
class Post implements Owneable {
use Owned;
}
class Comment implements Owneable {
use Owned;
}
class OwnedVoter implements VoterInterface
{
public function supportsAttribute($attribute)
{
return $attribute === 'edit';
}
public function supportsClass($class)
{
//same as return is_subclass_of($class, 'Owneable');
$interfaces = class_implements($class);
return in_array('Owneable' , $interfaces);
}
public function vote(TokenInterface $token, $ownedObject, array $attributes)
{
if (!$this->supportsClass(get_class($ownedObject))) {
return VoterInterface::ACCESS_ABSTAIN;
}
if (!$this->supportsAttribute($attributes[0])) {
return VoterInterface::ACCESS_ABSTAIN;
}
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
$userOwnsObject = $user->getId() === $ownedObject->getOwnerId();
if ($userOwnsObject) {
return VoterInterface::ACCESS_GRANTED;
}
return VoterInterface::ACCESS_DENIED;
}
}
提示:选民只是 class 和其他任何人一样,继承和抽象 class 等东西在这里也适用!
提示 2:选民已注册为服务,您可以将 security.context
或任何其他服务传递给它。所以你可以很好地重用你的代码