phpsymfonydoctrinedoctrine-extensionsgedmo-tree

Gedmo/Doctrine Tree Extension Cannot use object as array


I've created an entity with a closure table and a repository alongside. I'm trying a basic example of building a tree but am seeing the error Cannot use object of type App\Entity\Discussion\Comment as array when calling getCommentTreeForDiscussion

Entity:

#[Gedmo\Tree(type: 'closure')]
#[Gedmo\TreeClosure(class: CommentClosure::class)]
#[ORM\Entity(repositoryClass: CommentRepository::class)]
#[ORM\HasLifecycleCallbacks]
class Comment implements Identifiable
{
    use IdentifiableEntity;
    use TimestampableEntity;

    #[ORM\Column(type: 'text')]
    private string $content;

    #[ORM\ManyToOne(targetEntity: User::class)]
    #[ORM\JoinColumn(nullable: false)]
    private User $author;

    #[ORM\ManyToOne(targetEntity: Discussion::class, inversedBy: 'comments')]
    #[ORM\JoinColumn(nullable: false)]
    private Discussion $discussion;

    #[Gedmo\TreeParent]
    #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'replies')]
    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
    private ?Comment $parent = null;

    #[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent', cascade: ['persist', 'remove'])]
    private Collection $replies;

    #[Gedmo\TreeLevel]
    #[ORM\Column(type: 'integer')]
    private int $level = 0;

    #[ORM\ManyToMany(targetEntity: User::class)]
    #[ORM\JoinTable(name: 'comment_likes')]
    private Collection $likedBy;

    // --- Getters / Setters

Closure:

#[ORM\Entity]
class CommentClosure extends AbstractClosure
{
    #[ORM\ManyToOne(targetEntity: Comment::class)]
    #[ORM\JoinColumn(name: 'ancestor', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    protected $ancestor;

    #[ORM\ManyToOne(targetEntity: Comment::class)]
    #[ORM\JoinColumn(name: 'descendant', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    protected $descendant;
}

Repository:

class CommentRepository extends ClosureTreeRepository
{
    public function __construct(EntityManagerInterface $manager)
    {
        parent::__construct($manager, $manager->getClassMetadata(Comment::class));
    }

    public function getCommentTreeForDiscussion(Discussion $discussion): array
    {
        $roots = $this->findBy(['discussion' => $discussion, 'parent' => null], ['createdAt' => 'ASC']);

        return $this->buildTree($roots);
    }
}

Solution

  • The documentation provides an example on how to use buildTree().

    There are several issues with your code:

    I would suggest switching to the nested strategy. It's simpler and better supported:

    Closure tree implementation is experimental and not fully functional

    With the nested strategy, the following code should work:

        public function getCommentTreeForDiscussion(Discussion $discussion): array
        {
            $query = $this
                ->getEntityManager()
                ->createQueryBuilder()
                ->select('node')
                ->from(Comment::class, 'node')
                ->where('node.discussion = :discussion')
                ->setParameter('discussion', $discussion)
                ->orderBy('node.root, node.lft', 'ASC')
                ->getQuery();
            return $this->buildTree($query->getArrayResult());
        }
    

    In case you really want to use the closure strategy, it might be possible to use the getNodesHierarchyQueryBuilder() method like this:

        public function getCommentTreeForDiscussion(Discussion $discussion): array
        {
            $query = $this
                ->getNodesHierarchyQueryBuilder(null, false, [], true)
                ->where('node.discussion = :discussion')
                ->setParameter('discussion', $discussion)
                ->getQuery();
            return $this->buildTree($query->getArrayResult());
        }