Таблица ссылок Doctrine 2 и многие-ко-многим с дополнительным полем

88

(Извините за бессвязный вопрос: я пытался ответить на некоторые вопросы, когда писал этот пост, но вот он :)

Я пытаюсь создать модель базы данных с отношением «многие ко многим» внутри таблицы ссылок, но у которой также есть значение для каждой ссылки, в данном случае таблица складских запасов. (это базовый пример для других проблем, которые у меня возникают, но я подумал, что просто протестирую его, прежде чем продолжить).

Модель базы данных для базовой системы хранения нескольких магазинов и продуктов

Я использовал exportmwb для создания двух Entities Store и Product для этого простого примера, оба показаны ниже.

Однако теперь проблема в том, что я не могу понять, как получить доступ к значению stock.amount (подписанное int, поскольку оно может быть отрицательным) с помощью Doctrine. Кроме того, когда я пытаюсь создать таблицы с помощью функции doctrine orm: schema-tool: create,

макет базы данных, как видно из HeidiSQL

Это дало только две сущности и три таблицы, одну как таблицу ссылок без значений и две таблицы данных, так как отношения «многие ко многим» сами по себе не являются сущностями, поэтому я могу иметь только продукт и магазин как сущность.

Итак, логически я попытался изменить модель своей базы данных, чтобы запасы были отдельной таблицей с отношениями между магазином и продуктом. Я также переписал имена полей, чтобы исключить это как источник проблемы:

изменен макет базы данных

Затем я обнаружил, что я все еще не получил сущность Stock ... и в самой базе данных не было поля «количество».

Мне действительно нужно было связать эти магазины и продукты вместе в таблице запасов (среди прочего) ... поэтому просто добавить запас на самом продукте - не вариант.

root@hdev:/var/www/test/library# php doctrine.php orm:info
Found 2 mapped entities:
[OK]   Entity\Product
[OK]   Entity\Store

И когда я создаю базу данных, она по-прежнему не дает мне нужных полей в таблице запасов:

макет базы данных, как видно из HeidiSQL

Итак, поискав здесь кое-что, я обнаружил, что соединения «многие ко многим» не являются сущностями и, следовательно, не могут иметь значений. Поэтому я попытался изменить его на отдельную таблицу со связями с другими, но это все равно не сработало.

Что я здесь делаю не так?

Генри ван Меген
источник
Хорошо, я нашел пару упоминаний о том, что невозможно иметь связи "многие ко многим" с помощью Doctrine, с комментариями, советующими предотвратить эти отношения ... но что, если вы действительно застряли в ситуации, подобной той, которую я описал в мой первоначальный вопрос? У меня есть целая база данных, совместимая с Magento, которая полностью полагается на отношения «многие ко многим». Так что в основном мне говорят: «Doctrine ORM не может обрабатывать многие-ко-многим, не используйте его» ??
Генри ван Меген
3
Я бы дал вам +100, если бы мог, за ваши усилия, чтобы объяснить, что именно меня интересует, в такой красивой форме :-)
Торстен Ремер

Ответы:

141

Связь «многие-ко-многим» с дополнительными значениями не является «многие-ко-многим», это действительно новая сущность, поскольку теперь она имеет идентификатор (два отношения к связанным сущностям) и значения.

Это также причина того, почему ассоциации "многие-ко-многим" так редки: вы склонны хранить в них дополнительные свойства, например sorting ,amount и т. Д.

Вероятно, вам понадобится что-то вроде следующего (я сделал оба отношения двунаправленными, подумайте о том, чтобы сделать хотя бы одно из них однонаправленным):

Товар:

namespace Entity;

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Table(name="product") @ORM\Entity() */
class Product
{
    /** @ORM\Id() @ORM\Column(type="integer") */
    protected $id;

    /** ORM\Column(name="product_name", type="string", length=50, nullable=false) */
    protected $name;

    /** @ORM\OneToMany(targetEntity="Entity\Stock", mappedBy="product") */
    protected $stockProducts;
}

Хранить:

namespace Entity;

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Table(name="store") @ORM\Entity() */
class Store
{
    /** @ORM\Id() @ORM\Column(type="integer") */
    protected $id;

    /** ORM\Column(name="store_name", type="string", length=50, nullable=false) */
    protected $name;

    /** @ORM\OneToMany(targetEntity="Entity\Stock", mappedBy="store") */
    protected $stockProducts;
}

Склад:

namespace Entity;

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Table(name="stock") @ORM\Entity() */
class Stock
{
    /** ORM\Column(type="integer") */
    protected $amount;

    /** 
     * @ORM\Id()
     * @ORM\ManyToOne(targetEntity="Entity\Store", inversedBy="stockProducts") 
     * @ORM\JoinColumn(name="store_id", referencedColumnName="id", nullable=false) 
     */
    protected $store;

    /** 
     * @ORM\Id()
     * @ORM\ManyToOne(targetEntity="Entity\Product", inversedBy="stockProducts") 
     * @ORM\JoinColumn(name="product_id", referencedColumnName="id", nullable=false) 
     */
    protected $product;
}
Окрамиус
источник
Хорошо, я добавлю несколько геттеров и сеттеров, потому что с этой настройкой я получаю и работаю только первичные ключи без каких-либо значений :)
Генри ван Меген
Когда я использую эту настройку, а затем пытаюсь выполнить запрос с помощью Stock.store_id, я получаю сообщение об ошибке «На складе нет поля или ассоциации с именем store_id». Его следует найти, потому что столбец существует в базе данных.
афилина
@afilina, db не имеет значения при создании схемы - DBAL выдает исключение, потому что не находит столбец в метаданных DDL (в памяти)
Ocramius
@Ocramius Я имел в виду, что БД была создана из метаданных. Если он смог сгенерировать столбец в первую очередь, он сможет найти его во время запроса. Решением моей проблемы было сравнение Stock.store с желаемым идентификатором.
afilina 08
100% то, что мне нужно, и это работает как шарм! Вы знаете, как создать форму с помощью набора полей, чтобы редактировать сумму для каждого магазина и продукта?
cwhisperer
17

Doctrine отлично справляется с отношениями «многие ко многим».

Проблема в том, что вам не нужна простая ассоциация ManyToMany, потому что ассоциации не могут иметь «лишних» данных.

Ваша средняя (складская) таблица, поскольку она содержит больше, чем product_id и store_id, нуждается в собственной сущности для моделирования этих дополнительных данных.

Итак, вам действительно нужны три класса сущностей:

  • Товар
  • StockLevel
  • хранить

и две ассоциации:

  • Product oneToMany StockLevel
  • Store oneToMany StockLevel
Timdev
источник
1
Спасибо за ваш ответ ! Я добавил дополнительные поля в свою "стандартную" таблицу. Однако доктрина по-прежнему не рассматривает эту «таблицу соединений» и пропускает ее при запуске php app/console doctrine:mapping:import AppBundle yml, чтобы импортировать схему из базы данных. Я хотел бы, чтобы он сгенерировал этот дополнительный файл yaml сопоставления. Есть ли у кого-нибудь идея? :(
Stphane
Ответ не разрешает запись данных в сущность "соединение". Это проблема. Не декларирование сущностей. Может ли кто-нибудь поддержать пример, в котором данные передаются из формы (поле CollectionType имеет встроенную форму с полем EntityType). Я не могу передать данные из встроенной формы в основную форму и правильно сохранить коллекцию полей
zoore