简体   繁体   English

Doctrine ORM关联的默认值

[英]Default value of Doctrine ORM association

I have the entity (such as below).我有实体(如下所示)。 I want to set some default values while creating.我想在创建时设置一些默认值。 As you can see in __construct , it is easy to set the $name (string), but how can I set the $group ?正如您在__construct中看到的那样,设置$name (字符串)很容易,但是如何设置$group (for example I know that there is a group in database with id=122 ) (例如,我知道数据库中有一个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); // <<--------------
    }
}

I agree with moonwave99 that this is poor design.我同意 moonwave99 的观点,这是糟糕的设计。 Here you are trying to access the database (through the Doctrine service) from a place that is not container-aware (ie it does not, and should not, know about Doctrine).在这里,您正试图从一个不支持容器的地方(即它不知道,也不应该知道 Doctrine)访问数据库(通过 Doctrine 服务)。

I had a similar issue recently... pretty much the same exact issue, actually.我最近有一个类似的问题......实际上几乎完全相同的问题。 But I didn't want this logic to be inside the controller.但我不希望这个逻辑在控制器内部。 So I wrote a service to take care of the User creation.所以我写了一个服务来处理用户创建。 And I gave that service access to the only other service it needed: Doctrine.我让该服务访问它唯一需要的其他服务:Doctrine。

Here's an example, where a User is created with all available Roles:这是一个示例,其中创建了具有所有可用角色的用户:

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;
    }
}

Now register that service in config.yml or services.yml , remembering to pass the Doctrine service to it:现在在config.ymlservices.yml中注册该服务,记住将 Doctrine 服务传递给它:

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

And that's it... Now, in your controller, you can create a new User by doing:就是这样......现在,在你的控制器中,你可以通过以下方式创建一个新用户:

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

The recommended method is to require the associated Entity object within the constructor arguments, optionally in combination with a Factory such as the Entity Repository, to supply the Group Entity during instantiation.推荐的方法是在构造函数参数中要求关联的实体对象,可选地与实体存储库等工厂结合,以在实例化期间提供Group实体。 This ensures the entity is always in a valid state.这确保实体始终处于有效状态。

src/Entity/Person.php src/实体/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 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;
    }
}

This allows you to rely solely on the Doctrine ORM Entity Manager to maintain the default Group association.这允许您完全依赖 Doctrine ORM 实体管理器来维护默认的组关联。

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

This approach can be extended upon in Symfony to use Query services instead of the doctrine entity repository, to provide a central location that handles the instantiation of the entities.这种方法可以在 Symfony 中扩展以使用查询服务而不是原则实体存储库,以提供处理实体实例化的中央位置。

In Symfony 3.4+ you can use Repository services to provide dependency injection for the repository, instead of using the EntityManagerInterface .在 Symfony 3.4+ 中,您可以使用存储库服务为存储库提供依赖注入,而不是使用EntityManagerInterface

src/Service/PersonCreateQuery.php src/服务/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;
    }
}

Now you can use dependency injection to retrieve the Query service and use it as desired, such as with a Symfony Form or Controller.现在您可以使用依赖注入来检索查询服务并根据需要使用它,例如使用 Symfony Form 或 Controller。

namespace App\Controller;

use App\Service\PersonCreateQuery;

class PersonController
{
    public function createAction(PersonCreateQuery $query)
    {
        $person = $query('Mike');
        $this->getDoctrine()->getManager()->flush();

        //...
    } 
}

Note: Usages of $em->getReference() can be replaced with $em->find() .注意: $em->getReference()的用法可以替换为$em->find() Using $em->getReference() will prevent a query to the database but will throw an exception if the reference is invalid, while using $em->find() will return null instead.使用$em->getReference()将阻止对数据库的查询,但如果引用无效将抛出异常,而使用$em->find()将返回null


Another approach is to use either Lifecycle Callbacks in the entity or an Event Listener to do more complex functionality.另一种方法是使用实体中的生命周期回调或事件监听器来执行更复杂的功能。 However, this will cause your entity to be instantiated in an invalid state until it is persisted.但是,这将导致您的实体在被持久化之前以无效状态被实例化。

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);
       }
    }

}

Now when you persist a Person entity it will add the group if it was not explicitly set elsewhere.现在,当您保留一个 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

For sanity here are the links to the docs for the doctrine events:为了理智,这里是教义事件文档的链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM