Я новичок в Symfony 4 и Doctrine, поэтому, возможно, я слишком многого жду от Doctrine ORM, но тот факт, что я не могу найти подходящую документацию, чтобы проинформировать меня, сводит меня с ума!
Ошибка
SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: skill.id
Резюме Я создал отношения ManyToMany между своими объектами Player и Skill. Каждый из этих объектов имеет свой идентификатор, установленный вручную, потому что они поступают из внешнего API. Я накапливаю данные в PlayerController, который очищает сторонний API для локального кэширования данных в моей БД.
Написанный мной код позволяет хранить (кэшировать) сторонние данные в моей базе данных, если навык, который я добавляю, еще не был добавлен в базу данных. Однако, если я попытаюсь добавить другого игрока, который разделяет этот навык, вся транзакция завершится ошибкой, потому что он не может снова добавить навык из-за ограничения уникального идентификатора. Я полностью понимаю это, но я думал, что Doctrine ORM достаточно умен, чтобы просто создать запись в таблице соединений и игнорировать уже существующий навык.
Ожидаю ли я слишком многого от Doctrine, и если да, то есть ли какие-нибудь службы или помощники, которые вытащат меня из этой ситуации без написания кучи кода.
PlayerController выполняет это действие для сохранения данных:
private function persistPlayer($data){
$player = new Player($data); //Player __construct entity extracts + assigns data
foreach ($data->skills as $rawSkill) {
$skill = new Skill($rawSkill);
$player->addSkill($skill);
}
}
$this->entityManager->persist($player);
try {
$this->entityManager->flush();
} catch (\Exception $e) {
error_log($e->getMessage());
}
return $player;
}
Аннотации My Entities выглядят следующим образом (и были созданы с помощью команды bin/console make:entity:
class Player
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type = "integer")
*/
private $id;
/**
* @ORM\ManyToMany(targetEntity = "App\Entity\Skill", inversedBy = "skill",cascade = {"persist"})
*/
private $skill;
public function __construct($data)
{
$this->ability = new ArrayCollection();
$this->setId($data->id);
}
public function addSkill(Skill $skill): self
{
if (!$this->skill->contains($skill)) {
$this->skill[] = $skill;
}
return $this;
}
//.... more code
}
И мой навык:
/**
* @ORM\Entity(repositoryClass = "App\Repository\SkillRepository")
*/
class Skill
{
/**
* @var integer $id
* @ORM\Column(name = "id", type = "integer", nullable=false)
* @ORM\Id
*/
private $id;
/**
* @ORM\ManyToMany(targetEntity = "App\Entity\Player", mappedBy = "skill" ,cascade = {"persist"})
*/
private $skill;
public function __construct($data)
{
$this->ability = new ArrayCollection();
$this->setId($data->id);
}
public function setId($id)
{
return $this->id = $id;
}
//...more code
}
Моя соединительная таблица создана с использованием следующего синтаксиса:
CREATE TABLE player_skill (player_id INTEGER NOT NULL, skill_id
INTEGER NOT NULL, PRIMARY KEY(player_id, skill_id))
Оба они содержат коллекции массивов
Вы там устанавливаете идентификатор навыка? Не могли бы вы добавить соответствующий код, может, это поможет?
Я добавил два конструктора.
имена столбцов в таблице ссылок должны быть, player_id, skill_id
Я разделяю новую структуру таблицы с новым ответом, вы можете попробовать структуру таблицы?





Попробуйте аннотацию ниже:
class Player
{
...
/**
* @ORM\ManyToMany(targetEntity = "App\Entity\Skill", inversedBy = "skill",cascade = {"persist"})
*
* @ORM\JoinTable(name = "player_skill")
*
*/
private $skill;
...
}
вот так, с именем таблицы отношений
Пробовал и без разницы. Спасибо за предложение
Можете ли вы поделиться используемой промежуточной структурой таблицы?
Я добавлю это к исходному вопросу (извините за задержку)
вы можете попробовать следующую структуру таблицы?
CREATE TABLE `player_skill` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL, `role_id` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `playerSkill_skillId_idx` (`skill_id`), KEY `playerSkill_playerId_idx` (`player_id`), CONSTRAINT `playerSkill_skillId_idx` FOREIGN KEY (`skill_id`) REFERENCES `skill` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `playerSkill_playerId_idx` FOREIGN KEY (`player_id`) REFERENCES `player` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
и следуйте за аннотацией с помощью
class Player
{
...
/**
* @ORM\ManyToMany(targetEntity = "App\Entity\Skill",
inversedBy = "skill",cascade = {"persist"})
*
* @ORM\JoinTable(name = "player_skill")
*
*/
private $skill;
...
}
Можете ли вы также добавить код конструкторов Player и Skill?