Doctrine ORM не может сохранить одну сторону ManyToMany (SQLSTATE [23000]: нарушение ограничения UNIQUE)

Я новичок в 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 и Skill?

Jannes Botis 25.05.2018 09:20

Оба они содержат коллекции массивов

useyourillusiontoo 25.05.2018 09:22

Вы там устанавливаете идентификатор навыка? Не могли бы вы добавить соответствующий код, может, это поможет?

Jannes Botis 25.05.2018 09:24

Я добавил два конструктора.

useyourillusiontoo 25.05.2018 10:51

имена столбцов в таблице ссылок должны быть, player_id, skill_id

Deniz Aktürk 25.05.2018 13:07

Я разделяю новую структуру таблицы с новым ответом, вы можете попробовать структуру таблицы?

Deniz Aktürk 31.05.2018 12:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
225
2

Ответы 2

Попробуйте аннотацию ниже:

class Player
{

...

/**
 * @ORM\ManyToMany(targetEntity = "App\Entity\Skill", inversedBy = "skill",cascade = {"persist"})
 *
 * @ORM\JoinTable(name = "player_skill")
 *
 */
private $skill;

...

}

вот так, с именем таблицы отношений

Пробовал и без разницы. Спасибо за предложение

useyourillusiontoo 25.05.2018 14:22

Можете ли вы поделиться используемой промежуточной структурой таблицы?

Deniz Aktürk 25.05.2018 18:34

Я добавлю это к исходному вопросу (извините за задержку)

useyourillusiontoo 30.05.2018 18:50

вы можете попробовать следующую структуру таблицы?

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;

...

}

Другие вопросы по теме