Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A set of Doctrine Entity features to automate repetitive entity tasks and ease d
- [x] [UUID](docs/uuid.md)
- [x] [IP Tagged](docs/ip_tagged.md)
- [x] [Soft Delete](docs/soft_delete.md)
- [x] [Singleton](docs/singleton.md)
- [ ] Logging
- [ ] Tree
- [ ] Translation
Expand Down
6 changes: 6 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Rami\EntityKitBundle\EventListener\Authored\AuthoredListener;
use Rami\EntityKitBundle\EventListener\IpTagged\IpTaggedListener;
use Rami\EntityKitBundle\EventListener\LoggedEntity\LoggedEntityListener;
use Rami\EntityKitBundle\EventListener\Singleton\SingletonListener;
use Rami\EntityKitBundle\EventListener\Slugged\SluggedListener;
use Rami\EntityKitBundle\EventListener\SoftDelete\SoftDeleteFilterListener;
use Rami\EntityKitBundle\EventListener\TimeStamped\TimeStampedListener;
Expand Down Expand Up @@ -60,4 +61,9 @@
->set(SoftDeleteFilterListener::class)
->args([new Reference(ManagerRegistry::class), new Reference(ParameterBagInterface::class)])
->tag('kernel.event_listener', ['event' => 'kernel.controller', 'method' => 'onKernelController']);

$services
->set(SingletonListener::class)
->args([new Reference(ManagerRegistry::class)])
->tag('doctrine.event_listener', ['event' => 'prePersist']);
};
19 changes: 19 additions & 0 deletions docs/singleton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Singleton
-
Add a singleton entity where only one entity can be created or new entities cannot be created if one or more already exists.

This is good for an app configuration saved in the database

use the `Singleton` attribute on your entity class

```php
use Rami\EntityKitBundle\Common\Attributes\Singleton

#[Singleton]
class Blog
{
// properties omitted
}
```

This prevents new entities of type `Blog` from being created.
20 changes: 20 additions & 0 deletions src/Common/Attributes/Singleton.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/*
* Copyright (c) 2025.
*
* This file is part of the Entity Kit Bundle project
* @author Abdellah Ramadan <ramadanabdel24@gmail.com>
*
* For the full copyright and license information,
* please view the LICENSE file that was distributed with this source code.
*/

namespace Rami\EntityKitBundle\Common\Attributes;

use Attribute;

#[\Attribute(Attribute::TARGET_CLASS)]
class Singleton
{

}
8 changes: 8 additions & 0 deletions src/Entity/Traits/TreeTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Rami\EntityKitBundle\Entity\Traits;

trait TreeTrait
{

}
47 changes: 47 additions & 0 deletions src/EventListener/Singleton/SingletonListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/*
* Copyright (c) 2025.
*
* This file is part of the Entity Kit Bundle project
* @author Abdellah Ramadan <ramadanabdel24@gmail.com>
*
* For the full copyright and license information,
* please view the LICENSE file that was distributed with this source code.
*/

namespace Rami\EntityKitBundle\EventListener\Singleton;

use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\Persistence\ManagerRegistry;
use JetBrains\PhpStorm\NoReturn;
use Rami\EntityKitBundle\Common\Attributes\Singleton;
use Rami\EntityKitBundle\Exceptions\EntityCountException;

class SingletonListener
{

public function __construct(private ManagerRegistry $managerRegistry)
{
}

/**
* @throws EntityCountException
*/
#[NoReturn] public function prePersist(PrePersistEventArgs $args): void
{
$entity = $args->getObject();

$reflection = new \ReflectionObject($entity);
$attributes = $reflection->getAttributes(Singleton::class);

if (count($attributes) === 0) {
return;
}

$existingEntity = $this->managerRegistry->getManager()->getRepository(get_class($entity))->findOneBy([]);

if (null !== $existingEntity) {
throw new EntityCountException();
}
}
}
22 changes: 22 additions & 0 deletions src/Exceptions/EntityCountException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
/*
* Copyright (c) 2025.
*
* This file is part of the Entity Kit Bundle project
* @author Abdellah Ramadan <ramadanabdel24@gmail.com>
*
* For the full copyright and license information,
* please view the LICENSE file that was distributed with this source code.
*/

namespace Rami\EntityKitBundle\Exceptions;

use Symfony\Component\HttpFoundation\Response;

class EntityCountException extends \Exception
{
protected $code = Response::HTTP_CONFLICT;

protected $message = 'This entity is a singleton. Entity has already been created.';

}
Loading