首页 > 解决方案 > 在单个字段下映射多个实体

问题描述

有没有办法让实体中的单个字段绑定到多个不同的实体?

我有一个“任务”实体,它可以与客户实体或供应商实体(从不两者)相关联。现在这两个字段是分开的。

我需要在我的 TaskType 表单中使用它,以便用户可以选择将任务与哪个客户/供应商关联,最好在单个字段下,因为我计划添加更多可以关联的实体。

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Customer", inversedBy="tasks")
 */
private $customer;
/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Supplier", inversedBy="tasks")
 */
private $supplier;

public function getCustomer(): ?Customer
{
    return $this->customer;
}
public function setCustomer(?Customer $customer): self
{
    $this->customer = $customer;
    return $this;
}
public function getSupplier(): ?Supplier
...etc

标签: symfonydoctrine-ormdoctrinesymfony4

解决方案


也许您可以尝试以下方法:

理想情况下,我猜您想在Customer和之间共享信息Supplier。所以我们可以引入一个新的父类,Person例如(我不知道他们有什么样的责任,所以我们将采用最“通用”的类名),并使用Doctrine 继承映射

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({
 *  "customer" = "Customer",
 *  "supplier" = "Supplier"
 * })
 */
abstract class Person
{
  //... Fields, traits, embeddables...

  /**
   * A common attribute between our child classes
   * protected to respect encapsulation
   * 
   * @ORM\Column(type="text")
   */
  protected $name;

  /**
   * Here we define the general link to a task. It will be inherited by child classes
   *
   * @ORM\OneToMany(targetEntity="App\Entity\Task", mappedBy="assignedTo")
   */
  protected $tasks;

  // public getters/setters...
}

我认为类表继承策略在这里可以满足您的需求,因为您以后想添加更多实体。这样,我们就可以尊重开闭原则,以后再添加更多的子类,而不是只在一个类中修改一个逻辑。

另外,我将Person类抽象化,因为我们通常想要处理CustomerSupplier实例。但是根据您的需要,也许您可​​以删除abstract关键字。在这种情况下,您将必须包含Person在鉴别器映射中。

当然,现在CustomerSupplier两者都要扩展Person

//...
class Customer extends Person
//...

//...
class Supplier extends Person
//...

不要忘记id从子类中删除共享字段(例如,例如),它现在将继承自Person

因此,在任务中,您可以定义与ManyToOnea 的关系Person

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Person", inversedBy="tasks")
 */
private $assignedTo;

最后,对于您的任务表单,让我们有一个包含所有人姓名的选择列表:

<?php

namespace App\Form;

use App\Entity\Person;
use App\Entity\Task;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TaskType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      // other task fields
      ->add('assignedTo', EntityType::class, [
        'class' => Person::class,
        'choice_label' => 'name',
      ]);
  }

  public function configureOptions(OptionsResolver $resolver)
  {
    $resolver->setDefaults([
      'data_class' => Task::class,
    ]);
  }
}

它将选择所有的人,无论其类型如何。然后您可以稍后使用其他子类扩展它!我希望这将有所帮助。


推荐阅读