代码之家  ›  专栏  ›  技术社区  ›  Wildcard27

使用Api平台,自动将用户分配给对象(OneToMany)

  •  2
  • Wildcard27  · 技术社区  · 6 年前

    以下是我的设想:

    我有一个 Article User ). 一个用户可以拥有许多文章。用户可以通过API发布文章。

    我想要一个 user_id 根据承载令牌自动设置文章的列(我使用的是JWT auth)。

    我找不到任何关于如何做到这一点的文件。有人能帮我怎么做到这一点吗?

    以下是我的实体:

    <?php
    
    namespace App\Entity;
    
    use ApiPlatform\Core\Annotation\ApiResource;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
    use Symfony\Component\Security\Core\User\UserInterface;
    
    /**
     * @ApiResource()
     * @ORM\Table(name="users")
     * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
     * @UniqueEntity(fields="email", message="Email already taken")
     */
    class User implements UserInterface, \Serializable
    {
    
        /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        /**
         * @var string $password
         *
         * @ORM\Column(type="string", length=64)
         * @Assert\NotBlank()
         */
        private $password;
    
        /**
         * @var string $plainPassword
         *
         * @Assert\NotBlank()
         * @Assert\Length(max=4096)
         */
        private $plainPassword;
    
        /**
         * @var string $email
         *
         * @ORM\Column(type="string", length=254, unique=true)
         * @Assert\NotBlank()
         * @Assert\Email()
         */
        private $email;
    
        /**
         * @var bool $isActive
         *
         * @ORM\Column(name="is_active", type="boolean")
         */
        private $isActive;
    
        /**
         * @ORM\OneToMany(targetEntity="Article", mappedBy="user")
         */
        private $articles;
    
        /**
         * @ORM\Column(type="array")
         */
        private $roles;
    
        public function __construct($email)
        {
            $this->isActive = true;
            $this->email = $email;
            $this->articles = new ArrayCollection();
        }
    
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * @return string
         */
        public function getUsername()
        {
            return $this->email;
        }
    
        /**
         * @return string
         */
        public function getEmail()
        {
            return $this->email;
        }
    
        /**
         * @param string $email
         *
         * @return $this
         */
        public function setEmail($email)
        {
            $this->email = $email;
    
            return $this;
        }
    
        /**
         * @return null|string
         */
        public function getSalt()
        {
            return null;
        }
    
        /**
         * @return string
         */
        public function getPassword()
        {
            return $this->password;
        }
    
        /**
         * @param string $password
         *
         * @return $this
         */
        public function setPassword($password)
        {
            $this->password = $password;
    
            return $this;
        }
    
        /**
         * @return array
         */
        public function getRoles()
        {
            return ['ROLE_USER'];
        }
    
        public function eraseCredentials()
        {
        }
    
        /** @see \Serializable::serialize() */
        public function serialize()
        {
            return serialize(array(
                $this->id,
                $this->email,
                $this->password,
            ));
        }
    
        /** @see \Serializable::unserialize()
         * @param $serialized
         */
        public function unserialize($serialized)
        {
            list (
                $this->id,
                $this->email,
                $this->password,
                ) = unserialize($serialized, array('allowed_classes' => false));
        }
    }
    

    文章

    <?php
    
    namespace App\Entity;
    
    use ApiPlatform\Core\Annotation\ApiResource;
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;
    
    /**
     * A User's article
     *
     * @ORM\Table(name="articles")
     * @ApiResource(
     *     attributes={"access_control"="is_granted('ROLE_USER')"},
     *     collectionOperations={
     *         "get",
     *         "post"={"access_control"="is_granted('ROLE_USER')"}
     *     },
     *     itemOperations={
     *         "get"={"access_control"="is_granted('ROLE_USER') and object.owner == user"}
     *     }
     * )
     * @ORM\Entity
     * @ORM\HasLifecycleCallbacks()
     */
    class Article
    {
    
        /**
         * @var int $id
         *
         * @ORM\Id
         * @ORM\GeneratedValue
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @var string $user
         *
         * @ORM\ManyToOne(targetEntity="User", inversedBy="articles")
         */
        private $user;
    
        /**
         * @var string $name
         *
         * @ORM\Column(type="text")
         * @Assert\NotBlank()
         */
        private $name;
    
        /**
         * @var string $location
         *
         * @ORM\Column(type="text")
         */
        private $location;
    
        /**
         * @var \DateTimeInterface $createdAt
         *
         * @ORM\Column(type="datetime_immutable")
         */
        private $createdAt;
    
        /**
         * @var \DateTimeInterface $updatedAt
         *
         * @ORM\Column(type="date_immutable", nullable=true)
         */
        private $updatedAt;
    
        /**
         * @ORM\PrePersist()
         */
        public function setCreatedAt()
        {
            $this->createdAt = new \DateTime();
        }
    
        /**
         * @ORM\PreUpdate()
         */
        public function setUpdatedAt()
        {
            $this->updatedAt = new \DateTime();
        }
    
        /**
         * @return int
         */
        public function getId(): int
        {
            return $this->id;
        }
    
        /**
         * @param int $id
         */
        public function setId(int $id): void
        {
            $this->id = $id;
        }
    
        /**
         * @return string
         */
        public function getUser(): string
        {
            return $this->user;
        }
    
        /**
         * @return string
         */
        public function getName(): string
        {
            return $this->name;
        }
    
        /**
         * @param string $name
         */
        public function setName(string $name): void
        {
            $this->name = $name;
        }
    
        /**
         * @return string
         */
        public function getLocation(): string
        {
            return $this->location;
        }
    
        /**
         * @param string $location
         */
        public function setLocation(string $location): void
        {
            $this->location = $location;
        }
    
    
    }
    
    1 回复  |  直到 6 年前
        1
  •  14
  •   mblaettermann    6 年前

    这应该可以使用 EventListener : https://api-platform.com/docs/core/events

    有了这些,您就可以在不使用新控制器的情况下连接到apiplate进程的内部进程。非常适合您的用例。

    实现可以如下所示:

    <?php
    // api/src/EventSubscriber/AddOwnerToArticleSubscriber.php
    
    namespace App\EventSubscriber;
    
    use ApiPlatform\Core\EventListener\EventPriorities;
    use App\Entity\Article;
    use App\Entity\User;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
    use Symfony\Component\HttpKernel\KernelEvents;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
    
    final class AddOwnerToArticleSubscriber implements EventSubscriberInterface
    {
    
        /**
         * @var TokenStorageInterface
         */
        private $tokenStorage;
    
        public function __construct(TokenStorageInterface $tokenStorage)
        {
    
            $this->tokenStorage = $tokenStorage;
        }
    
        public static function getSubscribedEvents()
        {
            return [
                KernelEvents::VIEW => ['attachOwner', EventPriorities::PRE_WRITE],
            ];
        }
    
        public function attachOwner(GetResponseForControllerResultEvent $event)
        {
            $article = $event->getControllerResult();
            $method = $event->getRequest()->getMethod();
    
            if (!$article instanceof Article || Request::METHOD_POST !== $method) {
    
                // Only handle Article entities (Event is called on any Api entity)
                return;
            }
    
            // maybe these extra null checks are not even needed
            $token = $this->tokenStorage->getToken();
            if (!$token) {
                return;
            }
    
            $owner = $token->getUser();
            if (!$owner instanceof User) {
                return;
            }
    
    
            // Attach the user to the not yet persisted Article
            $article->setUser($owner);
    
        }
    }
    
        2
  •  1
  •   Dharman Aman Gojariya    4 年前

    <?php
       
    namespace App\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Serializer\Annotation\Groups;
    
    /**
     * @ORM\MappedSuperclass()
     */
    class Base implements PublishedInfoEntityInterface
    {
    
        /**
         * @ORM\Column(type="datetime", nullable=true)
         */
        private $createdAt;
    
        /**
         * @ORM\Column(type="datetime", nullable=true)
         */
        private $modifiedAt;
    
        /**
         * @ORM\ManyToOne(targetEntity="User")
         * @ORM\JoinColumn(nullable=true)
         */
        private $createdBy;
    
        /**
         * @ORM\ManyToOne(targetEntity="User")
         * @ORM\JoinColumn(nullable=true)
         */
        private $modifiedBy;
    
        /**
         * @ORM\Column(type="integer", nullable=true, length=2)
         */
        private $status;
    
        public function getCreatedAt()
        {
            return $this->createdAt;
        }
    
        public function setCreatedAt(\DateTimeInterface $createdAt): PublishedInfoEntityInterface
        {
            $this->createdAt = $createdAt;
    
            return $this;
        }
    
        public function getModifiedAt()
        {
            return $this->modifiedAt;
        }
    
        public function setModifiedAt(\DateTimeInterface $modifiedAt): PublishedInfoEntityInterface
        {
            $this->modifiedAt = $modifiedAt;
    
            return $this;
        }
    
        /**
         * @return User
         */
        public function getCreatedBy()
        {
            return $this->createdBy;
        }
    
        /**
         * @param User $createdBy
         * @return Base
         */
        public function setCreatedBy($createdBy): self
        {
            $this->createdBy = $createdBy;
            return $this;
        }
    
        /**
         * @return User
         */
        public function getModifiedBy()
        {
            return $this->modifiedBy;
        }
    
        /**
         * @param User $modifiedBy
         * @return Base
         */
        public function setModifiedBy($modifiedBy): self
        {
            $this->modifiedBy = $modifiedBy;
            return $this;
        }
    
        /**
         * @return int
         */
        public function getStatus()
        {
            return $this->status;
        }
    
        /**
         * @param integer $status
         */
        public function setStatus($status): void
        {
            $this->status = $status;
            return $this;
        }
    }
    

    <?php
    
    namespace App\EventSubscriber;
    
    use ApiPlatform\Core\EventListener\EventPriorities;
    use App\Entity\Base;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Event\ViewEvent;
    use Symfony\Component\HttpKernel\KernelEvents;
    use Symfony\Component\Security\Core\Security;
    
    class AuthoredEntitySubscriber implements EventSubscriberInterface
    {
        private $entityManager;
        /**
         * @var Security
         */
        private $security;
    
        public function __construct(EntityManagerInterface $entityManager,Security $security)
        {
            $this->entityManager = $entityManager;
            $this->security = $security;
        }
    
        public static function getSubscribedEvents()
        {
            return [KernelEvents::VIEW => ['setAuthor', EventPriorities::PRE_WRITE]];
        }
    
        public function setAuthor(ViewEvent $event)
        {
            $entity = $event->getControllerResult();
            $method = $event->getRequest()->getMethod();
            $role = $this->security->getToken()->getRoleNames();
    
            if (!$entity instanceof Base || !in_array($method, [Request::METHOD_POST, Request::METHOD_PUT]) || !$role) {
                return;
            }
    
             $entity->setModifiedBy($this->security->getUser());
    
             if (Request::METHOD_POST === $method) {
                 $entity->setCreatedBy($this->security->getUser());
             }
        }
    }
    

    如果要添加自动createAt和modifyAt,必须为createAt和modifyAt创建一个名为PublishedInfoEntityInterface的接口类,该类编写以下代码:

    <?php
    
    namespace App\Entity;
    
    interface PublishedInfoEntityInterface
    {
        public function setCreatedAt(\DateTimeInterface $dateTime): PublishedInfoEntityInterface;
    
        public function setModifiedAt(\DateTimeInterface $dateTime): PublishedInfoEntityInterface;
    }
    

    创建一个订阅服务器,自动填充createAt和modifyAt,如下所示

    <?php
    
    namespace App\EventSubscriber;
    
    
    use ApiPlatform\Core\EventListener\EventPriorities;
    use App\Entity\PublishedInfoEntityInterface;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\Event\ViewEvent;
    use Symfony\Component\HttpKernel\KernelEvents;
    
    class PublishedInfoEntitySubscriber implements EventSubscriberInterface
    {
    
        public static function getSubscribedEvents()
        {
            return [KernelEvents::VIEW => ['setDataTime', EventPriorities::PRE_WRITE]];
        }
    
        public function setDataTime(ViewEvent $event)
        {
            $entity = $event->getControllerResult();
            $method = $event->getRequest()->getMethod();
    
            if (!$entity instanceof PublishedInfoEntityInterface || !in_array($method, [Request::METHOD_POST, Request::METHOD_PUT])) {
                return;
            }
    
            $entity->setCreatedAt(new \DateTime());
    
            if (Request::METHOD_POST === $method){
                $entity->setModifiedAt(new \DateTime());
            }
        }
    }
    

    最后,每个你想要拥有这些属性的类,你只要像这样扩展这个类

    class User extends Base implements UserInterface
    

        3
  •  0
  •   Stormnorm    4 年前

    另一种方法是使用条令实体侦听器。

    
    class SetUserListener
    {
    
        private Security $security;
    
        public function __construct(Security $security)
        {
            $this->security = $security;
        }
    
        public function prePersist($obj)
        {
            if (!is_a($obj, Timer::class) &&
                !is_a($obj, DailySummary::class) &&
                !is_a($obj, Task::class)
            ) {
                return;
            }
    
            if ($this->security->getUser()) {
                $obj->setUser($this->security->getUser());
            }
        }
    }
    
    

    确保在服务中连接实体侦听器

        App\Doctrine\SetUserListener:
                tags: [ doctrine.orm.entity_listener ]
    
    推荐文章