feat: ajout de la gestion des sources de contenu avec des commandes et des gestionnaires pour l'importation, la mise à jour et l'exportation, ainsi que la création des ressources API correspondantes.
This commit is contained in:
parent
ebcca466a9
commit
32b4e4fbb2
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Command;
|
||||
|
||||
readonly class ImportContentSourceCommand
|
||||
{
|
||||
public function __construct(
|
||||
public array $contentSources
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Command;
|
||||
|
||||
readonly class UpsertContentSourceCommand
|
||||
{
|
||||
public function __construct(
|
||||
public ?int $id,
|
||||
public string $baseUrl,
|
||||
public string $chapterUrlFormat,
|
||||
public string $scrapingType,
|
||||
public ?string $imageSelector = null,
|
||||
public ?string $nextPageSelector = null,
|
||||
public ?string $chapterSelector = null,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Setting\Application\Command\ImportContentSourceCommand;
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
use InvalidArgumentException;
|
||||
|
||||
readonly class ImportContentSourceCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ContentSourceRepositoryInterface $contentSourceRepository
|
||||
) {}
|
||||
|
||||
public function handle(ImportContentSourceCommand $command): void
|
||||
{
|
||||
// Validation des données d'import
|
||||
if (!is_array($command->contentSources)) {
|
||||
throw new InvalidArgumentException('Content sources must be an array');
|
||||
}
|
||||
|
||||
$contentSourcesToImport = [];
|
||||
|
||||
foreach ($command->contentSources as $data) {
|
||||
if (!$this->isValidContentSourceData($data)) {
|
||||
throw new InvalidArgumentException('Invalid content source data provided');
|
||||
}
|
||||
|
||||
$contentSource = ContentSource::create(
|
||||
baseUrl: $data['baseUrl'],
|
||||
chapterUrlFormat: $data['chapterUrlFormat'],
|
||||
scrapingType: $data['scrapingType'],
|
||||
imageSelector: $data['imageSelector'] ?? null,
|
||||
nextPageSelector: $data['nextPageSelector'] ?? null,
|
||||
chapterSelector: $data['chapterSelector'] ?? null,
|
||||
);
|
||||
|
||||
$contentSourcesToImport[] = $contentSource;
|
||||
}
|
||||
|
||||
// Supprime tous les content sources existants puis importe les nouveaux
|
||||
$this->contentSourceRepository->deleteAll();
|
||||
$this->contentSourceRepository->saveMultiple($contentSourcesToImport);
|
||||
}
|
||||
|
||||
private function isValidContentSourceData(mixed $data): bool
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$requiredFields = ['baseUrl', 'chapterUrlFormat', 'scrapingType'];
|
||||
foreach ($requiredFields as $field) {
|
||||
if (!isset($data[$field]) || !is_string($data[$field]) || empty($data[$field])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Setting\Application\Command\UpsertContentSourceCommand;
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
|
||||
readonly class UpsertContentSourceCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ContentSourceRepositoryInterface $contentSourceRepository
|
||||
) {}
|
||||
|
||||
public function handle(UpsertContentSourceCommand $command): void
|
||||
{
|
||||
if ($command->id) {
|
||||
// Update existing
|
||||
$contentSource = $this->contentSourceRepository->findById($command->id);
|
||||
if ($contentSource) {
|
||||
$contentSource->update(
|
||||
baseUrl: $command->baseUrl,
|
||||
chapterUrlFormat: $command->chapterUrlFormat,
|
||||
scrapingType: $command->scrapingType,
|
||||
imageSelector: $command->imageSelector,
|
||||
nextPageSelector: $command->nextPageSelector,
|
||||
chapterSelector: $command->chapterSelector,
|
||||
);
|
||||
$this->contentSourceRepository->save($contentSource);
|
||||
}
|
||||
} else {
|
||||
// Create new
|
||||
$contentSource = ContentSource::create(
|
||||
baseUrl: $command->baseUrl,
|
||||
chapterUrlFormat: $command->chapterUrlFormat,
|
||||
scrapingType: $command->scrapingType,
|
||||
imageSelector: $command->imageSelector,
|
||||
nextPageSelector: $command->nextPageSelector,
|
||||
chapterSelector: $command->chapterSelector,
|
||||
);
|
||||
$this->contentSourceRepository->save($contentSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Query;
|
||||
|
||||
readonly class ExportContentSourceQuery
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Pas de paramètres pour cette query simple
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Query;
|
||||
|
||||
readonly class GetContentSourceQuery
|
||||
{
|
||||
public function __construct(
|
||||
public int $id
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Query;
|
||||
|
||||
readonly class ListContentSourceQuery
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Pas de paramètres pour cette query simple
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\QueryHandler;
|
||||
|
||||
use App\Domain\Setting\Application\Query\ExportContentSourceQuery;
|
||||
use App\Domain\Setting\Application\Response\ExportContentSourceResponse;
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
use DateTimeImmutable;
|
||||
|
||||
readonly class ExportContentSourceQueryHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ContentSourceRepositoryInterface $contentSourceRepository
|
||||
) {}
|
||||
|
||||
public function handle(ExportContentSourceQuery $query): ExportContentSourceResponse
|
||||
{
|
||||
$contentSources = $this->contentSourceRepository->findAll();
|
||||
|
||||
$exportData = array_map(function ($contentSource) {
|
||||
return [
|
||||
'baseUrl' => $contentSource->getBaseUrl(),
|
||||
'chapterUrlFormat' => $contentSource->getChapterUrlFormat(),
|
||||
'scrapingType' => $contentSource->getScrapingType(),
|
||||
'imageSelector' => $contentSource->getImageSelector(),
|
||||
'nextPageSelector' => $contentSource->getNextPageSelector(),
|
||||
'chapterSelector' => $contentSource->getChapterSelector(),
|
||||
];
|
||||
}, $contentSources);
|
||||
|
||||
return new ExportContentSourceResponse(
|
||||
contentSources: $exportData,
|
||||
exportDate: (new DateTimeImmutable())->format('Y-m-d H:i:s')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\QueryHandler;
|
||||
|
||||
use App\Domain\Setting\Application\Query\GetContentSourceQuery;
|
||||
use App\Domain\Setting\Application\Response\ContentSourceResponse;
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
use App\Domain\Setting\Domain\Exception\ContentSourceNotFoundException;
|
||||
|
||||
readonly class GetContentSourceQueryHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ContentSourceRepositoryInterface $contentSourceRepository
|
||||
) {}
|
||||
|
||||
public function handle(GetContentSourceQuery $query): ContentSourceResponse
|
||||
{
|
||||
$contentSource = $this->contentSourceRepository->findById($query->id);
|
||||
|
||||
if (!$contentSource) {
|
||||
throw new ContentSourceNotFoundException($query->id);
|
||||
}
|
||||
|
||||
return ContentSourceResponse::fromDomain($contentSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\QueryHandler;
|
||||
|
||||
use App\Domain\Setting\Application\Query\ListContentSourceQuery;
|
||||
use App\Domain\Setting\Application\Response\ContentSourceListResponse;
|
||||
use App\Domain\Setting\Application\Response\ContentSourceResponse;
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
|
||||
readonly class ListContentSourceQueryHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ContentSourceRepositoryInterface $contentSourceRepository
|
||||
) {}
|
||||
|
||||
public function handle(ListContentSourceQuery $query): ContentSourceListResponse
|
||||
{
|
||||
$contentSources = $this->contentSourceRepository->findAll();
|
||||
|
||||
$responses = array_map(
|
||||
fn($contentSource) => ContentSourceResponse::fromDomain($contentSource),
|
||||
$contentSources
|
||||
);
|
||||
|
||||
return new ContentSourceListResponse(
|
||||
contentSources: $responses,
|
||||
total: count($responses)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Response;
|
||||
|
||||
readonly class ContentSourceListResponse
|
||||
{
|
||||
/**
|
||||
* @param ContentSourceResponse[] $contentSources
|
||||
*/
|
||||
public function __construct(
|
||||
public array $contentSources,
|
||||
public int $total,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Response;
|
||||
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
|
||||
readonly class ContentSourceResponse
|
||||
{
|
||||
public function __construct(
|
||||
public int $id,
|
||||
public string $baseUrl,
|
||||
public string $chapterUrlFormat,
|
||||
public string $scrapingType,
|
||||
public ?string $imageSelector,
|
||||
public ?string $nextPageSelector,
|
||||
public ?string $chapterSelector,
|
||||
public string $cleanBaseUrl,
|
||||
) {}
|
||||
|
||||
public static function fromDomain(ContentSource $contentSource): self
|
||||
{
|
||||
return new self(
|
||||
id: $contentSource->getId(),
|
||||
baseUrl: $contentSource->getBaseUrl(),
|
||||
chapterUrlFormat: $contentSource->getChapterUrlFormat(),
|
||||
scrapingType: $contentSource->getScrapingType(),
|
||||
imageSelector: $contentSource->getImageSelector(),
|
||||
nextPageSelector: $contentSource->getNextPageSelector(),
|
||||
chapterSelector: $contentSource->getChapterSelector(),
|
||||
cleanBaseUrl: $contentSource->getCleanBaseUrl(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Application\Response;
|
||||
|
||||
readonly class ExportContentSourceResponse
|
||||
{
|
||||
public function __construct(
|
||||
public array $contentSources,
|
||||
public string $exportDate,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Domain\Contract\Repository;
|
||||
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
|
||||
interface ContentSourceRepositoryInterface
|
||||
{
|
||||
public function findAll(): array;
|
||||
public function findById(int $id): ?ContentSource;
|
||||
public function save(ContentSource $contentSource): void;
|
||||
public function delete(ContentSource $contentSource): void;
|
||||
public function deleteAll(): void;
|
||||
/**
|
||||
* @param ContentSource[] $contentSources
|
||||
*/
|
||||
public function saveMultiple(array $contentSources): void;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Domain\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class ContentSourceNotFoundException extends RuntimeException
|
||||
{
|
||||
public function __construct(int $id)
|
||||
{
|
||||
parent::__construct(sprintf('ContentSource with id %d not found', $id));
|
||||
}
|
||||
}
|
||||
100
src/Domain/Setting/Domain/Model/ContentSource.php
Normal file
100
src/Domain/Setting/Domain/Model/ContentSource.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Domain\Model;
|
||||
|
||||
final class ContentSource
|
||||
{
|
||||
public function __construct(
|
||||
private ?int $id,
|
||||
private string $baseUrl,
|
||||
private string $chapterUrlFormat,
|
||||
private string $scrapingType,
|
||||
private ?string $imageSelector = null,
|
||||
private ?string $nextPageSelector = null,
|
||||
private ?string $chapterSelector = null,
|
||||
) {}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getBaseUrl(): string
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
public function getChapterUrlFormat(): string
|
||||
{
|
||||
return $this->chapterUrlFormat;
|
||||
}
|
||||
|
||||
public function getScrapingType(): string
|
||||
{
|
||||
return $this->scrapingType;
|
||||
}
|
||||
|
||||
public function getImageSelector(): ?string
|
||||
{
|
||||
return $this->imageSelector;
|
||||
}
|
||||
|
||||
public function getNextPageSelector(): ?string
|
||||
{
|
||||
return $this->nextPageSelector;
|
||||
}
|
||||
|
||||
public function getChapterSelector(): ?string
|
||||
{
|
||||
return $this->chapterSelector;
|
||||
}
|
||||
|
||||
public function updateId(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getCleanBaseUrl(): string
|
||||
{
|
||||
return preg_replace(
|
||||
'/^(https?:\/\/)?(www\.)?|\/+$/',
|
||||
'',
|
||||
$this->baseUrl
|
||||
);
|
||||
}
|
||||
|
||||
public static function create(
|
||||
string $baseUrl,
|
||||
string $chapterUrlFormat,
|
||||
string $scrapingType,
|
||||
?string $imageSelector = null,
|
||||
?string $nextPageSelector = null,
|
||||
?string $chapterSelector = null,
|
||||
): self {
|
||||
return new self(
|
||||
id: null,
|
||||
baseUrl: $baseUrl,
|
||||
chapterUrlFormat: $chapterUrlFormat,
|
||||
scrapingType: $scrapingType,
|
||||
imageSelector: $imageSelector,
|
||||
nextPageSelector: $nextPageSelector,
|
||||
chapterSelector: $chapterSelector,
|
||||
);
|
||||
}
|
||||
|
||||
public function update(
|
||||
string $baseUrl,
|
||||
string $chapterUrlFormat,
|
||||
string $scrapingType,
|
||||
?string $imageSelector = null,
|
||||
?string $nextPageSelector = null,
|
||||
?string $chapterSelector = null,
|
||||
): void {
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->chapterUrlFormat = $chapterUrlFormat;
|
||||
$this->scrapingType = $scrapingType;
|
||||
$this->imageSelector = $imageSelector;
|
||||
$this->nextPageSelector = $nextPageSelector;
|
||||
$this->chapterSelector = $chapterSelector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider\ExportContentSourceStateProvider;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'ContentSourceExport',
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/content-sources/export',
|
||||
provider: ExportContentSourceStateProvider::class,
|
||||
description: 'Exporte toutes les sources de contenu au format JSON'
|
||||
)
|
||||
]
|
||||
)]
|
||||
class ExportContentSourceResource
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider\GetContentSourceStateProvider;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'ContentSource',
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/content-sources/{id}',
|
||||
provider: GetContentSourceStateProvider::class,
|
||||
description: 'Récupère une source de contenu par son identifiant'
|
||||
)
|
||||
]
|
||||
)]
|
||||
class GetContentSourceResource
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Type('integer')]
|
||||
public readonly int $id,
|
||||
public readonly string $baseUrl,
|
||||
public readonly string $chapterUrlFormat,
|
||||
public readonly string $scrapingType,
|
||||
public readonly ?string $imageSelector,
|
||||
public readonly ?string $nextPageSelector,
|
||||
public readonly ?string $chapterSelector,
|
||||
public readonly string $cleanBaseUrl,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\State\Processor\ImportContentSourceStateProcessor;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'ContentSourceImport',
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/content-sources/import',
|
||||
processor: ImportContentSourceStateProcessor::class,
|
||||
input: ImportContentSourceResource::class,
|
||||
status: 201,
|
||||
description: 'Importe des sources de contenu depuis un tableau JSON'
|
||||
)
|
||||
]
|
||||
)]
|
||||
class ImportContentSourceResource
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotBlank(message: 'Les sources de contenu sont obligatoires')]
|
||||
#[Assert\Type('array', message: 'Les sources de contenu doivent être un tableau')]
|
||||
#[Assert\Count(min: 1, minMessage: 'Au moins une source de contenu doit être fournie')]
|
||||
public readonly array $contentSources = []
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider\ListContentSourceStateProvider;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'ContentSource',
|
||||
operations: [
|
||||
new GetCollection(
|
||||
uriTemplate: '/content-sources',
|
||||
provider: ListContentSourceStateProvider::class,
|
||||
description: 'Récupère la liste des sources de contenu'
|
||||
)
|
||||
]
|
||||
)]
|
||||
class ListContentSourceResource
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use ApiPlatform\Metadata\Put;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\State\Processor\UpsertContentSourceStateProcessor;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'ContentSource',
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/content-sources',
|
||||
processor: UpsertContentSourceStateProcessor::class,
|
||||
input: UpsertContentSourceResource::class,
|
||||
status: 201,
|
||||
description: 'Crée une nouvelle source de contenu'
|
||||
),
|
||||
new Put(
|
||||
uriTemplate: '/content-sources/{id}',
|
||||
processor: UpsertContentSourceStateProcessor::class,
|
||||
input: UpsertContentSourceResource::class,
|
||||
description: 'Met à jour une source de contenu existante'
|
||||
)
|
||||
]
|
||||
)]
|
||||
class UpsertContentSourceResource
|
||||
{
|
||||
public function __construct(
|
||||
public readonly ?int $id = null,
|
||||
|
||||
#[Assert\NotBlank(message: 'L\'URL de base est obligatoire')]
|
||||
#[Assert\Url(message: 'L\'URL de base doit être une URL valide')]
|
||||
public readonly string $baseUrl = '',
|
||||
|
||||
#[Assert\NotBlank(message: 'Le format d\'URL de chapitre est obligatoire')]
|
||||
public readonly string $chapterUrlFormat = '',
|
||||
|
||||
#[Assert\NotBlank(message: 'Le type de scraping est obligatoire')]
|
||||
#[Assert\Choice(choices: ['html', 'javascript'], message: 'Le type de scraping doit être html ou javascript')]
|
||||
public readonly string $scrapingType = '',
|
||||
|
||||
public readonly ?string $imageSelector = null,
|
||||
public readonly ?string $nextPageSelector = null,
|
||||
public readonly ?string $chapterSelector = null,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Setting\Application\Command\ImportContentSourceCommand;
|
||||
use App\Domain\Setting\Application\CommandHandler\ImportContentSourceCommandHandler;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\Resource\ImportContentSourceResource;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use InvalidArgumentException;
|
||||
|
||||
readonly class ImportContentSourceStateProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ImportContentSourceCommandHandler $handler
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): int
|
||||
{
|
||||
assert($data instanceof ImportContentSourceResource);
|
||||
|
||||
try {
|
||||
$command = new ImportContentSourceCommand(
|
||||
contentSources: $data->contentSources
|
||||
);
|
||||
|
||||
$this->handler->handle($command);
|
||||
|
||||
return Response::HTTP_CREATED;
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new BadRequestHttpException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Setting\Application\Command\UpsertContentSourceCommand;
|
||||
use App\Domain\Setting\Application\CommandHandler\UpsertContentSourceCommandHandler;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\Resource\UpsertContentSourceResource;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
readonly class UpsertContentSourceStateProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private UpsertContentSourceCommandHandler $handler
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): int
|
||||
{
|
||||
assert($data instanceof UpsertContentSourceResource);
|
||||
|
||||
$id = $uriVariables['id'] ?? $data->id ?? null;
|
||||
|
||||
$command = new UpsertContentSourceCommand(
|
||||
id: $id,
|
||||
baseUrl: $data->baseUrl,
|
||||
chapterUrlFormat: $data->chapterUrlFormat,
|
||||
scrapingType: $data->scrapingType,
|
||||
imageSelector: $data->imageSelector,
|
||||
nextPageSelector: $data->nextPageSelector,
|
||||
chapterSelector: $data->chapterSelector,
|
||||
);
|
||||
|
||||
$this->handler->handle($command);
|
||||
|
||||
return $id ? Response::HTTP_OK : Response::HTTP_CREATED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Domain\Setting\Application\Query\ExportContentSourceQuery;
|
||||
use App\Domain\Setting\Application\QueryHandler\ExportContentSourceQueryHandler;
|
||||
|
||||
readonly class ExportContentSourceStateProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ExportContentSourceQueryHandler $handler
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
|
||||
{
|
||||
$query = new ExportContentSourceQuery();
|
||||
$response = $this->handler->handle($query);
|
||||
|
||||
return [
|
||||
'contentSources' => $response->contentSources,
|
||||
'exportDate' => $response->exportDate,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Domain\Setting\Application\Query\GetContentSourceQuery;
|
||||
use App\Domain\Setting\Application\QueryHandler\GetContentSourceQueryHandler;
|
||||
use App\Domain\Setting\Domain\Exception\ContentSourceNotFoundException;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\Resource\GetContentSourceResource;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class GetContentSourceStateProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private GetContentSourceQueryHandler $handler
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?GetContentSourceResource
|
||||
{
|
||||
try {
|
||||
$query = new GetContentSourceQuery($uriVariables['id']);
|
||||
$response = $this->handler->handle($query);
|
||||
|
||||
return new GetContentSourceResource(
|
||||
id: $response->id,
|
||||
baseUrl: $response->baseUrl,
|
||||
chapterUrlFormat: $response->chapterUrlFormat,
|
||||
scrapingType: $response->scrapingType,
|
||||
imageSelector: $response->imageSelector,
|
||||
nextPageSelector: $response->nextPageSelector,
|
||||
chapterSelector: $response->chapterSelector,
|
||||
cleanBaseUrl: $response->cleanBaseUrl,
|
||||
);
|
||||
} catch (ContentSourceNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\ApiPlatform\State\Provider;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Domain\Setting\Application\Query\ListContentSourceQuery;
|
||||
use App\Domain\Setting\Application\QueryHandler\ListContentSourceQueryHandler;
|
||||
use App\Domain\Setting\Infrastructure\ApiPlatform\Resource\GetContentSourceResource;
|
||||
|
||||
readonly class ListContentSourceStateProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ListContentSourceQueryHandler $handler
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
|
||||
{
|
||||
$query = new ListContentSourceQuery();
|
||||
$response = $this->handler->handle($query);
|
||||
|
||||
return array_map(
|
||||
fn($contentSourceResponse) => new GetContentSourceResource(
|
||||
id: $contentSourceResponse->id,
|
||||
baseUrl: $contentSourceResponse->baseUrl,
|
||||
chapterUrlFormat: $contentSourceResponse->chapterUrlFormat,
|
||||
scrapingType: $contentSourceResponse->scrapingType,
|
||||
imageSelector: $contentSourceResponse->imageSelector,
|
||||
nextPageSelector: $contentSourceResponse->nextPageSelector,
|
||||
chapterSelector: $contentSourceResponse->chapterSelector,
|
||||
cleanBaseUrl: $contentSourceResponse->cleanBaseUrl,
|
||||
),
|
||||
$response->contentSources
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\Persistence\Mapper;
|
||||
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
use App\Entity\ContentSource as ContentSourceEntity;
|
||||
|
||||
readonly class ContentSourceMapper
|
||||
{
|
||||
public function toDomain(ContentSourceEntity $entity): ContentSource
|
||||
{
|
||||
return new ContentSource(
|
||||
id: $entity->getId(),
|
||||
baseUrl: $entity->getBaseUrl(),
|
||||
chapterUrlFormat: $entity->getChapterUrlFormat(),
|
||||
scrapingType: $entity->getScrapingType(),
|
||||
imageSelector: $entity->getImageSelector(),
|
||||
nextPageSelector: $entity->getNextPageSelector(),
|
||||
chapterSelector: $entity->getChapterSelector(),
|
||||
);
|
||||
}
|
||||
|
||||
public function toEntity(ContentSource $contentSource): ContentSourceEntity
|
||||
{
|
||||
$entity = new ContentSourceEntity();
|
||||
|
||||
$entity->setBaseUrl($contentSource->getBaseUrl())
|
||||
->setChapterUrlFormat($contentSource->getChapterUrlFormat())
|
||||
->setScrapingType($contentSource->getScrapingType())
|
||||
->setImageSelector($contentSource->getImageSelector())
|
||||
->setNextPageSelector($contentSource->getNextPageSelector())
|
||||
->setChapterSelector($contentSource->getChapterSelector());
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Setting\Infrastructure\Persistence\Repository;
|
||||
|
||||
use App\Domain\Setting\Domain\Contract\Repository\ContentSourceRepositoryInterface;
|
||||
use App\Domain\Setting\Domain\Model\ContentSource;
|
||||
use App\Domain\Setting\Infrastructure\Persistence\Mapper\ContentSourceMapper;
|
||||
use App\Entity\ContentSource as ContentSourceEntity;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
readonly class DoctrineContentSourceRepository implements ContentSourceRepositoryInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $entityManager,
|
||||
private ContentSourceMapper $mapper
|
||||
) {}
|
||||
|
||||
public function findAll(): array
|
||||
{
|
||||
$entities = $this->entityManager->getRepository(ContentSourceEntity::class)->findAll();
|
||||
|
||||
return array_map(
|
||||
fn(ContentSourceEntity $entity) => $this->mapper->toDomain($entity),
|
||||
$entities
|
||||
);
|
||||
}
|
||||
|
||||
public function findById(int $id): ?ContentSource
|
||||
{
|
||||
$entity = $this->entityManager->find(ContentSourceEntity::class, $id);
|
||||
|
||||
return $entity ? $this->mapper->toDomain($entity) : null;
|
||||
}
|
||||
|
||||
public function save(ContentSource $contentSource): void
|
||||
{
|
||||
$entity = $this->mapper->toEntity($contentSource);
|
||||
|
||||
$this->entityManager->persist($entity);
|
||||
$this->entityManager->flush();
|
||||
|
||||
// Met à jour l'ID du modèle du domaine si nécessaire
|
||||
if ($entity->getId() && $contentSource->getId() === null) {
|
||||
$contentSource->updateId($entity->getId());
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(ContentSource $contentSource): void
|
||||
{
|
||||
if ($contentSource->getId()) {
|
||||
$entity = $this->entityManager->find(ContentSourceEntity::class, $contentSource->getId());
|
||||
if ($entity) {
|
||||
$this->entityManager->remove($entity);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteAll(): void
|
||||
{
|
||||
$this->entityManager->createQuery('DELETE FROM ' . ContentSourceEntity::class)->execute();
|
||||
}
|
||||
|
||||
public function saveMultiple(array $contentSources): void
|
||||
{
|
||||
foreach ($contentSources as $contentSource) {
|
||||
$entity = $this->mapper->toEntity($contentSource);
|
||||
$this->entityManager->persist($entity);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user