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);
}
}
The documentation provides an example
on how to use buildTree()
.
There are several issues with your code:
You are not following the example: you are not calling getArrayResult()
on a query object, and you are fetching only the root nodes (i.e. comments with no parent) instead of the whole trees (all the comments that belong to the discussion).
The example works with the nested strategy but not the closure strategy.
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());
}