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

停止Symfony 4通过EventListener更新关系实体

  •  0
  • bateman_ap  · 技术社区  · 7 年前

    我有一个具有多个关系的实体,这里有一个简化的示例:

    class Api
    {
       /**
       * @ORM\OneToMany(targetEntity="App\Entity\Score", mappedBy="api")
       */
       private $scores;
    
       public function __construct()
       {
           $this->scores = new ArrayCollection();
       }
    
       /**
       * @ORM\Column(type="string", length=400, nullable=true)
       */
       private $apiKey;
    
       /**
       * @return mixed
       */
       public function getApiKey() {
          return $this->apiKey;
       }
    
       /**
       * @param mixed $apiKey
       */
       public function setApiKey( $apiKey ): void {
          $this->apiKey = $apiKey;
       }
    

    OneToMany的另一面是这样的:

     class Score
     {
         /**
         * @ORM\ManyToOne(targetEntity="App\Entity\Api", inversedBy="scores")
         * @ORM\JoinColumn(nullable=true)
         */
         private $api;
    
         /**
         * @ORM\Column(type="decimal", precision=2, scale=5, nullable=true)
         */
         private $highScore;
    
         /**
         * @return mixed
         */
         public function getHighScore()
         {
             return $this->highScore;
         }
    
         /**
         * @param mixed $highScore
         */
         public function setHighScore($highScore): void
         {
             $this->highScore= $highScore;
         }
    

    所有这些都很好,但是我想对API密钥进行一些简单的加密,所以我使用openssl\u encrypt和openssl\u decrypt在编码时将iv存储在API表中,以便能够解码。为了自动执行此操作,我设置了一个EventListener,如下所示:

    class ApiSubscriber implements EventSubscriber
    {
        private $encryption;
    
        public function __construct(Encryption $encryption) {
            $this->encryption = $encryption;
        }
    
        public function getSubscribedEvents()
        {
            return array(
                'prePersist',
                'preUpdate',
                'postLoad'
            );
        }
    
        public function prePersist(LifecycleEventArgs $args)
        {
            $this->index($args);
        }
    
        public function preUpdate(LifecycleEventArgs $args)
        {
            $this->index($args);
        }
    
        public function postLoad(LifecycleEventArgs $args)
        {
            $entity = $args->getObject();
    
            if ($entity instanceof Api) {
                $apiSecret = $entity->getApiSecret();
                $iv = $entity->getIv();
                $encodedSecret = $this->encryption->decrypt($apiSecret, $iv);
                $entity->setApiSecret($encodedSecret);
            }
        }
    
        public function index(LifecycleEventArgs $args)
        {
            $entity = $args->getObject();
    
            if ($entity instanceof Api) {
                $apiSecret = $entity->getApiKey();
                $encodedSecret = $this->encryption->encrypt($apiSecret);
    
                $entity->setApiSecret($encodedSecret['encodedString']);
                $entity->setIv($encodedSecret['iv']);
    
                if($encodedSecret['success'])
                {
                    $entity->setApiKey($encodedSecret['encodedString']);
                    $entity->setIv($encodedSecret['iv']);
                }
            }
        }
    }
    

    同样,所有这些都非常好,但是当我更新分数实体时,我的问题来了。看起来似乎是因为它与Api有多通关系,所以会调用订阅者并使用新的iv密钥将ApiKey更新为新的编码值。

    这通常很好,但我需要在CLI命令中使用它,它在Api实体中循环并使用它们调用Api服务,但此更新会导致循环使用旧的过时信息并失败。

    您知道如何仅在直接修改Api实体时调用订阅者,而不是与之有关系的实体吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Alan T.    7 年前

    preUpdate 生命周期方法接收子类型 LifecycleEventArgs 这就是 PreUpdateEventArgs . 此类允许您访问正在更新的实体的更改集。

    如中所述 doctrine's documentation 你可以用它来改变你的行为 预更新 基于实体中实际更改的内容进行回调。

    在您的情况下,您需要使用 $args->hasChangedField 如果更新仅涉及关联字段,则跳过加密部分。如果且仅当加密过程中实际使用的字段发生更改时,您还可以改变此逻辑并执行加密部分。


    另一方面,多音关联的反面不应该有 预更新 如果修改了所属方,则会触发事件。讨论了这一特殊点 here . 因此,如果 预更新 您的 Api 实体被触发,这意味着其更改集不是空的,但它不能是与 scores 字段正常。