Это функция 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);
}
Я хотел бы знать, возможно ли использовать одного избирателя класса для многих объектов в одном и том же пакете, или мне нужно создать избирателя для каждого объекта?
РЕДАКТИРОВАТЬ
Я нашел это решение:
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;
}
Короче говоря, вы можете использовать своего избирателя столько раз, сколько захотите. Например, ваш избиратель может работать против интерфейса.
Однако вы не должны использовать избирателя, чтобы судить слишком много вещей, просто чтобы сохранить несколько строк кода. Вероятно, если избиратель может судить о некотором наборе объектов, которые не являются производным классом, но имеют что-то общее. Что, в свою очередь, является хорошим местом для интерфейса и особенностей. И поэтому избиратель должен работать против этого интерфейса.
Вот для чего существуют интерфейсы, чтобы дать вам контракт.
Если у вас есть массив классов в supportsClass
и чем в будущем вы что-то измените в одном из них. Вы можете отключить Voter для этого класса, но так как он не связан интерфейсом, никакой статический анализ или интерпретатор PHP не поймают его. И это довольно проблематично.
Как вы видете Voter
это сборка из 3-х частей.
Voter
может определиться с предметами определенного класса.Voter
может решить об этом действии.Это не совсем как это устроено. Но это должно дать вам представление, для чего нужен этот избиратель.
Вы:
//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;
}
}
СОВЕТ: Избиратель — просто класс, как и любой другой, здесь работают такие вещи, как наследование и абстрактный класс!
СОВЕТ2: избиратель зарегистрирован как услуга, которую вы можете передать security.context
или любой другой сервис к нему. Таким образом, вы можете использовать свой код довольно хорошо
Других решений пока нет …