src/Entity/PostComment.php
<?php
// SPDX-FileCopyrightText: 2023 /kbin contributors <https://kbin.pub/>
//
// SPDX-License-Identifier: AGPL-3.0-only
declare(strict_types=1);
namespace App\Entity;
use App\Entity\Contracts\ActivityPubActivityInterface;
use App\Entity\Contracts\ContentInterface;
use App\Entity\Contracts\FavouriteInterface;
use App\Entity\Contracts\ReportInterface;
use App\Entity\Contracts\TagInterface;
use App\Entity\Contracts\VisibilityInterface;
use App\Entity\Contracts\VotableInterface;
use App\Entity\Traits\ActivityPubActivityTrait;
use App\Entity\Traits\CreatedAtTrait;
use App\Entity\Traits\EditedAtTrait;
use App\Entity\Traits\VisibilityTrait;
use App\Entity\Traits\VotableTrait;
use App\Repository\PostCommentRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Index;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\OrderBy;
use Webmozart\Assert\Assert;
#[Entity(repositoryClass: PostCommentRepository::class)]
#[Index(columns: ['up_votes'], name: 'post_comment_up_votes_idx')]
#[Index(columns: ['score'], name: 'post_comment_score_idx')]
#[Index(columns: ['last_active'], name: 'post_comment_last_active_at_idx')]
#[Index(columns: ['created_at'], name: 'post_comment_created_at_idx')]
#[Index(columns: ['body_ts'], name: 'post_comment_body_ts_idx')]
#[Index(columns: ['ap_id'], name: 'post_comment_ap_id_idx')]
class PostComment implements VotableInterface, VisibilityInterface, ReportInterface, FavouriteInterface, TagInterface, ActivityPubActivityInterface
{
use VotableTrait;
use VisibilityTrait;
use ActivityPubActivityTrait;
use EditedAtTrait;
use CreatedAtTrait {
CreatedAtTrait::__construct as createdAtTraitConstruct;
}
#[ManyToOne(targetEntity: User::class, inversedBy: 'postComments')]
#[JoinColumn(nullable: false)]
public User $user;
#[ManyToOne(targetEntity: Post::class, inversedBy: 'comments')]
#[JoinColumn(nullable: false, onDelete: 'CASCADE')]
public ?Post $post;
#[ManyToOne(targetEntity: Magazine::class)]
#[JoinColumn(nullable: false, onDelete: 'CASCADE')]
public ?Magazine $magazine;
#[ManyToOne(targetEntity: PostComment::class, inversedBy: 'children')]
#[JoinColumn(onDelete: 'CASCADE')]
public ?PostComment $parent;
#[ManyToOne(targetEntity: PostComment::class, inversedBy: 'nested')]
#[JoinColumn(onDelete: 'CASCADE')]
public ?PostComment $root = null;
#[ManyToOne(targetEntity: Image::class, cascade: ['persist'])]
#[JoinColumn]
public ?Image $image = null;
#[Column(type: 'text', length: 4500)]
public ?string $body;
#[Column(type: 'string')]
public string $lang = 'en';
#[Column(type: 'integer', options: ['default' => 0])]
public int $favouriteCount = 0;
#[Column(type: 'integer', options: ['default' => 0])]
public int $score = 0;
#[Column(type: 'datetimetz')]
public ?\DateTime $lastActive;
#[Column(type: 'string', nullable: true)]
public ?string $ip = null;
#[Column(type: 'json', nullable: true, options: ['jsonb' => true])]
public ?array $tags = null;
#[Column(type: 'json', nullable: true, options: ['jsonb' => true])]
public ?array $mentions = null;
#[Column(type: 'boolean')]
public bool $isAdult = false;
#[Column(type: 'boolean', options: ['default' => false])]
public ?bool $updateMark = false;
#[OneToMany(mappedBy: 'parent', targetEntity: PostComment::class, orphanRemoval: true)]
#[OrderBy(['createdAt' => 'ASC'])]
public Collection $children;
#[OneToMany(mappedBy: 'root', targetEntity: PostComment::class, orphanRemoval: true)]
#[OrderBy(['createdAt' => 'ASC'])]
public Collection $nested;
#[OneToMany(mappedBy: 'comment', targetEntity: PostCommentVote::class, cascade: ['persist', 'remove'], fetch: 'EXTRA_LAZY', orphanRemoval: true)]
public Collection $votes;
#[OneToMany(mappedBy: 'postComment', targetEntity: PostCommentReport::class, cascade: ['remove'], fetch: 'EXTRA_LAZY', orphanRemoval: true)]
public Collection $reports;
#[OneToMany(mappedBy: 'postComment', targetEntity: PostCommentFavourite::class, cascade: ['remove'], fetch: 'EXTRA_LAZY', orphanRemoval: true)]
public Collection $favourites;
#[OneToMany(mappedBy: 'postComment', targetEntity: PostCommentCreatedNotification::class, cascade: ['remove'], fetch: 'EXTRA_LAZY', orphanRemoval: true)]
public Collection $notifications;
#[Id]
#[GeneratedValue]
#[Column(type: 'integer')]
private int $id;
#[Column(type: 'text', nullable: true, insertable: false, updatable: false, options: ['default' => 'english'])]
private $bodyTs;
public int $ranking = 0;
public int $commentCount = 0;
public function __construct(string $body, ?Post $post, User $user, PostComment $parent = null, string $ip = null)
{
$this->body = $body;
$this->post = $post;
$this->user = $user;
$this->parent = $parent;
$this->ip = $ip;
$this->votes = new ArrayCollection();
$this->children = new ArrayCollection();
$this->reports = new ArrayCollection();
$this->favourites = new ArrayCollection();
if ($parent) {
$this->root = $parent->root ?? $parent;
}
$this->createdAtTraitConstruct();
$this->updateLastActive();
}
public function updateLastActive(): void
{
$this->lastActive = \DateTime::createFromImmutable($this->createdAt);
$this->post->lastActive = \DateTime::createFromImmutable($this->createdAt);
}
public function getId(): int
{
return $this->id;
}
public function getChildrenRecursive(int &$startIndex = 0): \Traversable
{
foreach ($this->children as $child) {
yield $startIndex++ => $child;
yield from $child->getChildrenRecursive($startIndex);
}
}
public function softDelete(): void
{
$this->visibility = VisibilityInterface::VISIBILITY_SOFT_DELETED;
}
public function trash(): void
{
$this->visibility = VisibilityInterface::VISIBILITY_TRASHED;
}
public function restore(): void
{
$this->visibility = VisibilityInterface::VISIBILITY_VISIBLE;
}
public function isAuthor(User $user): bool
{
return $user === $this->user;
}
public function getShortTitle(?int $length = 60): string
{
$body = wordwrap($this->body ?? '', $length);
$body = explode("\n", $body);
return trim($body[0]).(isset($body[1]) ? '...' : '');
}
public function getMagazine(): ?Magazine
{
return $this->magazine;
}
public function getUser(): ?User
{
return $this->user;
}
/**
* @deprecated
*/
public function updateCounts(): self
{
$this->favouriteCount = $this->favourites->count();
return $this;
}
public function isFavored(User $user): bool
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq('user', $user));
return $this->favourites->matching($criteria)->count() > 0;
}
public function getTags(): array
{
return array_values($this->tags ?? []);
}
public function __sleep()
{
return [];
}
public function updateRanking(): void
{
}
public function getParentSubject(): ?ContentInterface
{
return $this->post;
}
}