Dar*_*ski 15 many-to-many symfony doctrine-orm
在我的Symdony2项目中,我有两个相关的实体:Service和ServiceGroup.这应该是多对多关系,因为每个组可以有许多服务,并且每个服务可以属于许多组.而且,我需要一个用户界面来管理服务和组.因此,在编辑服务时,用户应该能够选择它所属的组.类似地,在编辑ServiceGroup时,用户应该能够选择属于该组的服务.我已经通过在我的学说中建立多对多关系来实现这一目标.一切都像魅力一样,包括在Symfony2中构建自定义表单类型的用户界面(我使用"实体"表单字段类型,以允许用户在ServiceGroup编辑器和服务编辑器中的组中选择服务).我唯一的问题是我不能再使用Doctrine命令行来更新数据库模式.
这是我的服务实体源代码的一部分:
class Service
{
/**
* @var ArrayCollection $groups
* @ORM\ManyToMany(targetEntity="ServiceGroup")
* @ORM\JoinTable(
* name="service_servicegroup",
* joinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="servicegroup_id", referencedColumnName="id")}
* )
*/
private $groups;
}
Run Code Online (Sandbox Code Playgroud)
这是我的ServiceGroup实体源代码的一部分:
class ServiceGroup
{
/**
* @var ArrayCollection $services
* @ORM\ManyToMany(targetEntity="Service")
* @ORM\JoinTable(
* name="service_servicegroup",
* joinColumns={@ORM\JoinColumn(name="servicegroup_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")}
* )
*/
private $services;
}
Run Code Online (Sandbox Code Playgroud)
我在两种情况下都使用JoinTable,因为这是我在用户界面编辑器中保存关系时找到工作的唯一方法,如下所示:
服务编辑:
服务编辑
名称:[服务1]
此服务所属的组:
[x] A组
[] B组
[] C组
[ 保存 ]
和ServiceGroup编辑器:
小组编辑
姓名:[A组]
服务属于这个群组:
[x]服务1
[]服务2
[]服务3
[ 保存 ]
使用这种多对多配置,我可以毫无问题地使用这些编辑器(表单),当使用没有JoinTable注释的多对多,我只能完全使用一个表单,第二个是不保存"此服务所属的组"或"服务属于此组"选项中的更改(取决于我在多对多注释语句中设置mappedBy和inversedBy参数的方向).
当我尝试使用Symfony2命令更新模式时,我遇到的问题与doctrine模式生成机制有关:
php app/console doctrine:schema:update --dump-sql
Run Code Online (Sandbox Code Playgroud)
我得到了这个例外:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'service_servicegroup' already exists.
Run Code Online (Sandbox Code Playgroud)
看起来Doctrine试图为每个JoinTable语句创建"service_servicegroup"表.因此,它正在使用当前模式,我使用相同的命令在数据库中构建,但是一步一步,首先没有定义多对多关系,接下来只有一个多对多关系定义(对于服务实体).当我向第二个实体(ServiceGroup)添加了"多对多"关系时,我的应用程序从用户角度看起来没有问题,但是我不能再使用"doctrine:schema:update"命令.
我不知道我的代码有什么问题,也许这种关系应该以不同的方式实现,或者它可能是一个Doctrine bug /限制.任何帮助或建议将不胜感激.
更新:
我注意到我需要的是配置ManyToMany关系以拥有两个拥有方.默认是拥有一个拥有方和一个反方.Doctrine 文档告诉您在ManyToMany关系中可以拥有两个拥有者,但不会解释很多.有谁可以举个例子?
解决方法:
我找到了一个解决方案,可能并不理想,但它对我有用.由于没有办法在多对多关系中拥有两个拥有的边,我已经为我的entites改变了Doctrine注释.服务实体现在是拥有方:
class Service
{
/**
* @var ArrayCollection $groups
* @ORM\ManyToMany(targetEntity="ServiceGroup", inversedBy="services", cascade={"all"})
* @ORM\JoinTable(
* name="service_to_group_assoc",
* joinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
private $groups;
}
Run Code Online (Sandbox Code Playgroud)
而ServiceGroup实体是反面的:
class ServiceGroup
{
/**
* @var ArrayCollection $services
* @ORM\ManyToMany(targetEntity="Service", mappedBy="groups", cascade={"all"})
*/
private $services;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,使用此配置,仅在更新服务实体时更新关系.当我在ServiceGroup对象中更改$ services并保持它时,关系将保持不变.所以,我改变了我的Controller类,并借助小型解决方案解决方案,实现了预期的结果.这是我的Controller代码的一部分,它负责更新ServiceGroup实体(使用自定义表单类型):
// before update - get copy of currently related services:
$services = clone $group->getServices();
// ...
// when $form->isValid() etc. updating the ServiceGroup entity:
// add selected services to group
foreach($group->getServices() as $service)
{
$service->addServiceGroup($group);
$services->removeElement($service);
}
// remove unselected services from group
foreach($services as $service)
{
$service->removeServiceGroup($group);
}
Run Code Online (Sandbox Code Playgroud)
这是Service实体类的addServiceGroup和removeServiceGroup方法的实现:
/**
* Add group
*
* @param ServiceGroup $groups
*/
public function addServiceGroup(ServiceGroup $groups)
{
if(!in_array($groups, $this->groups->toArray()))
{
$this->groups[] = $groups;
}
}
/**
* Remove group
*
* @param ServiceGroup $groups
*/
public function removeServiceGroup(ServiceGroup $groups)
{
$key = $this->groups->indexOf($groups);
if($key!==FALSE)
{
$this->groups->remove($key);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我与拥有(Service)和反向(ServiceGroup)方面有多对多的关系,并且在保存时更新了实体和关系的表单(Service实体的默认表单就足够了,但对于ServiceGroup我提供了上面提到的修改).Symfony/Doctrine控制台工具就像魅力一样.这可能是以更好(更简单?)的方式解决的,但对我来说这已经足够了.
确保拥有方(服务)始终得到更新的示例。
class ServiceGroup
{
public function addService($service)
{
if (!$service) return;
$this->services[] = $service;
$service->addGroup($this);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12796 次 |
| 最近记录: |