Отображение самоссылающегося объекта в ветке

Я пытаюсь отобразить дерево ссылок, используя ассоциацию самоссылки Doctrine. У меня есть сущность вроде:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name = "link")
 * @ORM\Entity(repositoryClass = "App\Repository\LinkRepository")
 */
class Link
{
    /**
     * @ORM\Column(name = "link_id", type = "integer", nullable=false, options = {"unsigned"=true})
         * @ORM\Id()
         * @ORM\GeneratedValue(strategy = "IDENTITY")
     */
    private $linkId;

    /**
     * @ORM\Column(name = "name", type = "string", length=50)
     */
    private $name;

    /**
     * @ORM\Column(name = "url", type = "string", length=500)
     */
    private $url;

    /**
     * @ORM\Column(name = "parent_id", type = "integer")
     */
    private $parentId;

    /**
     * @ORM\OneToMany(targetEntity = "Link", mappedBy = "parent")
     */
    private $children;

    /**
     * @ORM\ManyToOne(targetEntity = "Link", inversedBy = "children")
     * @ORM\JoinColumn(name = "parent_id", referencedColumnName = "link_id")
     */
    private $parent;

    public function __construct()
    {
        $this->children = new ArrayCollection();
    }

    public function getLinkId(): ?int
    {
        return $this->linkId;
    }

    // Getters and setters ...

    /**
     * @return ArrayCollection
     */
    public function getChildren()
    {
        return $this->children;
    }
}

и в моем контроллере он извлекает ссылку и вызывает шаблон ветки, например:

public function link(int $linkId, LinkRepository $linkRepository)
{
    $link = $linkRepository->findOneBy(['linkId' => $linkId]);

    return $this->render('link.html.twig',
    [
        'link' => $link
    ]);
}

И шаблон ветки примерно такой:

{% extends 'base.html.twig' %}

{% block body %}
    {{ link.name }}

    <h2>Children:</h2>

    {% import _self as macros %}
    <ul>
        <li>{{ link.name }}
            {{ macros.link_tree(link) }}
        </li>

    </ul>
{% endblock %}

{% macro link_tree(link) %}
    {% import _self as macros %}
    <ul>
    {% for linkChild in link.children %}
        <li>
            {{ link.name }}
            {% if linkChild.children %}
                <ul>
                    {{ macros.link_tree(linkChild.children) }}
                </ul>
            {% endif %}
        </li>
    {% endfor %}
    </ul>
{% endmacro %}

Хотя когда я вызываю этот контроллер, он выдает мне эту ошибку:

Neither the property "children" nor one of the methods "children()", "getchildren()"/"ischildren()"/"haschildren()" or "__call()" exist and have public access in class "Doctrine\ORM\PersistentCollection".

Кажется, это происходит, когда я ссылаюсь на linkChild.children в шаблоне ветки.

Как я могу рекурсивно перебирать детей () в ветке?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
0
217
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Прежде всего, ваше сопоставление Doctrine неверно: вы должны удалить поле parentId, в нем нет необходимости, поскольку вы уже добавили соответствующую ассоциацию с полем parent.

Во-вторых, вы должны использовать Symfony ParamConverter, чтобы получить ссылку внутри контроллера следующим образом:

public function link(Link $link)

Да, это так просто, как кажется, вы можете получить ссылку, просто набрав переменную ссылки в своем действии контроллера, нет необходимости использовать там LinkRepository. Вы можете найти больше о ParamConverter здесь.

Наконец, похоже, проблема в ваших данных, потому что вы получили экземпляр коллекции Doctrine, когда ожидаете экземпляр класса Link. Попробуйте выполнить отладку, используя {{ dump() }} внутри вашего шаблона ветки, на данный момент недостаточно данных, чтобы помочь вам в этом. Но вам определенно следует сначала решить проблему с отображением, прежде чем пытаться снова.

Спасибо за совет. Я посмотрю на ParamConvertor. Отображение доктрины не было проблемой (я попытался удалить поле parentId, и это не решило проблему). Я разместил свое решение ниже.

Noodles 27.05.2019 00:50
Ответ принят как подходящий

Вот и получается, что я отправлял не то в макрос, он ждал linkChild, а я пропускал linkChild.children. Внутри макроса он пытался сослаться linkChild.children.children

Это было исправлено с помощью:

{{ link.name }}
{% if linkChild.children %}
  <ul>
    {{ macros.link_tree(linkChild) }}
  </ul>
{% endif %}

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