В моем проекте 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;
}
И вот часть моего исходного кода объекта 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;
}
Я использую JoinTable в обоих случаях, потому что это единственный способ найти работу, когда дело касается сохранения отношений в редакторах пользовательских интерфейсов, которые выглядят следующим образом:
Редактор служб:
Сервисный редактор
Имя: [Сервис 1]
Группы, к которым относится эта служба:
[x] Группа A
[] Группа B
[] Группа C
[ СОХРАНИТЬ]
И редактор ServiceGroup:
Редактор групп
Название: [Группа A]
Службы относятся к этой группе:
[x] Сервис 1
[] Сервис 2
[] Сервис 3
[ СОХРАНИТЬ]
С этой конфигурацией "многие-ко-многим" я могу использовать эти редакторы (формы) без проблем, при использовании много-ко-многим без аннотации JoinTable я могу использовать только одну форму полностью, и второй не сохраняет изменения в "Группы, к которым относится эта услуга" или "Службы относятся к этой группе" (зависит от того, в каком направлении я устанавливаю параметры MappedBy и inversedBy в заявлении аннотации Many-To-Many).
Проблема, которую я имею, связана с механизмом генерации схемы доктрины при попытке обновить схему с помощью команды Symfony2:
php app/console doctrine:schema:update --dump-sql
Я получаю это исключение:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'service_servicegroup' already exists.
Похоже, что Doctrine пытается создать таблицу "service_servicegroup" для каждого оператора JoinTable. Таким образом, он работает над текущей схемой, которую я создал в базе данных с использованием одной и той же команды, но поэтапно, сначала, когда отношение Many-to-Many определено и следующее с одним определением отношения Many-to-Many ( для объекта службы). Когда я добавил отношение Many-to-Many ко второй сущности (ServiceGroup), мои швы приложений работают без проблем с точки зрения пользователя, но я больше не могу использовать команду "doctrine: schema: update".
Я не знаю, что не так с моим кодом, может быть, это отношение должно быть реализовано по-другому или, возможно, это ошибка/ограничение Doctrine. Любая помощь или предложение будут оценены.
UPDATE:
Я заметил, что мне нужно настроить отношение ManyToMany к двум владельцам. По умолчанию используется одна собственная сторона и одна обратная сторона. Доктрина документация говорит, что вы можете иметь две стороны-владельца в отношении ManyToMany, но не объясняет это много. Любой может привести пример?
Временное решение:
Я нашел обходные решения, которые, возможно, не идеальны, но он работает для меня. Поскольку нет способа иметь две стороны владения во многих отношениях, я изменил аннотацию 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;
}
И объект ServiceGroup обратный:
class ServiceGroup
{
/**
* @var ArrayCollection $services
* @ORM\ManyToMany(targetEntity="Service", mappedBy="groups", cascade={"all"})
*/
private $services;
}
К сожалению, при этой конфигурации отношение обновляется только при обновлении объекта службы. Когда я изменяю $services в объекте ServiceGroup и сохраняю его, отношение не изменится. Итак, я изменил свой класс 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);
}
Это реализация методов 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);
}
}
Теперь у меня есть отношение "многие ко многим" с собственностью (Service) и обратная (ServiceGroup) сторона и формы, которые обновляют как сущность, так и отношение при сохранении (форма по умолчанию для объекта службы достаточно, но для ServiceGroup я приведенные выше модификации). Инструменты консоли Symfony/Doctrine работают как шарм. Вероятно, это можно решить лучше (проще?), Но для меня этого пока достаточно.