У нас есть схема базы данных, которая в упрощенной (слегка надуманной) форме выглядит следующим образом:
Где внешний ключ от пользователей к доменам установлен на столбцах (domainId, groupId), чтобы гарантировать ссылочную целостность. Эта структура прекрасно работает по назначению.
Однако для нового приложения, взаимодействующего с той же базой данных, теперь мне нужно создать сопоставление для Doctrine, которое отображает вышеуказанную структуру, в том числе отношение внешнего ключа на два колонны.
Я пробовал следующее:
<entity name="User" table="users">
<!-- other fields -->
<many-to-one field="domain" target-entity="Domain" fetch="LAZY">
<join-columns>
<join-column name="domainId" referenced-column-name="domainId"/>
<join-column name="groupId" referenced-column-name="groupId"/>
</join-columns>
</many-to-one>
</entity>
Но это дает мне ошибку:
UnitOfWork.php line 2649: Undefined index: groupId
Итак, мой вопрос:
Как правильно описать многостолбовое отношение многих к одному внешнего ключа в Doctrine?
Для полноты базы данных создайте код для схемы, как описано в ERD выше:
CREATE TABLE `users` (
`userId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`groupId` INT(10) UNSIGNED NOT NULL,
`domainId` INT(10) UNSIGNED NOT NULL,
`someData` VARCHAR(32),
PRIMARY KEY (`userId`),
KEY `key_users_groupId_domainId` (`groupId`, `domainId`)
) ENGINE=InnoDB;
CREATE TABLE `domains` (
`domainId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`groupId` INT(10) UNSIGNED NOT NULL,
`someOtherData` VARCHAR(32),
PRIMARY KEY (`domainId`),
KEY `key_domains_groupId` (`groupId`)
) ENGINE=InnoDB;CREATE TABLE `groups` (
`groupId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`someMoreData` VARCHAR(32),
PRIMARY KEY (`groupId`)
) ENGINE=InnoDB;ALTER TABLE `users`
ADD CONSTRAINT `fk_users_domains` FOREIGN KEY (`groupId`, `domainId`) REFERENCES `domains` (`groupId`, `domainId`),
ADD CONSTRAINT `fk_users_groups` FOREIGN KEY (`groupId`) REFERENCES `groups` (`groupId`);
ALTER TABLE `domains`
ADD CONSTRAINT `fk_domains_groups` FOREIGN KEY (`groupId`) REFERENCES `groups` (`groupId`);
Это не фантастический ответ на ваш вопрос. Кроме того, я никогда не использовал Doctrine или Doctrine2. Но я потратил некоторое время, оглядываясь по сторонам, и в итоге получил первые три ссылки:
Доктрина множественного составного внешнего ключа, Вопрос, хотя он не отображает сопоставления XML и может быть неосновным, по крайней мере, он касается нескольких столбцов в FK. И ответьте на некоторые аспекты Doctrine2, которые игнорируются в соответствии с Ответом.
Doctrine2 Карта объектов с составными внешними ключами в … Вопрос, который не набрал особой ценности, но, по крайней мере, мог бы быть включен в ваш вопрос как кандидат в дураки.
XML Mapping Документация по отображению XML Doctrine2. Не имеет значения при поиске по тексту multi
но в поисках composite
говорит это:
Для составных ключей вы можете указать более одного id-элемента, однако
суррогатные ключи рекомендуются для использования с Doctrine 2.
Что привело меня к этому Википедия определение суррогата, которое гласит:
Суррогат внутренне генерируется системой и невидим
пользователю или приложению.
А также Природный против суррогата. Обсуждение выбора между двумя.
Вернемся к вашей модели, перечисленной в порядке убывания независимости:
CREATE TABLE `groups` (
`groupId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`someMoreData` VARCHAR(32),
PRIMARY KEY (`groupId`)
) ENGINE=InnoDB;
CREATE TABLE `domains` (
`domainId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`groupId` int(10) unsigned NOT NULL,
`someOtherData` varchar(32) DEFAULT NULL,
PRIMARY KEY (`domainId`),
KEY `key_domains_groupId` (`groupId`),
CONSTRAINT `fk_domains_groups` FOREIGN KEY (`groupId`) REFERENCES `groups` (`groupId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users` (
`userId` int(10) unsigned NOT NULL AUTO_INCREMENT,
`groupId` int(10) unsigned NOT NULL,
`domainId` int(10) unsigned NOT NULL,
`someData` varchar(32) DEFAULT NULL,
PRIMARY KEY (`userId`),
KEY `key_users_groupId_domainId` (`groupId`,`domainId`),
CONSTRAINT `fk_users_domains` FOREIGN KEY (`groupId`, `domainId`) REFERENCES `domains` (`groupId`, `domainId`),
CONSTRAINT `fk_users_groups` FOREIGN KEY (`groupId`) REFERENCES `groups` (`groupId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Некоторая работа с нуля:
truncate table groups; -- disallowed
delete from groups;
alter table groups auto_increment 1; -- reset, after running delete from.
insert groups(someMoreData) values ('group0001'),('group0002');
select * from groups;
insert domains(groupId,someOtherData) values
(1,'sod'),(1,'sod'),(1,'sod'),
(2,'sod'),(2,'sod');
select * from domains; -- AI 1 to 5 above
insert users(groupId,domainId,someData) values (1,1,'sd'); -- success
insert users(groupId,domainId,someData) values (1,3,'sd'); -- success
insert users(groupId,domainId,someData) values (1,4,'sd'); -- Error 1452 fk failure
Становится совершенно очевидным, что users
действительно не нужен составной FK в domains
, Скорее, ему просто нужен один столбец FK в суррогатном AI PK domains
, Этого достаточно и достаточно для достижения того же эффекта, что и вы.
В соответствии с этим, users.domainId
достаточно, и users.groupId
вводит денормализацию, и последний должен быть отброшен.
Во всяком случае, надеюсь, это поможет.
Других решений пока нет …