【问题标题】:Default value of Doctrine ORM associationDoctrine ORM 关联的默认值
【发布时间】:2020-12-12 07:06:10
【问题描述】:

我有实体(如下所示)。我想在创建时设置一些默认值。 正如您在__construct 中看到的,设置$name(字符串)很容易,但是如何设置$group? (例如我知道数据库中有一个id=122的组)

/**
 * @ORM\Entity
 */
class Person {
    private $id;

    /** @ORM\Column(type="string") */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="Group", inversedBy="persons")
     * @ORM\JoinColumn(referencedColumnName="id")
     */
    private $group;

    public function setGroup(Group $group)
    {
        $this->group = $group;
        $group->addPerson($this);
    }

    // ... setters/getters

    //construct default Person
    public function __construct()
    {
        $this->setName("Mike");
        $this->setGroup($EXISTING_GROUP_FROM_MY_DB); // <<--------------
    }
}

【问题讨论】:

  • 糟糕的设计。到这个在控制器中。

标签: php symfony doctrine-orm


【解决方案1】:

我同意 moonwave99 的观点,即这是糟糕的设计。在这里,您尝试从不支持容器的地方(即它不知道也不应该知道 Doctrine)访问数据库(通过 Doctrine 服务)。

我最近遇到了类似的问题...实际上几乎完全相同的问题。但我不希望这个逻辑出现在控制器内部。所以我写了一个服务来处理用户的创建。我让该服务可以访问它唯一需要的其他服务:Doctrine。

这是一个示例,其中创建了具有所有可用角色的用户:

namespace MyBundle\Entity;

class UserFactory
{
    private $doctrine;

    public function __construct($doctrine)
    {
        $this->doctrine = $doctrine;
    }

    public function generateNewUser($email, $password)
    {
        $user = new User();

        // Since you have access to the Doctrine service, you can use $this->doctrine
        // to do anything you would normally do in your controller with $this->getDoctrine()
        $roles = $this->doctrine->getEntityManager()->getRepository("MyBundle:Role")->findAll();

        foreach ($roles as $role)
        {
            $user->addRole($role);
        }

        return $user;
    }
}

现在在config.ymlservices.yml 中注册该服务,记得将Doctrine 服务传递给它:

services:
    mybundle.factory.user:
        class: MyBundle\Entity\UserFactory
        arguments: ['@doctrine']

就是这样......现在,在您的控制器中,您可以通过以下方式创建一个新用户:

public function MyController()
{
    $user = $this->get("mybundle.factory.user")->generateNewUser("someone@email.com", "password123");
}

【讨论】:

    【解决方案2】:

    推荐的方法是要求构造函数参数中的关联实体对象,可选地与工厂(例如实体存储库)结合,在实例化期间提供Group实体。这可确保实体始终处于有效状态。

    src/Entity/Person.php

    namespace App\Entity;
    
    /**
     * @ORM\Entity(repositoryClass="App\Repository\PersonRepository")
     */
    class Person 
    {
       //...
    
        public function __construct($name, Group $group)
        {
             $this->setName($name);
             $this->setGroup($group);
        }
    
        //...
    }
    

    src/Repsotory/PersonRepository.php

    namespace App\Repsotory;
    
    use App\Entity\Group;
    use App\Entity\Person;
    
    class PersonRepository
    {
        const DEFAULT_GROUP = 122;
    
        public function create($name, Group $group = null)
        {
            if (null === $group) {
                $group = $this->_em->getReference(Group::class, self::DEFAULT_GROUP);
            }
            $person = new Person($name, $group);
            $this->_em->persist($person);
     
            return $person;
        }
    }
    

    这允许您完全依赖 Doctrine ORM 实体管理器来维护默认的组关联。

    $person = $em->getRepository(Person::class)->create('Mike');
    $group = $person->getGroup();
    echo $group->getId(); //outputs: 122
    $em->flush();
    

    这种方法可以在 Symfony 中扩展以使用查询服务而不是理论实体存储库,以提供处理实体实例化的中心位置。

    在 Symfony 3.4+ 中,您可以使用 Repository services 为存储库提供依赖注入,而不是使用 EntityManagerInterface

    src/Service/PersonCreateQuery.php

    namespace App\Service;
    
    use App\Entity\Group;
    use App\Entity\Person;
    use Doctrine\ORM\EntityManagerInterface;
    
    class PersonCreateQuery
    {
        private $em;
    
        public function __construct(EntityManagerInterface $em)
        {
            $this->em = $em;
        }
    
        public function __invoke($name)
        {
            $group = $this->em->getReference(Group::class, 122);
            $person = new Person($name, $group);
            $this->em->persist($person);
    
            return $person;
        }
    }
    

    现在您可以使用依赖注入来检索查询服务并根据需要使用它,例如使用 Symfony 表单或控制器。

    namespace App\Controller;
    
    use App\Service\PersonCreateQuery;
    
    class PersonController
    {
        public function createAction(PersonCreateQuery $query)
        {
            $person = $query('Mike');
            $this->getDoctrine()->getManager()->flush();
    
            //...
        } 
    }
    

    注意:$em-&gt;getReference() 的用法可以替换为$em-&gt;find()。使用$em-&gt;getReference() 将阻止对数据库的查询,但如果引用无效则会抛出异常,而使用$em-&gt;find() 将返回null


    另一种方法是使用实​​体中的生命周期回调或事件侦听器来执行更复杂的功能。但是,这将导致您的实体在被持久化之前被实例化为无效状态。

    use Doctrine\ORM\Event\LifecycleEventArgs;
    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity
     * @ORM\HasLifecycleCallbacks
     */
    class Person 
    {
         const DEFAULT_GROUP = 122;
    
         /** @ORM\Column(type="string") */
         private $name = 'Mike';
    
        /**
         * @ORM\ManyToOne(targetEntity="Group", inversedBy="persons")
         * @ORM\JoinColumn(referencedColumnName="id")
         */
        private $group;
    
        //....
    
        public function setGroup(Group $group)
        {
            $this->group = $group;
            $group->addPerson($this);
        }
    
        /**
         * @param LifecycleEventArgs $event
         * @ORM\PrePersist
         */
        public function onPrePersist(LifecycleEventArgs $event)
        {
           if (!$this->group instanceof Group) {
               /** set default group if not specified */
               $group = $event->getEntityManager()->getReference(Group::class,  self::DEFAULT_GROUP);
               $this->setGroup($group);
           }
        }
    
    }
    

    现在,当您持久化一个 Person 实体时,如果它没有在其他地方明确设置,它将添加该组。

    $person = new Person();
    $person->setName('Foo Bar');
    $em->persist($person); //persist or do nothing if already persisted
    
    $group = $person->getGroup();
    echo $group->getId(); //outputs: 122
    
    $groupPerson = $group->getPerson();
    echo $groupPerson->getName(); //outputs: Foo Bar
    $em->flush(); //save to database
    

    为了理智,这里是教义事件文档的链接:

    【讨论】:

      猜你喜欢
      • 2017-05-23
      • 2016-09-22
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多