实体类型和具有额外字段关系的多对多显示为下拉列表 (select)

EntityType and many to many with extra field relation presented as dropdown (select)

我创建了一个这样的表格

$builder->add('employees', EntityType::class, [
    'class'         => ActivityEmployee::class,
    'choice_label'  => function (ActivityEmployee $employee) {
        return sprintf('%s %s', $employee->getEmployee()->getName(), $employee->getEmployee()->getLastName());
    },
    'multiple'      => true,
])

因此,它可以很好地呈现现有数据。它向我显示了与已编辑 activity.

有关的所有员工

然而 choices 应该有所有雇员选择(employee 实体)并且 selected data 在 activity 雇员关系中只有雇员就像现在一样。

我试图添加一个 query_builder 选项来提供所有雇员的列表,但我只能使用 EntityRepository 这意味着 ActivityEmployeesRepository 而不是 EmployeesRepository 本身。

A 不知道如何实现它。基本上这种关系可以通过自定义 activityEmployeeTypeCollectionType 来完成,但我想为 selecting 员工使用 multi-select。

我可以使用另一种方法来不将我的员工字段映射到那样的实体

$currentEmployees = [];
foreach ($activity->getEmployees() as $activityEmployee) {
    $currentEmployees[] = $activityEmployee->getEmployee();
}
$builder->add('employees', EntityType::class, [
    'class'        => Employee::class,
    'choice_label' => function (Employee $employee) {
        return sprintf('%s %s', $employee->getName(), $employee->getLastName());
    },
    'mapped'       => false,
    'multiple'     => true,
    'data'         => $currentEmployees,
]);

它工作正常,但我需要自己处理更新关系。没关系,但是我想知道如何在第一种方法中实现这样的事情。

实施细节很重要。据我所知,您有以下实体:

Activity (entity)
  - employees (OneToMany -> ActivityEmployee)
ActivityEmployee (entity)
  - activity (ManyToOne -> Activity)
  - employee (ManyToOne -> Employee)
Employee (entity)
  - activities (OneToMany -> ActivityEmployee) - this one might be missing, actually.

现在您显然没有隐藏任何实现细节。意思是,你的 Activity::getEmployees() returns []ActivityEmployee.

我会这样做:

class Activity {
   /** @ORM\OneToMany(targetEntity=ActivityEmployee::class) */
   private $activityEmployees;

   /** @return Employee[] */
   public function getEmployees() :Collection {
       return $this->activityEmployees->map(function(ActivityEmployee $ae) {
             return $ae->getEmployee();
       });
   }

   public function addEmployee(Employee $employee) {
       // check, if the employee is already registered, add only then!
       if(!$this->getEmployees()->contains($employee)) {
           $this->activityEmployees->add(new ActivityEmployee($this, $employee));
       }
   }
   public function removeEmployee(Employee $employee) {
       foreach($this->activityEmployees as $activityEmployee) {
           if($activityEmployee->getEmployee() === $employee) {
                $this->activityEmployees->removeElement($activityEmployee);
           }
       }
   }
}

通过这种方式,您隐藏了 Activity 如何处理员工和外部世界(特别是表单组件使用的 PropertyAccessor),它看起来好像 Activity 有一个 属性 employees 实际上是 Employee[].

如果你这样实现它,你的第一个表单实际上应该可以正常工作(显然是将 ActivityEmployee 换成 Employee)- 假设我没有犯一些重大错误。当然,当我实际上特别需要关系对象时,我也会添加像 getActivityEmployees 这样的方法。

如果你的多对多可以包含重复项,那么整个事情肯定就不那么漂亮了。

如果 你的 ActivityEmployee 实际上除了 activityemployee 没有其他属性,你显然可以用 @ORM\ManyToMany 替换整个东西并且只使用 Employee[] 而不是 ActivityEmployee[]。但是,我假设您有一些额外的列,例如 created 或其他列。