Forum tree model, i18n, and frontend updates

This commit is contained in:
Micha
2025-12-24 13:15:02 +01:00
parent 98a2f1d536
commit 193273c843
29 changed files with 1115 additions and 218 deletions

178
api/src/Entity/Forum.php Normal file
View File

@@ -0,0 +1,178 @@
<?php
namespace App\Entity;
use ApiPlatform\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
#[ApiFilter(SearchFilter::class, properties: ['parent' => 'exact', 'type' => 'exact'])]
#[ApiFilter(ExistsFilter::class, properties: ['parent'])]
#[ApiResource(
normalizationContext: ['groups' => ['forum:read']],
denormalizationContext: ['groups' => ['forum:write']],
operations: [
new Get(),
new GetCollection(),
new Post(security: "is_granted('ROLE_ADMIN')"),
new Patch(security: "is_granted('ROLE_ADMIN')"),
new Delete(security: "is_granted('ROLE_ADMIN')")
]
)]
class Forum
{
public const TYPE_CATEGORY = 'category';
public const TYPE_FORUM = 'forum';
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['forum:read', 'thread:read'])]
private ?int $id = null;
#[ORM\Column(length: 100)]
#[Assert\NotBlank]
#[Groups(['forum:read', 'forum:write', 'thread:read'])]
private ?string $name = null;
#[ORM\Column(type: 'text', nullable: true)]
#[Groups(['forum:read', 'forum:write'])]
private ?string $description = null;
#[ORM\Column(length: 20)]
#[Assert\Choice(choices: [self::TYPE_CATEGORY, self::TYPE_FORUM])]
#[Groups(['forum:read', 'forum:write'])]
private string $type = self::TYPE_CATEGORY;
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
#[Groups(['forum:read', 'forum:write'])]
private ?self $parent = null;
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[Groups(['forum:read'])]
private Collection $children;
#[ORM\OneToMany(mappedBy: 'forum', targetEntity: Thread::class)]
#[Groups(['forum:read'])]
private Collection $threads;
#[ORM\Column]
#[Groups(['forum:read'])]
private ?\DateTimeImmutable $createdAt = null;
#[ORM\Column]
#[Groups(['forum:read'])]
private ?\DateTimeImmutable $updatedAt = null;
public function __construct()
{
$this->children = new ArrayCollection();
$this->threads = new ArrayCollection();
}
#[ORM\PrePersist]
public function onCreate(): void
{
$now = new \DateTimeImmutable();
$this->createdAt = $now;
$this->updatedAt = $now;
}
#[ORM\PreUpdate]
public function onUpdate(): void
{
$this->updatedAt = new \DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getType(): string
{
return $this->type;
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getParent(): ?self
{
return $this->parent;
}
public function setParent(?self $parent): self
{
$this->parent = $parent;
return $this;
}
/**
* @return Collection<int, Forum>
*/
public function getChildren(): Collection
{
return $this->children;
}
/**
* @return Collection<int, Thread>
*/
public function getThreads(): Collection
{
return $this->threads;
}
public function isCategory(): bool
{
return $this->type === self::TYPE_CATEGORY;
}
public function isForum(): bool
{
return $this->type === self::TYPE_FORUM;
}
}