feat(notification) : add Notification entity, repository, and migration
This commit is contained in:
38
migrations/Version20260315184538.php
Normal file
38
migrations/Version20260315184538.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20260315184538 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE notification (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, type VARCHAR(50) NOT NULL, title VARCHAR(255) NOT NULL, message TEXT NOT NULL, is_read BOOLEAN NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, user_id INT NOT NULL, related_ticket_id INT DEFAULT NULL, PRIMARY KEY (id))');
|
||||
$this->addSql('CREATE INDEX IDX_BF5476CAD8C11BC9 ON notification (related_ticket_id)');
|
||||
$this->addSql('CREATE INDEX idx_notification_user ON notification (user_id)');
|
||||
$this->addSql('CREATE INDEX idx_notification_user_read ON notification (user_id, is_read)');
|
||||
$this->addSql('ALTER TABLE notification ADD CONSTRAINT FK_BF5476CAA76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE');
|
||||
$this->addSql('ALTER TABLE notification ADD CONSTRAINT FK_BF5476CAD8C11BC9 FOREIGN KEY (related_ticket_id) REFERENCES client_ticket (id) ON DELETE SET NULL NOT DEFERRABLE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE notification DROP CONSTRAINT FK_BF5476CAA76ED395');
|
||||
$this->addSql('ALTER TABLE notification DROP CONSTRAINT FK_BF5476CAD8C11BC9');
|
||||
$this->addSql('DROP TABLE notification');
|
||||
}
|
||||
}
|
||||
160
src/Entity/Notification.php
Normal file
160
src/Entity/Notification.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use App\Repository\NotificationRepository;
|
||||
use App\State\NotificationProvider;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
provider: NotificationProvider::class,
|
||||
security: "is_granted('IS_AUTHENTICATED_FULLY')",
|
||||
),
|
||||
new Patch(
|
||||
security: "is_granted('IS_AUTHENTICATED_FULLY') and object.getUser() == user",
|
||||
),
|
||||
],
|
||||
normalizationContext: ['groups' => ['notification:read']],
|
||||
denormalizationContext: ['groups' => ['notification:write']],
|
||||
order: ['createdAt' => 'DESC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: NotificationRepository::class)]
|
||||
#[ORM\Index(columns: ['user_id'], name: 'idx_notification_user')]
|
||||
#[ORM\Index(columns: ['user_id', 'is_read'], name: 'idx_notification_user_read')]
|
||||
class Notification
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(length: 50)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $type = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $message = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: ClientTicket::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?ClientTicket $relatedTicket = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read', 'notification:write'])]
|
||||
private bool $isRead = false;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(string $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage(string $message): static
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRelatedTicket(): ?ClientTicket
|
||||
{
|
||||
return $this->relatedTicket;
|
||||
}
|
||||
|
||||
public function setRelatedTicket(?ClientTicket $relatedTicket): static
|
||||
{
|
||||
$this->relatedTicket = $relatedTicket;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRead(): bool
|
||||
{
|
||||
return $this->isRead;
|
||||
}
|
||||
|
||||
public function setIsRead(bool $isRead): static
|
||||
{
|
||||
$this->isRead = $isRead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
46
src/Repository/NotificationRepository.php
Normal file
46
src/Repository/NotificationRepository.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Notification;
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Notification>
|
||||
*/
|
||||
class NotificationRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Notification::class);
|
||||
}
|
||||
|
||||
public function countUnreadByUser(User $user): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('n')
|
||||
->select('COUNT(n.id)')
|
||||
->where('n.user = :user')
|
||||
->andWhere('n.isRead = false')
|
||||
->setParameter('user', $user)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function markAllReadByUser(User $user): int
|
||||
{
|
||||
return $this->createQueryBuilder('n')
|
||||
->update()
|
||||
->set('n.isRead', 'true')
|
||||
->where('n.user = :user')
|
||||
->andWhere('n.isRead = false')
|
||||
->setParameter('user', $user)
|
||||
->getQuery()
|
||||
->executeStatement()
|
||||
;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user