be aware commit

This commit is contained in:
Flo 2024-10-29 20:32:35 +00:00
parent 71b8a29527
commit 8fbc6291dd
49 changed files with 1382 additions and 4 deletions

View File

@ -4,12 +4,25 @@ return [
'bee-rbac' => [
'roles' => [
'admin',
'beekeeper',
'user',
],
'permissions' => [
'user' => [
],
'beekeeper' => [
'colony.create',
'colony.delete',
'colony.update',
'colony.read-details',
'colony.read-list',
],
'admin' => [
'colony.create',
'colony.delete',
'colony.update',
'colony.read-details',
'colony.read-list',
'user.create-user',
],
]

View File

@ -58,6 +58,7 @@ $aggregator = new ConfigAggregator([
\Bee\Handling\User\ConfigProvider::class,
\Bee\Handling\UserSession\ConfigProvider::class,
\Bee\Handling\Registration\ConfigProvider::class,
\Bee\Handling\Colony\ConfigProvider::class,
// API
/// Command
@ -67,6 +68,7 @@ $aggregator = new ConfigAggregator([
\Bee\API\External\Health\ConfigProvider::class,
\Bee\API\External\User\ConfigProvider::class,
\Bee\API\External\Authentication\ConfigProvider::class,
\Bee\API\External\Colony\ConfigProvider::class,
/// Internal

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Bee\Migrations\Bee;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240915085327 extends AbstractMigration
{
public function getDescription(): string
{
return "Create Table 'colony'";
}
public function up(Schema $schema): void
{
$sql = "CREATE TABLE colony (
id binary(16) NOT NULL,
user_id binary(16) NOT NULL,
name varchar(255) NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id)
);";
$this->addSql($sql);
}
public function down(Schema $schema): void
{
$this->addSql("DROP TABLE colony;");
}
}

View File

@ -66,8 +66,6 @@ class InitializeDataCommand extends Command
$this->entityManager->persist($adminUser);
$this->entityManager->flush();
}
$io->success('OK!');
} catch (\Throwable $e) {
$io->error($e->getMessage());
$io->error($e->getTraceAsString());

View File

@ -84,8 +84,6 @@ class RbacUpdateCommand extends Command
$this->entityManager->flush();
}
}
$io->success('OK!');
} catch (\Throwable $e) {
$io->error($e->getMessage());
$io->error($e->getTraceAsString());

View File

@ -0,0 +1,62 @@
<?php
use Bee\API\External\Colony\Handler\CreateHandler;
use Bee\API\External\Colony\Handler\DeleteHandler;
use Bee\API\External\Colony\Handler\ReadDetailsHandler;
use Bee\API\External\Colony\Handler\ReadListHandler;
use Bee\API\External\Colony\Handler\UpdateHandler;
use Bee\Infrastructure\Rbac\Middleware\EnsureAuthorizationMiddleware;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
return [
[
'name' => 'colony.create',
'path' => '/api/colony/create',
'allowed_methods' => ['POST'],
'middleware' => [
LoggedInUserMiddleware::class,
EnsureAuthorizationMiddleware::class,
CreateHandler::class,
],
],
[
'name' => 'colony.delete',
'path' => '/api/colony/delete',
'allowed_methods' => ['POST'],
'middleware' => [
LoggedInUserMiddleware::class,
EnsureAuthorizationMiddleware::class,
DeleteHandler::class,
],
],
[
'name' => 'colony.update',
'path' => '/api/colony/update',
'allowed_methods' => ['POST'],
'middleware' => [
LoggedInUserMiddleware::class,
EnsureAuthorizationMiddleware::class,
UpdateHandler::class,
],
],
[
'name' => 'colony.read-list',
'path' => '/api/colony/read-list',
'allowed_methods' => ['POST'],
'middleware' => [
LoggedInUserMiddleware::class,
EnsureAuthorizationMiddleware::class,
ReadListHandler::class,
],
],
[
'name' => 'colony.read-details',
'path' => '/api/colony/read-details',
'allowed_methods' => ['POST'],
'middleware' => [
LoggedInUserMiddleware::class,
EnsureAuthorizationMiddleware::class,
ReadDetailsHandler::class,
],
],
];

View File

@ -0,0 +1,31 @@
<?php
use Bee\API\External\Colony\Handler\CreateHandler;
use Bee\API\External\Colony\Handler\DeleteHandler;
use Bee\API\External\Colony\Handler\ReadDetailsHandler;
use Bee\API\External\Colony\Handler\ReadListHandler;
use Bee\API\External\Colony\Handler\UpdateHandler;
use Bee\API\External\Colony\ResponseFormatter\CreateResponseFormatter;
use Bee\API\External\Colony\ResponseFormatter\DeleteResponseFormatter;
use Bee\API\External\Colony\ResponseFormatter\ReadDetailsResponseFormatter;
use Bee\API\External\Colony\ResponseFormatter\ReadListResponseFormatter;
use Bee\API\External\Colony\ResponseFormatter\UpdateResponseFormatter;
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
return [
'factories' => [
// Handler
CreateHandler::class => AutoWiringFactory::class,
DeleteHandler::class => AutoWiringFactory::class,
UpdateHandler::class => AutoWiringFactory::class,
ReadListHandler::class => AutoWiringFactory::class,
ReadDetailsHandler::class => AutoWiringFactory::class,
// Response Formatter
CreateResponseFormatter::class => AutoWiringFactory::class,
DeleteResponseFormatter::class => AutoWiringFactory::class,
UpdateResponseFormatter::class => AutoWiringFactory::class,
ReadListResponseFormatter::class => AutoWiringFactory::class,
ReadDetailsResponseFormatter::class => AutoWiringFactory::class,
],
];

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony;
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => require __DIR__ . '/./../config/service_manager.php',
'routes' => require __DIR__ . '/./../config/routes.php',
];
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\Handler;
use Bee\API\External\Colony\ResponseFormatter\CreateResponseFormatter;
use Bee\Handling\Colony\Handler\Command\Create\CreateCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Create\CreateCommandHandler;
use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware;
use Bee\Infrastructure\Response\SuccessResponse;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CreateHandler implements RequestHandlerInterface
{
public function __construct(
private readonly CreateCommandHandler $handler,
private readonly CreateCommandBuilder $builder,
private readonly CreateResponseFormatter $responseFormatter,
) {
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA);
$user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY);
$createCommand = $this->builder->build(
$user,
$data['name']
);
$result = $this->handler->execute($createCommand);
return new SuccessResponse($this->responseFormatter->format($result));
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\Handler;
use Bee\API\External\Colony\ResponseFormatter\DeleteResponseFormatter;
use Bee\Handling\Colony\Exception\ColonyNotFoundByIdException;
use Bee\Handling\Colony\Exception\ColonyUserInvalidAssociationException;
use Bee\Handling\Colony\Handler\Command\Delete\DeleteCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Delete\DeleteCommandHandler;
use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware;
use Bee\Infrastructure\Response\SuccessResponse;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Ramsey\Uuid\Uuid;
class DeleteHandler implements RequestHandlerInterface
{
public function __construct(
private readonly DeleteCommandHandler $handler,
private readonly DeleteCommandBuilder $builder,
private readonly DeleteResponseFormatter $responseFormatter,
) {
}
/**
* @throws ColonyNotFoundByIdException
* @throws ColonyUserInvalidAssociationException
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA);
$user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY);
$deleteCommand = $this->builder->build(
$user,
Uuid::fromString($data['id'])
);
$result = $this->handler->execute($deleteCommand);
return new SuccessResponse($this->responseFormatter->format($result));
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\Handler;
use Bee\API\External\Colony\ResponseFormatter\ReadDetailsResponseFormatter;
use Bee\Handling\Colony\Handler\Query\ReadDetails\ReadDetailsQueryBuilder;
use Bee\Handling\Colony\Handler\Query\ReadDetails\ReadDetailsQueryHandler;
use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware;
use Bee\Infrastructure\Response\SuccessResponse;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Ramsey\Uuid\Uuid;
class ReadDetailsHandler implements RequestHandlerInterface
{
public function __construct(
private readonly ReadDetailsQueryHandler $handler,
private readonly ReadDetailsQueryBuilder $builder,
private readonly ReadDetailsResponseFormatter $responseFormatter,
) {
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA);
$user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY);
$readDetailsQuery = $this->builder->build(
$user,
Uuid::fromString($data['id'])
);
$result = $this->handler->execute($readDetailsQuery);
return new SuccessResponse($this->responseFormatter->format($result));
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\Handler;
use Bee\API\External\Colony\ResponseFormatter\ReadListResponseFormatter;
use Bee\Handling\Colony\Handler\Query\ReadList\ReadListQueryBuilder;
use Bee\Handling\Colony\Handler\Query\ReadList\ReadListQueryHandler;
use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware;
use Bee\Infrastructure\Response\SuccessResponse;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ReadListHandler implements RequestHandlerInterface
{
public function __construct(
private readonly ReadListQueryHandler $handler,
private readonly ReadListQueryBuilder $builder,
private readonly ReadListResponseFormatter $responseFormatter,
) {
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA);
$user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY);
$readListQuery = $this->builder->build(
$user,
$data['query'],
$data['page'],
$data['perPage'],
$data['orderBy'],
$data['orderDirection'],
);
$result = $this->handler->execute($readListQuery);
return new SuccessResponse($this->responseFormatter->format($result));
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\Handler;
use Bee\API\External\Colony\ResponseFormatter\UpdateResponseFormatter;
use Bee\Handling\Colony\Handler\Command\Update\UpdateCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Update\UpdateCommandHandler;
use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware;
use Bee\Infrastructure\Response\SuccessResponse;
use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Ramsey\Uuid\Uuid;
class UpdateHandler implements RequestHandlerInterface
{
public function __construct(
private readonly UpdateCommandHandler $handler,
private readonly UpdateCommandBuilder $builder,
private readonly UpdateResponseFormatter $responseFormatter,
) {
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA);
$user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY);
$updateCommand = $this->builder->build(
$user,
Uuid::fromString($data['id']),
$data['name'] ?? null,
);
$result = $this->handler->execute($updateCommand);
return new SuccessResponse($this->responseFormatter->format($result));
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\ResponseFormatter;
use Bee\Handling\Colony\Handler\Command\Create\CreateCommandResult;
class CreateResponseFormatter
{
public function format(CreateCommandResult $createCommandResult): array
{
$colony = $createCommandResult->getColony();
return [
'id' => $colony->getId(),
'name' => $colony->getName(),
];
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\ResponseFormatter;
use Bee\Handling\Colony\Handler\Command\Delete\DeleteCommandResult;
class DeleteResponseFormatter
{
public function format(DeleteCommandResult $deleteCommandResult): array
{
return [
];
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\ResponseFormatter;
use Bee\Handling\Colony\Handler\Query\ReadDetails\ReadDetailsQueryResult;
class ReadDetailsResponseFormatter
{
public function format(ReadDetailsQueryResult $readDetailsQueryResult): array
{
$colony = $readDetailsQueryResult->getColony();
return [
'id' => $colony->getId(),
'name' => $colony->getName(),
];
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\ResponseFormatter;
use Bee\Data\Business\Entity\Colony;
use Bee\Handling\Colony\Handler\Query\ReadList\ReadListQueryResult;
class ReadListResponseFormatter
{
public function format(ReadListQueryResult $readListQueryResult): array
{
$items = [];
/** @var Colony $colony */
foreach ($readListQueryResult->getColonies() as $colony) {
$items[] = [
'id' => $colony->getId(),
'name' => $colony->getName()
];
}
return [
"total" => $readListQueryResult->getTotal(),
"items" => $items
];
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\API\External\Colony\ResponseFormatter;
use Bee\Handling\Colony\Handler\Command\Update\UpdateCommandResult;
class UpdateResponseFormatter
{
public function format(UpdateCommandResult $updateCommandResult): array
{
$colony = $updateCommandResult->getColony();
return [
'id' => $colony->getId(),
'name' => $colony->getName()
];
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace Bee\Data\Business\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Bee\Infrastructure\UuidGenerator\UuidGenerator;
use Ramsey\Uuid\UuidInterface;
/**
* @ORM\Entity(repositoryClass="Bee\Data\Business\Repository\ColonyRepository")
* @ORM\Table(name="colony")
*/
class Colony {
/**
* @ORM\Id
* @ORM\Column(name="id", type="uuid_binary_ordered_time")
*/
private UuidInterface $id;
/** @ORM\Column(name="user_id", type="uuid_binary_ordered_time", nullable=false) */
private UuidInterface $userId;
/**
* @ORM\ManyToOne(targetEntity="Bee\Data\Business\Entity\User")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private User $user;
/** @ORM\Column(name="name", type="string") */
private string $name;
/** @ORM\Column(name="created_at", type="datetime") */
private DateTime $createdAt;
/** @ORM\Column(name="updated_at", type="datetime") */
private DateTime $updatedAt;
public function __construct() {
$this->id = UuidGenerator::generate();
$now = new DateTime();
$this->setCreatedAt($now);
$this->setUpdatedAt($now);
}
/**
* @ORM\PrePersist
* @ORM\PreUpdate
*/
public function updateTimestamps(): void {
$now = new DateTime();
$this->setUpdatedAt($now);
}
public function getId(): UuidInterface {
return $this->id;
}
public function getUserId(): UuidInterface {
return $this->userId;
}
public function setUserId(UuidInterface $userId): void {
$this->userId = $userId;
}
public function getUser(): User {
return $this->user;
}
public function setUser(User $user): void {
$this->user = $user;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getCreatedAt(): DateTime {
return $this->createdAt;
}
public function setCreatedAt(DateTime $createdAt): void {
$this->createdAt = $createdAt;
}
public function getUpdatedAt(): DateTime {
return $this->updatedAt;
}
public function setUpdatedAt(DateTime $updatedAt): void {
$this->updatedAt = $updatedAt;
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Bee\Data\Business\Repository;
use Doctrine\ORM\EntityRepository;
class ColonyRepository extends EntityRepository {
}

View File

@ -0,0 +1,47 @@
<?php
use Bee\Handling\Colony\Builder\ColonyBuilder;
use Bee\Handling\Colony\Handler\Command\Create\CreateCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Create\CreateCommandHandler;
use Bee\Handling\Colony\Handler\Command\Delete\DeleteCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Delete\DeleteCommandHandler;
use Bee\Handling\Colony\Handler\Command\Update\UpdateCommandBuilder;
use Bee\Handling\Colony\Handler\Command\Update\UpdateCommandHandler;
use Bee\Handling\Colony\Handler\Query\ReadDetails\ReadDetailsQueryBuilder;
use Bee\Handling\Colony\Handler\Query\ReadDetails\ReadDetailsQueryHandler;
use Bee\Handling\Colony\Handler\Query\ReadList\ReadListQueryBuilder;
use Bee\Handling\Colony\Handler\Query\ReadList\ReadListQueryHandler;
use Bee\Handling\Colony\Repository\ColonyRepository;
use Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule;
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
use Reinfi\DependencyInjection\Factory\InjectionFactory;
return [
'factories' => [
/// Builder
ColonyBuilder::class => AutoWiringFactory::class,
/// Repository
ColonyRepository::class => AutoWiringFactory::class,
/// Rule
ColonyIsAssociatedWithUserRule::class => AutoWiringFactory::class,
/// CQRS
// Create
CreateCommandBuilder::class => AutoWiringFactory::class,
CreateCommandHandler::class => AutoWiringFactory::class,
// Delete
DeleteCommandBuilder::class => AutoWiringFactory::class,
DeleteCommandHandler::class => InjectionFactory::class,
// Update
UpdateCommandBuilder::class => AutoWiringFactory::class,
UpdateCommandHandler::class => InjectionFactory::class,
// Read List
ReadListQueryBuilder::class => AutoWiringFactory::class,
ReadListQueryHandler::class => AutoWiringFactory::class,
// Read Details
ReadDetailsQueryBuilder::class => AutoWiringFactory::class,
ReadDetailsQueryHandler::class => InjectionFactory::class,
],
];

View File

@ -0,0 +1,20 @@
<?php
namespace Bee\Handling\Colony\Builder;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Entity\User;
class ColonyBuilder
{
public function build(
User $user,
string $name
): Colony
{
$colony = new Colony();
$colony->setUser($user);
$colony->setName($name);
return $colony;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony;
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => require __DIR__ . '/./../config/service_manager.php',
];
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Exception;
use Bee\Infrastructure\Exception\ErrorCode;
use Bee\Infrastructure\Exception\ErrorDomain;
use Bee\Infrastructure\Exception\Exception\Exception;
use Ramsey\Uuid\UuidInterface;
class ColonyNotFoundByIdException extends Exception
{
private const MESSAGE = 'A Colony with the id %s was not be found!';
public function __construct(
UuidInterface $id
) {
parent::__construct(
sprintf(
self::MESSAGE,
$id->toString()
),
ErrorDomain::Colony,
ErrorCode::NotFound
);
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Exception;
use Bee\Infrastructure\Exception\ErrorCode;
use Bee\Infrastructure\Exception\ErrorDomain;
use Bee\Infrastructure\Exception\Exception\Exception;
use Ramsey\Uuid\UuidInterface;
class ColonyUserInvalidAssociationException extends Exception
{
private const MESSAGE = 'The Colony with the id %s is not associated with User %s!';
public function __construct(
UuidInterface $id,
UuidInterface $userId,
) {
parent::__construct(
sprintf(
self::MESSAGE,
$id->toString(),
$userId->toString()
),
ErrorDomain::Colony,
ErrorCode::UserInvalidAssociation
);
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Create;
use Bee\Data\Business\Entity\User;
class CreateCommand
{
public function __construct(
private readonly User $user,
private readonly string $name,
) {
}
public function getUser(): User
{
return $this->user;
}
public function getName(): string
{
return $this->name;
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Create;
use Bee\Data\Business\Entity\User;
class CreateCommandBuilder
{
public function build(
User $user,
string $name,
): CreateCommand {
return new CreateCommand(
$user,
$name,
);
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Create;
use Bee\Data\Business\Manager\EntityManager;
use Bee\Handling\Colony\Builder\ColonyBuilder;
class CreateCommandHandler
{
public function __construct(
private readonly ColonyBuilder $builder,
private readonly EntityManager $entityManager,
) {
}
public function execute(CreateCommand $createCommand): CreateCommandResult
{
$colony = $this->builder->build(
$createCommand->getUser(),
$createCommand->getName(),
);
$this->entityManager->persist($colony);
$this->entityManager->flush();
return new CreateCommandResult($colony);
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Create;
use Bee\Data\Business\Entity\Colony;
class CreateCommandResult
{
public function __construct(
private readonly Colony $colony,
) {
}
public function getColony(): Colony
{
return $this->colony;
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Delete;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class DeleteCommand
{
public function __construct(
private readonly User $user,
private readonly UuidInterface $colonyId
) {
}
public function getUser(): User
{
return $this->user;
}
public function getColonyId(): UuidInterface
{
return $this->colonyId;
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Delete;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class DeleteCommandBuilder
{
public function build(
User $user,
UuidInterface $colonyId
): DeleteCommand {
return new DeleteCommand(
$user,
$colonyId
);
}
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Delete;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Manager\EntityManager;
use Bee\Data\Business\Repository\ColonyRepository;
use Bee\Handling\Colony\Exception\ColonyNotFoundByIdException;
use Bee\Handling\Colony\Exception\ColonyUserInvalidAssociationException;
use Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule;
use Reinfi\DependencyInjection\Annotation\Inject;
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
class DeleteCommandHandler
{
/**
* @InjectDoctrineRepository(
* entityManager="Bee\Data\Business\Manager\EntityManager",
* entity="Bee\Data\Business\Entity\Colony"
* )
* @Inject("Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule")
* @Inject("Bee\Data\Business\Manager\EntityManager")
*/
public function __construct(
private readonly ColonyRepository $colonyRepository,
private readonly ColonyIsAssociatedWithUserRule $colonyIsAssociatedWithUserRule,
private readonly EntityManager $entityManager,
) {
}
public function execute(DeleteCommand $deleteCommand): DeleteCommandResult
{
/** @var Colony $colony */
$colony = $this->colonyRepository->findOneBy(['id' => $deleteCommand->getColonyId()]);
if ($colony === null) {
throw new ColonyNotFoundByIdException($deleteCommand->getColonyId());
}
if (!$this->colonyIsAssociatedWithUserRule->appliesTo($colony, $deleteCommand->getUser())) {
throw new ColonyUserInvalidAssociationException($colony->getId(), $deleteCommand->getUser()->getId());
}
$this->entityManager->remove($colony);
$this->entityManager->flush();
return new DeleteCommandResult();
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Delete;
class DeleteCommandResult
{
public function __construct(
#TODO
) {
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Update;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class UpdateCommand
{
public function __construct(
private readonly User $user,
private readonly UuidInterface $colonyId,
private readonly ?string $name,
) {
}
public function getUser(): User
{
return $this->user;
}
public function getColonyId(): UuidInterface
{
return $this->colonyId;
}
public function getName(): ?string
{
return $this->name;
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Update;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class UpdateCommandBuilder
{
public function build(
User $user,
UuidInterface $colonyId,
?string $name,
): UpdateCommand {
return new UpdateCommand(
$user,
$colonyId,
$name
);
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Update;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Manager\EntityManager;
use Bee\Data\Business\Repository\ColonyRepository;
use Bee\Handling\Colony\Exception\ColonyNotFoundByIdException;
use Bee\Handling\Colony\Exception\ColonyUserInvalidAssociationException;
use Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule;
use Reinfi\DependencyInjection\Annotation\Inject;
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
class UpdateCommandHandler
{
/**
* @InjectDoctrineRepository(
* entityManager="Bee\Data\Business\Manager\EntityManager",
* entity="Bee\Data\Business\Entity\Colony"
* )
* @Inject("Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule")
* @Inject("Bee\Data\Business\Manager\EntityManager")
*/
public function __construct(
private readonly ColonyRepository $colonyRepository,
private readonly ColonyIsAssociatedWithUserRule $colonyIsAssociatedWithUserRule,
private readonly EntityManager $entityManager,
) {
}
public function execute(UpdateCommand $updateCommand): UpdateCommandResult
{
/** @var Colony $colony */
$colony = $this->colonyRepository->findOneBy(['id' => $updateCommand->getColonyId()]);
if ($colony === null) {
throw new ColonyNotFoundByIdException($updateCommand->getColonyId());
}
if (!$this->colonyIsAssociatedWithUserRule->appliesTo($colony, $updateCommand->getUser())) {
throw new ColonyUserInvalidAssociationException($colony->getId(), $updateCommand->getUser()->getId());
}
if ($updateCommand->getName() !== null) {
$colony->setName($updateCommand->getName());
}
$this->entityManager->persist($colony);
$this->entityManager->flush();
return new UpdateCommandResult($colony);
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Command\Update;
use Bee\Data\Business\Entity\Colony;
class UpdateCommandResult
{
public function __construct(
private readonly Colony $colony
) {
}
public function getColony(): Colony
{
return $this->colony;
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadDetails;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class ReadDetailsQuery
{
public function __construct(
private readonly User $user,
private readonly UuidInterface $colonyId
) {
}
public function getUser(): User
{
return $this->user;
}
public function getColonyId(): UuidInterface
{
return $this->colonyId;
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadDetails;
use Bee\Data\Business\Entity\User;
use Ramsey\Uuid\UuidInterface;
class ReadDetailsQueryBuilder
{
public function build(
User $user,
UuidInterface $colonyId
): ReadDetailsQuery {
return new ReadDetailsQuery(
$user,
$colonyId
);
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadDetails;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Repository\ColonyRepository;
use Bee\Handling\Colony\Exception\ColonyNotFoundByIdException;
use Bee\Handling\Colony\Exception\ColonyUserInvalidAssociationException;
use Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule;
use Reinfi\DependencyInjection\Annotation\Inject;
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
class ReadDetailsQueryHandler
{
/**
* @InjectDoctrineRepository(
* entityManager="Bee\Data\Business\Manager\EntityManager",
* entity="Bee\Data\Business\Entity\Colony"
* )
* @Inject("Bee\Handling\Colony\Rule\ColonyIsAssociatedWithUserRule")
*/
public function __construct(
private readonly ColonyRepository $colonyRepository,
private readonly ColonyIsAssociatedWithUserRule $colonyIsAssociatedWithUserRule,
) {
}
/**
* @throws ColonyNotFoundByIdException
* @throws ColonyUserInvalidAssociationException
*/
public function execute(ReadDetailsQuery $readDetailsQuery): ReadDetailsQueryResult
{
/** @var Colony $colony */
$colony = $this->colonyRepository->findOneBy(['id' => $readDetailsQuery->getColonyId()]);
if ($colony === null) {
throw new ColonyNotFoundByIdException($readDetailsQuery->getColonyId());
}
if (!$this->colonyIsAssociatedWithUserRule->appliesTo($colony, $readDetailsQuery->getUser())) {
throw new ColonyUserInvalidAssociationException($colony->getId(), $readDetailsQuery->getUser()->getId());
}
return new ReadDetailsQueryResult($colony);
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadDetails;
use Bee\Data\Business\Entity\Colony;
class ReadDetailsQueryResult
{
public function __construct(
private readonly Colony $colony,
) {
}
public function getColony(): Colony
{
return $this->colony;
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadList;
use Bee\Data\Business\Entity\User;
class ReadListQuery
{
public function __construct(
private readonly User $user,
private readonly ?string $query,
private readonly int $page,
private readonly int $perPage,
private readonly ?string $orderBy,
private readonly ?string $orderDirection,
) {
}
public function getUser(): User
{
return $this->user;
}
public function getQuery(): ?string
{
return $this->query;
}
public function getPage(): int
{
return $this->page;
}
public function getPerPage(): int
{
return $this->perPage;
}
public function getOrderBy(): ?string
{
return $this->orderBy;
}
public function getOrderDirection(): ?string
{
return $this->orderDirection;
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadList;
use Bee\Data\Business\Entity\User;
class ReadListQueryBuilder
{
public function build(
User $user,
?string $query,
int $page,
int $perPage,
?string $orderBy,
?string $orderDirection,
): ReadListQuery {
return new ReadListQuery(
$user,
$query,
$page,
$perPage,
$orderBy,
$orderDirection,
);
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadList;
use Bee\Handling\Colony\Repository\ColonyRepository;
class ReadListQueryHandler
{
public function __construct(
private ColonyRepository $repository
) {
}
public function execute(ReadListQuery $readListQuery): ReadListQueryResult
{
$coloniesPaginator = $this->repository->readColonyList(
$readListQuery->getUser(),
$readListQuery->getQuery(),
$readListQuery->getPage(),
$readListQuery->getPerPage(),
$readListQuery->getOrderBy(),
$readListQuery->getOrderDirection()
);
return new ReadListQueryResult(
$coloniesPaginator->count(),
iterator_to_array($coloniesPaginator->getIterator())
);
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Handler\Query\ReadList;
class ReadListQueryResult
{
public function __construct(
private readonly int $total,
private readonly array $colonies
) {
}
public function getTotal(): int
{
return $this->total;
}
public function getColonies(): array
{
return $this->colonies;
}
}

View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Repository;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Entity\User;
use Bee\Data\Business\Manager\EntityManager;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType;
class ColonyRepository
{
private const FIELD_MAPPING = [
"name" => "c.name"
];
public function __construct(
private readonly EntityManager $entityManager
) {
}
public function readColonyList(
User $user,
?string $query,
int $page,
int $perPage,
?string $orderBy,
?string $orderDirection,
): Paginator
{
$orderBy = self::FIELD_MAPPING[$orderBy] ?? self::FIELD_MAPPING['name'];
$orderDirection = $orderDirection ?? 'asc';
$qb = $this->entityManager->createQueryBuilder();
$qb->select('c')
->from(Colony::class, 'c')
->where('c.userId = :userId')
->setParameter('userId', $user->getId(), UuidBinaryOrderedTimeType::NAME);
if ($query !== null) {
$query = "%" . $query . "%";
$qb->andWhere('c.name like :query')
->setParameter('query', $query);
}
$qb->orderBy($orderBy, $orderDirection);
$qb->setMaxResults($perPage);
$qb->setFirstResult($perPage * ($page-1));
return new Paginator($qb->getQuery());
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Bee\Handling\Colony\Rule;
use Bee\Data\Business\Entity\Colony;
use Bee\Data\Business\Entity\User;
class ColonyIsAssociatedWithUserRule
{
public function appliesTo(Colony $colony, User $user): bool {
return $colony->getUser() === $user;
}
}

View File

@ -6,6 +6,7 @@ enum ErrorCode : string {
case SomethingWentWrong = 'SomethingWentWrong';
case NotFound = 'NotFound';
case UserInvalidAssociation = 'UserInvalidAssociation';
case AlreadyExists = 'AlreadyExists';
case WrongCredentials = 'WrongCredentials';
case Mismatch = 'Mismatch';

View File

@ -4,6 +4,7 @@ namespace Bee\Infrastructure\Exception;
enum ErrorDomain : string {
case Generic = 'Generic';
case Colony = 'Colony';
case Role = 'Role';
case User = 'User';