Как сделать связь одного поля таблицы с несколькими сущьностями (entity)?

Столкнулся с проблемой, которая поставила меня в тупик. Вроде все просто казалось - необходимо сделать в админке менеджер многоуровневого меню. Проблема в том, что каждый из пунктов меню берет саму ссылку (URL) из сущности (Entity), которая привязана к данному пункту меню. Типов сущностей несколько, соответственно и классов пункта меню (MenuItem) тоже несколько:
  • MenuItemPage имеет связь с сущностью Page (будет ссылка на одиночную страничку)
  • MenuItemCategory имеет связь с сущностью Category (будет ссылка на категорию)
  • ... другие типы
  • наконец самая простая, MenuItemSimple не имеет связей и у нее можно задать произвольную ссылку


В итоге должно бы получиться меню с подобной структурой:

  • MenuItemSimple - корень меню, без привязки к сущностям
    • MenuItemPage - Ссылка на страницу
    • MenuItemPage - Ссылка на страницу
    • MenuItemSimple - далее корень подменю категорий, без привязки к сущности
      1. MenuItemCategory - Ссылка на категорию
      2. MenuItemCategory - Ссылка на категорию
      3. MenuItemCategory - Ссылка на категорию
      4. MenuItemCategory - Ссылка на категорию



Код получился таким

/**
 * @ORM\Entity
 * @ORM\Table("menus")
 * @Gedmo\Mapping\Annotation\Tree(type="nested")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="discriminator", type="string")
 * @ORM\DiscriminatorMap({
 *     "simple" = "MenuItemSimple",
 *     "page" = "MenuItemPage",
 *     "article" = "MenuItemArticle",
 *     "category" = "MenuItemCategory"
 * })
 * @ORM\Entity(repositoryClass="App\Repository\MenuItemRepository")
 */
abstract class MenuItem
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=256, nullable=true)
     */
    private $titleLink;

    /**
     * Href property for link
     *
     * @ORM\Column(type="string", length=256, nullable=true)
     */
    private $hrefLink;

    public function getTitleLink(): ?string
    {
        return $this->titleLink;
    }

    public function setTitleLink(?string $titleLink): self
    {
        $this->titleLink = $titleLink;
        return $this;
    }

    public function getHrefLink(): ?string
    {
        return $this->hrefLink;
    }

    public function setHrefLink(?string $hrefLink): self
    {
        $this->hrefLink = $hrefLink;
        return $this;
    }
}


/**
 * @ORM\Entity
 */
class MenuItemCategory extends MenuItem {

    /**
     * @ORM\ManyToOne(targetEntity="Category")
     */
    private $entity;

    public function getEntity(): ?Category
    {
        return $this->entity;
    }

    public function setEntity(?Category $entity): self
    {
        $this->entity = $entity;
        return $this;
    }
}
/**
 * @ORM\Entity
 */
class MenuItemSimple extends MenuItem {}


Другие типы сущностей пунктов меню аналогичны MenuItemCategory - дублировать не буду.

Суть всей проблемы - не получается связать поле entity_id с несколькими таблицами (таблицы pages, categories, articles и т.д... соответственно) - итоговая таблица меню связывается только с одной. И разнести пункты меню по разным таблицам (ну, по типу связи) - тоже нельзя т.к. пункты меню должны быть в одной табличке.

Единственным решением я пока вижу - объединить все типы пунктов меню (MenuItemPage, MenuItemCategory, MenuItemArticle и другие) в один - MenuItem, и в нем вместо одного поля entity будут несколько полей (свойств), каждое из которых связано со своей сущностью. Таким образом и таблица будет иметь не одно поле для связи, а столько, сколько сущностей объявлено. Короче, объединить все типы сущности меню в одну. Это даже реализовать проще... но не так красиво, как по отдельности.

Помогите советом, пожалуйста!
  • Вопрос задан
  • 462 просмотра
Решения вопроса 1
@Flying
Вы уже используете DiscriminatorMap который является правильным решением в данной задаче. Вам ничто не мешает теперь иметь в наследуемых классах отдельные свойства содержащие связи с конкретными entities, специфичными для каждого конкретного типа пункта меню. Вы также вполне можете объявить getEntity() / setEntity() в классе MenuItem абстрактными, а в дочерних классах реализовывать их. Конечно типы на уровне языка вам будет не задать, но никто не будет вам мешать использовать PHPDoc для создания type hints.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы