首页 > 解决方案 > EntityType 和多对多,额外字段关系显示为下拉列表(选择)

问题描述

我创建了一个这样的表格

$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,
])

因此,它可以很好地呈现已经存在的数据。它向我显示了与已编辑活动相关的所有员工。

但是,choices因为应该有所有的雇员来选择(employee实体),并且data像现在一样在 activityEmployee 关系中选择了唯一的雇员。

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

A无法弄清楚如何实现它。基本上这种关系可以通过CollectionType自定义来完成,activityEmployeeType但我想使用多选来选择员工。

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

$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,
]);

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

标签: symfonysymfony4

解决方案


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

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()回报[]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 实际上除了activityand之外没有其他属性employee,那么您显然可以将整个内容替换为 a@ORM\ManyToMany并且只使用Employee[]而不是ActivityEmployee[]. 但是,我假设您有一些额外的列,例如created


推荐阅读