bulk upload
This commit is contained in:
parent
7c6bd31a6c
commit
3f97413c4e
@ -27,6 +27,7 @@
|
||||
"MyTube\\API\\Console\\": "src\/ApiDomain\/Console\/src",
|
||||
"MyTube\\API\\External\\Authentication\\": "src\/ApiDomain\/External\/Authentication\/src",
|
||||
"MyTube\\API\\External\\Health\\": "src\/ApiDomain\/External\/Health\/src",
|
||||
"MyTube\\API\\External\\Tag\\": "src\/ApiDomain\/External\/Tag\/src",
|
||||
"MyTube\\API\\External\\User\\": "src\/ApiDomain\/External\/User\/src",
|
||||
"MyTube\\API\\External\\Video\\": "src\/ApiDomain\/External\/Video\/src",
|
||||
"MyTube\\API\\External\\VideoList\\": "src\/ApiDomain\/External\/VideoList\/src",
|
||||
@ -34,6 +35,7 @@
|
||||
"MyTube\\Data\\Log\\": "src\/DataDomain\/Log\/src",
|
||||
"MyTube\\Handling\\Registration\\": "src\/HandlingDomain\/Registration\/src",
|
||||
"MyTube\\Handling\\Role\\": "src\/HandlingDomain\/Role\/src",
|
||||
"MyTube\\Handling\\Tag\\": "src\/HandlingDomain\/Tag\/src",
|
||||
"MyTube\\Handling\\User\\": "src\/HandlingDomain\/User\/src",
|
||||
"MyTube\\Handling\\UserSession\\": "src\/HandlingDomain\/UserSession\/src",
|
||||
"MyTube\\Handling\\Video\\": "src\/HandlingDomain\/Video\/src",
|
||||
@ -95,6 +97,7 @@
|
||||
"psr-4": {
|
||||
"MyTube\\API\\External\\Authentication\\": "src\/ApiDomain\/External\/Authentication\/src",
|
||||
"MyTube\\API\\External\\Health\\": "src\/ApiDomain\/External\/Health\/src",
|
||||
"MyTube\\API\\External\\Tag\\": "src\/ApiDomain\/External\/Tag\/src",
|
||||
"MyTube\\API\\External\\User\\": "src\/ApiDomain\/External\/User\/src",
|
||||
"MyTube\\API\\External\\Video\\": "src\/ApiDomain\/External\/Video\/src",
|
||||
"MyTube\\API\\External\\VideoList\\": "src\/ApiDomain\/External\/VideoList\/src",
|
||||
@ -102,6 +105,7 @@
|
||||
"MyTube\\Data\\Log\\": "src\/DataDomain\/Log\/src",
|
||||
"MyTube\\Handling\\Registration\\": "src\/HandlingDomain\/Registration\/src",
|
||||
"MyTube\\Handling\\Role\\": "src\/HandlingDomain\/Role\/src",
|
||||
"MyTube\\Handling\\Tag\\": "src\/HandlingDomain\/Tag\/src",
|
||||
"MyTube\\Handling\\User\\": "src\/HandlingDomain\/User\/src",
|
||||
"MyTube\\Handling\\UserSession\\": "src\/HandlingDomain\/UserSession\/src",
|
||||
"MyTube\\Handling\\Video\\": "src\/HandlingDomain\/Video\/src",
|
||||
|
||||
@ -59,6 +59,7 @@ $aggregator = new ConfigAggregator([
|
||||
\MyTube\Handling\Registration\ConfigProvider::class,
|
||||
\MyTube\Handling\Video\ConfigProvider::class,
|
||||
\MyTube\Handling\VideoList\ConfigProvider::class,
|
||||
\MyTube\Handling\Tag\ConfigProvider::class,
|
||||
|
||||
// API
|
||||
/// Command
|
||||
@ -70,6 +71,7 @@ $aggregator = new ConfigAggregator([
|
||||
\MyTube\API\External\Authentication\ConfigProvider::class,
|
||||
\MyTube\API\External\Video\ConfigProvider::class,
|
||||
\MyTube\API\External\VideoList\ConfigProvider::class,
|
||||
\MyTube\API\External\Tag\ConfigProvider::class,
|
||||
|
||||
/// Internal
|
||||
|
||||
|
||||
@ -14,18 +14,24 @@ final class Version20240223130626 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
return "Create Table 'tag'";
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$sql = "CREATE TABLE tag (
|
||||
id binary(16) NOT NULL,
|
||||
description 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 down() migration is auto-generated, please modify it to your needs
|
||||
|
||||
$this->addSql("DROP TABLE tag;");
|
||||
}
|
||||
}
|
||||
|
||||
38
data/migrations/myTube/Version20240223130835.php
Normal file
38
data/migrations/myTube/Version20240223130835.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Migrations\MyTube;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240223130835 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return "Create Table 'tag_alias'";
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$sql = "CREATE TABLE tag_alias (
|
||||
id binary(16) NOT NULL,
|
||||
tag_id binary(16) NOT NULL,
|
||||
description 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 tag_alias;");
|
||||
}
|
||||
}
|
||||
35
data/migrations/myTube/Version20240223142111.php
Normal file
35
data/migrations/myTube/Version20240223142111.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Migrations\MyTube;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240223142111 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return "Create Table 'video_tag'";
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$sql = "CREATE TABLE video_tag (
|
||||
video_id binary(16) NOT NULL,
|
||||
tag_id binary(16) NOT NULL,
|
||||
PRIMARY KEY (video_id, tag_id)
|
||||
);";
|
||||
|
||||
$this->addSql($sql);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql("DROP TABLE video_tag;");
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ upstream host-backend-app {
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
client_max_body_size 1000M;
|
||||
client_max_body_size 10000M;
|
||||
|
||||
location / {
|
||||
fastcgi_pass host-backend-app;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
file_uploads = On
|
||||
memory_limit = 1000M
|
||||
upload_max_filesize = 1000M
|
||||
post_max_size = 1000M
|
||||
max_execution_time = 600
|
||||
memory_limit = 10000M
|
||||
upload_max_filesize = 10000M
|
||||
post_max_size = 10000M
|
||||
max_execution_time = 1000
|
||||
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\Console\Command\AnalyzeVideoTitlesCommand;
|
||||
use MyTube\API\Console\Command\InitializeDataCommand;
|
||||
use MyTube\API\Console\Command\RbacUpdateCommand;
|
||||
|
||||
@ -7,5 +8,6 @@ return [
|
||||
'commands' => [
|
||||
InitializeDataCommand::class,
|
||||
RbacUpdateCommand::class,
|
||||
AnalyzeVideoTitlesCommand::class,
|
||||
]
|
||||
];
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\Console\Command\AnalyzeVideoTitlesCommand;
|
||||
use MyTube\API\Console\Command\InitializeDataCommand;
|
||||
use MyTube\API\Console\Command\RbacUpdateCommand;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
@ -8,5 +9,6 @@ return [
|
||||
'factories' => [
|
||||
InitializeDataCommand::class => AutoWiringFactory::class,
|
||||
RbacUpdateCommand::class => AutoWiringFactory::class,
|
||||
AnalyzeVideoTitlesCommand::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\API\Console\Command;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use MyTube\Data\Business\Manager\MyTubeEntityManager;
|
||||
use MyTube\Data\Business\Repository\VideoRepository;
|
||||
use MyTube\Handling\Video\Analyzer\VideoTitleAnalyzer;
|
||||
use MyTube\Infrastructure\Logging\Logger\Logger;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(name: 'analyze:video-title', description: 'Analyzes video titles and add tags')]
|
||||
class AnalyzeVideoTitlesCommand extends Command
|
||||
{
|
||||
private readonly VideoRepository $videoRepository;
|
||||
|
||||
public function __construct(
|
||||
private readonly MyTubeEntityManager $entityManager,
|
||||
private readonly VideoTitleAnalyzer $analyzer,
|
||||
private readonly Logger $logger,
|
||||
) {
|
||||
parent::__construct($this->getName());
|
||||
|
||||
$this->videoRepository = $this->entityManager->getRepository(Video::class);
|
||||
}
|
||||
|
||||
protected function execute(
|
||||
InputInterface $input,
|
||||
OutputInterface $output
|
||||
): int {
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
try {
|
||||
$videos = $this->videoRepository->findAll();
|
||||
|
||||
/** @var Video $video */
|
||||
foreach ($videos as $video) {
|
||||
$io->info($video->getTitle());
|
||||
|
||||
$matches = $this->analyzer->analyze($video);
|
||||
|
||||
/** @var Tag $match */
|
||||
foreach ($matches as $match) {
|
||||
$io->info(sprintf(" - added Tag '%s'", $match->getDescription()));
|
||||
}
|
||||
|
||||
$this->entityManager->persist($video);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
$io->success('OK!');
|
||||
} catch (\Throwable $e) {
|
||||
$io->error($e->getMessage());
|
||||
$io->error($e->getTraceAsString());
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
23
src/ApiDomain/External/Tag/config/routes.php
vendored
Normal file
23
src/ApiDomain/External/Tag/config/routes.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\External\Tag\Handler\AddAliasHandler;
|
||||
use MyTube\API\External\Tag\Handler\CreateHandler;
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => 'tag.create',
|
||||
'path' => '/api/tag/create[/]',
|
||||
'allowed_methods' => ['POST'],
|
||||
'middleware' => [
|
||||
CreateHandler::class,
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'tag.add-alias',
|
||||
'path' => '/api/tag/add-alias[/]',
|
||||
'allowed_methods' => ['POST'],
|
||||
'middleware' => [
|
||||
AddAliasHandler::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
19
src/ApiDomain/External/Tag/config/service_manager.php
vendored
Normal file
19
src/ApiDomain/External/Tag/config/service_manager.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\External\Tag\Handler\AddAliasHandler;
|
||||
use MyTube\API\External\Tag\Handler\CreateHandler;
|
||||
use MyTube\API\External\Tag\ResponseFormatter\AddAliasResponseFormatter;
|
||||
use MyTube\API\External\Tag\ResponseFormatter\CreateResponseFormatter;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
|
||||
return [
|
||||
'factories' => [
|
||||
// Handler
|
||||
AddAliasHandler::class => AutoWiringFactory::class,
|
||||
CreateHandler::class => AutoWiringFactory::class,
|
||||
|
||||
// Response Formatter
|
||||
AddAliasResponseFormatter::class => AutoWiringFactory::class,
|
||||
CreateResponseFormatter::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
16
src/ApiDomain/External/Tag/src/ConfigProvider.php
vendored
Normal file
16
src/ApiDomain/External/Tag/src/ConfigProvider.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\Tag;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'dependencies' => require __DIR__ . './../config/service_manager.php',
|
||||
'routes' => require __DIR__ . '/./../config/routes.php',
|
||||
];
|
||||
}
|
||||
}
|
||||
46
src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php
vendored
Normal file
46
src/ApiDomain/External/Tag/src/Handler/AddAliasHandler.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\Tag\Handler;
|
||||
|
||||
use MyTube\Handling\Tag\Exception\TagAliasAlreadyExistsException;
|
||||
use MyTube\Handling\Tag\Exception\TagNotFoundByIdException;
|
||||
use MyTube\Handling\Tag\Handler\Command\AddAlias\AddAliasCommandHandler;
|
||||
use MyTube\Handling\Tag\Handler\Command\AddAlias\AddAliasCommandBuilder;
|
||||
use MyTube\API\External\Tag\ResponseFormatter\AddAliasResponseFormatter;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use MyTube\Infrastructure\Response\SuccessResponse;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class AddAliasHandler implements RequestHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AddAliasCommandHandler $addAliasCommandHandler,
|
||||
private readonly AddAliasCommandBuilder $addAliasCommandBuilder,
|
||||
private readonly AddAliasResponseFormatter $responseFormatter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TagNotFoundByIdException
|
||||
* @throws TagAliasAlreadyExistsException
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$data = json_decode(
|
||||
$request->getBody()->getContents(),
|
||||
true
|
||||
);
|
||||
|
||||
$addAliasCommand = $this->addAliasCommandBuilder->build(
|
||||
Uuid::fromString($data['tagId']),
|
||||
$data['description']
|
||||
);
|
||||
$result = $this->addAliasCommandHandler->execute($addAliasCommand);
|
||||
|
||||
return new SuccessResponse($this->responseFormatter->format($result));
|
||||
}
|
||||
}
|
||||
42
src/ApiDomain/External/Tag/src/Handler/CreateHandler.php
vendored
Normal file
42
src/ApiDomain/External/Tag/src/Handler/CreateHandler.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\Tag\Handler;
|
||||
|
||||
use MyTube\Handling\Tag\Exception\TagAlreadyExistsException;
|
||||
use MyTube\Handling\Tag\Handler\Command\Create\CreateCommandHandler;
|
||||
use MyTube\Handling\Tag\Handler\Command\Create\CreateCommandBuilder;
|
||||
use MyTube\API\External\Tag\ResponseFormatter\CreateResponseFormatter;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use MyTube\Infrastructure\Response\SuccessResponse;
|
||||
|
||||
class CreateHandler implements RequestHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CreateCommandHandler $createCommandHandler,
|
||||
private readonly CreateCommandBuilder $createCommandBuilder,
|
||||
private readonly CreateResponseFormatter $responseFormatter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TagAlreadyExistsException
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$data = json_decode(
|
||||
$request->getBody()->getContents(),
|
||||
true
|
||||
);
|
||||
|
||||
$createCommand = $this->createCommandBuilder->build(
|
||||
$data['description']
|
||||
);
|
||||
$result = $this->createCommandHandler->execute($createCommand);
|
||||
|
||||
return new SuccessResponse($this->responseFormatter->format($result));
|
||||
}
|
||||
}
|
||||
25
src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php
vendored
Normal file
25
src/ApiDomain/External/Tag/src/ResponseFormatter/AddAliasResponseFormatter.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\Tag\ResponseFormatter;
|
||||
|
||||
use MyTube\Handling\Tag\Handler\Command\AddAlias\AddAliasCommandResult;
|
||||
|
||||
class AddAliasResponseFormatter
|
||||
{
|
||||
public function format(AddAliasCommandResult $addAliasCommandResult): array
|
||||
{
|
||||
$alias = $addAliasCommandResult->getAlias();
|
||||
$tag = $alias->getTag();
|
||||
|
||||
return [
|
||||
'id' => $alias->getId()->toString(),
|
||||
'description' => $alias->getDescription(),
|
||||
'tag' => [
|
||||
'id' => $tag->getId()->toString(),
|
||||
'description' => $tag->getDescription()
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
19
src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php
vendored
Normal file
19
src/ApiDomain/External/Tag/src/ResponseFormatter/CreateResponseFormatter.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\Tag\ResponseFormatter;
|
||||
|
||||
use MyTube\Handling\Tag\Handler\Command\Create\CreateCommandResult;
|
||||
|
||||
class CreateResponseFormatter
|
||||
{
|
||||
public function format(CreateCommandResult $createCommandResult): array
|
||||
{
|
||||
$tag = $createCommandResult->getTag();
|
||||
return [
|
||||
'id' => $tag->getId()->toString(),
|
||||
'description' => $tag->getDescription(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\External\VideoList\Handler\ReadListHandler;
|
||||
use MyTube\API\External\VideoList\Handler\UploadHandler;
|
||||
|
||||
return [
|
||||
[
|
||||
@ -11,4 +12,12 @@ return [
|
||||
ReadListHandler::class
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'video-list.upload',
|
||||
'path' => '/api/video-list/upload[/]',
|
||||
'allowed_methods' => ['POST'],
|
||||
'middleware' => [
|
||||
UploadHandler::class
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
<?php
|
||||
|
||||
use MyTube\API\External\Video\Handler\StreamHandler;
|
||||
use MyTube\API\External\Video\Handler\UploadHandler;
|
||||
use MyTube\API\External\VideoList\Handler\UploadHandler;
|
||||
use MyTube\API\External\VideoList\Handler\ReadListHandler;
|
||||
use MyTube\API\External\VideoList\ResponseFormatter\ReadListResponseFormatter;
|
||||
use MyTube\API\External\VideoList\ResponseFormatter\UploadResponseFormatter;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
|
||||
return [
|
||||
'factories' => [
|
||||
ReadListHandler::class => AutoWiringFactory::class,
|
||||
UploadHandler::class => AutoWiringFactory::class,
|
||||
|
||||
ReadListResponseFormatter::class => AutoWiringFactory::class,
|
||||
UploadResponseFormatter::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
|
||||
41
src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php
vendored
Normal file
41
src/ApiDomain/External/VideoList/src/Handler/UploadHandler.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\VideoList\Handler;
|
||||
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Laminas\Http\Response;
|
||||
use MyTube\Handling\VideoList\Handler\Command\Upload\UploadCommandHandler;
|
||||
use MyTube\Handling\VideoList\Handler\Command\Upload\UploadCommandBuilder;
|
||||
use MyTube\API\External\VideoList\ResponseFormatter\UploadResponseFormatter;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use MyTube\Infrastructure\Response\SuccessResponse;
|
||||
|
||||
class UploadHandler implements RequestHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UploadCommandHandler $uploadCommandHandler,
|
||||
private readonly UploadCommandBuilder $uploadCommandBuilder,
|
||||
private readonly UploadResponseFormatter $responseFormatter,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$uploadedFiles = $request->getUploadedFiles();
|
||||
|
||||
if (count($uploadedFiles) < 1) {
|
||||
return new JsonResponse('Upload at least one File', Response::STATUS_CODE_400);
|
||||
}
|
||||
|
||||
$uploadCommand = $this->uploadCommandBuilder->build(
|
||||
$request->getUploadedFiles()
|
||||
);
|
||||
$result = $this->uploadCommandHandler->execute($uploadCommand);
|
||||
|
||||
return new SuccessResponse($this->responseFormatter->format($result));
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\VideoList\ResponseFormatter;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
|
||||
class ReadListResponseFormatter
|
||||
@ -14,9 +15,17 @@ class ReadListResponseFormatter
|
||||
|
||||
/** @var Video $video */
|
||||
foreach ($videos as $video) {
|
||||
$tags = [];
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($video->getTags() as $tag) {
|
||||
$tags[] = $tag->getDescription();
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'title' => $video->getTitle(),
|
||||
'id' => $video->getId()->toString(),
|
||||
'tags' => $tags
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
46
src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php
vendored
Normal file
46
src/ApiDomain/External/VideoList/src/ResponseFormatter/UploadResponseFormatter.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\API\External\VideoList\ResponseFormatter;
|
||||
|
||||
use MyTube\Handling\VideoList\Handler\Command\Upload\UploadCommandResult;
|
||||
use MyTube\Handling\VideoList\Model\UploadedFileResult;
|
||||
|
||||
class UploadResponseFormatter
|
||||
{
|
||||
public function format(UploadCommandResult $uploadCommandResult): array
|
||||
{
|
||||
$uploadedFileResults = $uploadCommandResult->getUploadedFileResults();
|
||||
$details = [];
|
||||
|
||||
$successCount = 0;
|
||||
$failCount = 0;
|
||||
|
||||
/** @var UploadedFileResult $uploadedFileResult */
|
||||
foreach ($uploadedFileResults as $uploadedFileResult) {
|
||||
|
||||
$detail = [
|
||||
'file' => $uploadedFileResult->getUploadedFile()->getClientFilename(),
|
||||
'success' => $uploadedFileResult->getSuccess(),
|
||||
];
|
||||
|
||||
if ($uploadedFileResult->getSuccess()) {
|
||||
$detail['id'] = $uploadedFileResult->getVideo()->getId()->toString();
|
||||
$successCount++;
|
||||
} else {
|
||||
$detail['error'] = $uploadedFileResult->getException()->getMessage();
|
||||
$failCount++;
|
||||
}
|
||||
|
||||
$details[] = $detail;
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => count($uploadedFileResults),
|
||||
'failed' => $failCount,
|
||||
'succeeded' => $successCount,
|
||||
'details' => $details
|
||||
];
|
||||
}
|
||||
}
|
||||
119
src/DataDomain/Business/src/Entity/Tag.php
Normal file
119
src/DataDomain/Business/src/Entity/Tag.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Data\Business\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use MyTube\Infrastructure\UuidGenerator\UuidGenerator;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="MyTube\Data\Business\Repository\TagRepository")
|
||||
* @ORM\Table(name="tag")
|
||||
*/
|
||||
class Tag {
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="uuid_binary_ordered_time")
|
||||
*/
|
||||
private UuidInterface $id;
|
||||
|
||||
/** @ORM\Column(name="description", type="string") */
|
||||
private string $description;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Video", mappedBy="tags")
|
||||
* @ORM\JoinTable(name="video_tag")
|
||||
*/
|
||||
private Collection $videos;
|
||||
|
||||
/** @ORM\OneToMany(targetEntity="TagAlias", mappedBy="tag") */
|
||||
private Collection $aliases;
|
||||
|
||||
/** @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();
|
||||
|
||||
$this->aliases = new ArrayCollection();
|
||||
$this->videos = new ArrayCollection();
|
||||
|
||||
$now = new DateTime();
|
||||
$this->setCreatedAt($now);
|
||||
$this->setUpdatedAt($now);
|
||||
}
|
||||
|
||||
public function getId(): UuidInterface {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getDescription(): string {
|
||||
return $this->description;
|
||||
}
|
||||
public function setDescription(string $description): void {
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getVideos(): Collection
|
||||
{
|
||||
return $this->videos;
|
||||
}
|
||||
public function setVideos(Collection $videos): void
|
||||
{
|
||||
$this->videos = $videos;
|
||||
}
|
||||
public function addVideo(Video $video): void
|
||||
{
|
||||
if (!$this->videos->contains($video)) {
|
||||
$this->videos->add($video);
|
||||
}
|
||||
}
|
||||
public function removeVideo(Video $video): void
|
||||
{
|
||||
if ($this->videos->contains($video)) {
|
||||
$this->videos->removeElement($video);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAliases(): Collection
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
public function setAliases(Collection $aliases): void
|
||||
{
|
||||
$this->aliases = $aliases;
|
||||
}
|
||||
public function addAlias(TagAlias $alias): void
|
||||
{
|
||||
if (!$this->aliases->contains($alias)) {
|
||||
$this->aliases->add($alias);
|
||||
}
|
||||
}
|
||||
public function removeAlias(TagAlias $alias): void
|
||||
{
|
||||
if ($this->aliases->contains($alias)) {
|
||||
$this->aliases->removeElement($alias);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
90
src/DataDomain/Business/src/Entity/TagAlias.php
Normal file
90
src/DataDomain/Business/src/Entity/TagAlias.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Data\Business\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use MyTube\Infrastructure\UuidGenerator\UuidGenerator;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="MyTube\Data\Business\Repository\TagAliasRepository")
|
||||
* @ORM\Table(name="tag_alias")
|
||||
*/
|
||||
class TagAlias {
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id", type="uuid_binary_ordered_time")
|
||||
*/
|
||||
private UuidInterface $id;
|
||||
|
||||
/** @ORM\Column(name="tag_id", type="uuid_binary_ordered_time") */
|
||||
private UuidInterface $tagId;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Tag", inversedBy="aliases")
|
||||
* @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
|
||||
*/
|
||||
private Tag $tag;
|
||||
|
||||
/** @ORM\Column(name="description", type="string") */
|
||||
private string $description;
|
||||
|
||||
/** @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);
|
||||
}
|
||||
|
||||
public function getId(): UuidInterface {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTagId(): UuidInterface
|
||||
{
|
||||
return $this->tagId;
|
||||
}
|
||||
public function setTagId(UuidInterface $tagId): void
|
||||
{
|
||||
$this->tagId = $tagId;
|
||||
}
|
||||
|
||||
public function getTag(): Tag
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
public function setTag(Tag $tag): void
|
||||
{
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
public function getDescription(): string {
|
||||
return $this->description;
|
||||
}
|
||||
public function setDescription(string $description): void {
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,8 @@
|
||||
namespace MyTube\Data\Business\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use MyTube\Infrastructure\UuidGenerator\UuidGenerator;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
@ -24,6 +26,12 @@ class Video {
|
||||
/** @ORM\Column(name="directory_path", type="string") */
|
||||
private ?string $directoryPath;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="videos")
|
||||
* @ORM\JoinTable(name="video_tag")
|
||||
*/
|
||||
private Collection $tags;
|
||||
|
||||
/** @ORM\Column(name="created_at", type="datetime") */
|
||||
private DateTime $createdAt;
|
||||
|
||||
@ -34,6 +42,8 @@ class Video {
|
||||
public function __construct() {
|
||||
$this->id = UuidGenerator::generate();
|
||||
|
||||
$this->tags = new ArrayCollection();
|
||||
|
||||
$now = new DateTime();
|
||||
$this->setCreatedAt($now);
|
||||
$this->setUpdatedAt($now);
|
||||
@ -47,7 +57,6 @@ class Video {
|
||||
public function getTitle(): string {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void {
|
||||
$this->title = $title;
|
||||
}
|
||||
@ -55,15 +64,34 @@ class Video {
|
||||
public function getDirectoryPath(): ?string {
|
||||
return $this->directoryPath;
|
||||
}
|
||||
|
||||
public function setDirectoryPath(?string $directoryPath): void {
|
||||
$this->directoryPath = $directoryPath;
|
||||
}
|
||||
|
||||
public function getTags(): Collection
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
public function setTags(Collection $tags): void
|
||||
{
|
||||
$this->tags = $tags;
|
||||
}
|
||||
public function addTag(Tag $tag): void
|
||||
{
|
||||
if (!$this->tags->contains($tag)) {
|
||||
$this->tags->add($tag);
|
||||
}
|
||||
}
|
||||
public function removeTag(Tag $tag): void
|
||||
{
|
||||
if ($this->tags->contains($tag)) {
|
||||
$this->tags->removeElement($tag);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTime {
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTime $createdAt): void {
|
||||
$this->createdAt = $createdAt;
|
||||
}
|
||||
@ -71,7 +99,6 @@ class Video {
|
||||
public function getUpdatedAt(): DateTime {
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public function setUpdatedAt(DateTime $updatedAt): void {
|
||||
$this->updatedAt = $updatedAt;
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Data\Business\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class TagAliasRepository extends EntityRepository {
|
||||
}
|
||||
8
src/DataDomain/Business/src/Repository/TagRepository.php
Normal file
8
src/DataDomain/Business/src/Repository/TagRepository.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Data\Business\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class TagRepository extends EntityRepository {
|
||||
}
|
||||
34
src/HandlingDomain/Tag/config/service_manager.php
Normal file
34
src/HandlingDomain/Tag/config/service_manager.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use MyTube\Handling\Tag\Builder\TagAliasBuilder;
|
||||
use MyTube\Handling\Tag\Builder\TagBuilder;
|
||||
use MyTube\Handling\Tag\Handler\Command\AddAlias\AddAliasCommandBuilder;
|
||||
use MyTube\Handling\Tag\Handler\Command\AddAlias\AddAliasCommandHandler;
|
||||
use MyTube\Handling\Tag\Handler\Command\Create\CreateCommandBuilder;
|
||||
use MyTube\Handling\Tag\Handler\Command\Create\CreateCommandHandler;
|
||||
use MyTube\Handling\Tag\Rule\TagAliasExistsRule;
|
||||
use MyTube\Handling\Tag\Rule\TagExistsRule;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
use Reinfi\DependencyInjection\Factory\InjectionFactory;
|
||||
|
||||
|
||||
return [
|
||||
'factories' => [
|
||||
|
||||
/// Rule
|
||||
TagExistsRule::class => InjectionFactory::class,
|
||||
TagAliasExistsRule::class => InjectionFactory::class,
|
||||
|
||||
/// Builder
|
||||
TagBuilder::class => AutoWiringFactory::class,
|
||||
TagAliasBuilder::class => AutoWiringFactory::class,
|
||||
|
||||
/// CQRS
|
||||
// Add Alias
|
||||
AddAliasCommandBuilder::class => AutoWiringFactory::class,
|
||||
AddAliasCommandHandler::class => InjectionFactory::class,
|
||||
// Create
|
||||
CreateCommandBuilder::class => AutoWiringFactory::class,
|
||||
CreateCommandHandler::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
21
src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php
Normal file
21
src/HandlingDomain/Tag/src/Builder/TagAliasBuilder.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Builder;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
use MyTube\Data\Business\Entity\TagAlias;
|
||||
|
||||
class TagAliasBuilder
|
||||
{
|
||||
public function build(
|
||||
Tag $tag,
|
||||
string $description,
|
||||
): TagAlias
|
||||
{
|
||||
$tagAlias = new TagAlias();
|
||||
$tagAlias->setTag($tag);
|
||||
$tagAlias->setDescription($description);
|
||||
|
||||
return $tagAlias;
|
||||
}
|
||||
}
|
||||
18
src/HandlingDomain/Tag/src/Builder/TagBuilder.php
Normal file
18
src/HandlingDomain/Tag/src/Builder/TagBuilder.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Builder;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
|
||||
class TagBuilder
|
||||
{
|
||||
public function build(
|
||||
string $description,
|
||||
): Tag
|
||||
{
|
||||
$tag = new Tag();
|
||||
$tag->setDescription($description);
|
||||
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
15
src/HandlingDomain/Tag/src/ConfigProvider.php
Normal file
15
src/HandlingDomain/Tag/src/ConfigProvider.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'dependencies' => require __DIR__ . '/./../config/service_manager.php',
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Exception;
|
||||
|
||||
use MyTube\Infrastructure\Exception\ErrorCode;
|
||||
use MyTube\Infrastructure\Exception\ErrorDomain;
|
||||
use MyTube\Infrastructure\Exception\Exception\MyTubeException;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class TagAliasAlreadyExistsException extends MyTubeException {
|
||||
|
||||
private const MESSAGE = "The TagAlias '%s' already exists!";
|
||||
|
||||
public function __construct(string $description)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
self::MESSAGE,
|
||||
$description
|
||||
),
|
||||
ErrorDomain::TagAlias,
|
||||
ErrorCode::AlreadyExists
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Exception;
|
||||
|
||||
use MyTube\Infrastructure\Exception\ErrorCode;
|
||||
use MyTube\Infrastructure\Exception\ErrorDomain;
|
||||
use MyTube\Infrastructure\Exception\Exception\MyTubeException;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class TagAlreadyExistsException extends MyTubeException {
|
||||
|
||||
private const MESSAGE = "The Tag '%s' already exists!";
|
||||
|
||||
public function __construct(string $description)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
self::MESSAGE,
|
||||
$description
|
||||
),
|
||||
ErrorDomain::Tag,
|
||||
ErrorCode::AlreadyExists
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Exception;
|
||||
|
||||
use MyTube\Infrastructure\Exception\ErrorCode;
|
||||
use MyTube\Infrastructure\Exception\ErrorDomain;
|
||||
use MyTube\Infrastructure\Exception\Exception\MyTubeException;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class TagNotFoundByIdException extends MyTubeException {
|
||||
|
||||
private const MESSAGE = 'The Tag with the Id %s was not found!';
|
||||
|
||||
public function __construct(UuidInterface $id)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
self::MESSAGE,
|
||||
$id->toString()
|
||||
),
|
||||
ErrorDomain::Tag,
|
||||
ErrorCode::NotFound
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\AddAlias;
|
||||
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class AddAliasCommand
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UuidInterface $tagId,
|
||||
private readonly string $description,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getTagId(): UuidInterface
|
||||
{
|
||||
return $this->tagId;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\AddAlias;
|
||||
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class AddAliasCommandBuilder
|
||||
{
|
||||
public function build(
|
||||
UuidInterface $tagId,
|
||||
string $description,
|
||||
): AddAliasCommand {
|
||||
return new AddAliasCommand(
|
||||
$tagId,
|
||||
$description
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\AddAlias;
|
||||
|
||||
use MyTube\Data\Business\Manager\MyTubeEntityManager;
|
||||
use MyTube\Data\Business\Repository\TagRepository;
|
||||
use MyTube\Handling\Tag\Builder\TagAliasBuilder;
|
||||
use MyTube\Handling\Tag\Exception\TagAliasAlreadyExistsException;
|
||||
use MyTube\Handling\Tag\Exception\TagNotFoundByIdException;
|
||||
use MyTube\Handling\Tag\Rule\TagAliasExistsRule;
|
||||
use Reinfi\DependencyInjection\Annotation\Inject;
|
||||
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
|
||||
|
||||
class AddAliasCommandHandler
|
||||
{
|
||||
/**
|
||||
* @Inject("MyTube\Handling\Tag\Rule\TagAliasExistsRule")
|
||||
* @InjectDoctrineRepository(
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\Tag"
|
||||
* )
|
||||
* @Inject("MyTube\Handling\Tag\Builder\TagAliasBuilder")
|
||||
* @Inject("MyTube\Data\Business\Manager\MyTubeEntityManager")
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly TagAliasExistsRule $existsRule,
|
||||
private readonly TagRepository $tagRepository,
|
||||
private readonly TagAliasBuilder $aliasBuilder,
|
||||
private readonly MyTubeEntityManager $entityManager,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TagAliasAlreadyExistsException
|
||||
* @throws TagNotFoundByIdException
|
||||
*/
|
||||
public function execute(AddAliasCommand $addAliasCommand): AddAliasCommandResult
|
||||
{
|
||||
$tagId = $addAliasCommand->getTagId();
|
||||
$description = $addAliasCommand->getDescription();
|
||||
|
||||
if ($this->existsRule->appliesTo($description)) {
|
||||
throw new TagAliasAlreadyExistsException($description);
|
||||
}
|
||||
|
||||
$tag = $this->tagRepository->findOneBy(['id' => $tagId]);
|
||||
if ($tag === null) {
|
||||
throw new TagNotFoundByIdException($tagId);
|
||||
}
|
||||
|
||||
$tagAlias = $this->aliasBuilder->build(
|
||||
$tag,
|
||||
$description
|
||||
);
|
||||
|
||||
$this->entityManager->persist($tagAlias);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new AddAliasCommandResult($tagAlias);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\AddAlias;
|
||||
|
||||
use MyTube\Data\Business\Entity\TagAlias;
|
||||
|
||||
class AddAliasCommandResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TagAlias $alias,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getAlias(): TagAlias
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\Create;
|
||||
|
||||
class CreateCommand
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $description
|
||||
) {
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\Create;
|
||||
|
||||
class CreateCommandBuilder
|
||||
{
|
||||
public function build(
|
||||
string $description
|
||||
): CreateCommand {
|
||||
return new CreateCommand(
|
||||
$description
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\Create;
|
||||
|
||||
use MyTube\Data\Business\Manager\MyTubeEntityManager;
|
||||
use MyTube\Handling\Tag\Builder\TagBuilder;
|
||||
use MyTube\Handling\Tag\Exception\TagAlreadyExistsException;
|
||||
use MyTube\Handling\Tag\Rule\TagExistsRule;
|
||||
|
||||
class CreateCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TagExistsRule $existsRule,
|
||||
private readonly TagBuilder $tagBuilder,
|
||||
private readonly MyTubeEntityManager $entityManager
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TagAlreadyExistsException
|
||||
*/
|
||||
public function execute(CreateCommand $createCommand): CreateCommandResult
|
||||
{
|
||||
$description = $createCommand->getDescription();
|
||||
|
||||
if ($this->existsRule->appliesTo($description)) {
|
||||
throw new TagAlreadyExistsException($description);
|
||||
}
|
||||
|
||||
$tag = $this->tagBuilder->build($description);
|
||||
|
||||
$this->entityManager->persist($tag);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new CreateCommandResult($tag);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Tag\Handler\Command\Create;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
|
||||
class CreateCommandResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Tag $tag,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getTag(): Tag
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
}
|
||||
27
src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php
Normal file
27
src/HandlingDomain/Tag/src/Rule/TagAliasExistsRule.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Rule;
|
||||
|
||||
use MyTube\Data\Business\Repository\TagAliasRepository;
|
||||
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
|
||||
|
||||
class TagAliasExistsRule
|
||||
{
|
||||
/**
|
||||
* @InjectDoctrineRepository(
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\TagAlias"
|
||||
* )
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly TagAliasRepository $tagAliasRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function appliesTo(
|
||||
string $description,
|
||||
): bool
|
||||
{
|
||||
return $this->tagAliasRepository->findOneBy(['description' => $description]) !== null;
|
||||
}
|
||||
}
|
||||
28
src/HandlingDomain/Tag/src/Rule/TagExistsRule.php
Normal file
28
src/HandlingDomain/Tag/src/Rule/TagExistsRule.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Tag\Rule;
|
||||
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
use MyTube\Data\Business\Repository\TagRepository;
|
||||
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
|
||||
|
||||
class TagExistsRule
|
||||
{
|
||||
/**
|
||||
* @InjectDoctrineRepository(
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\Tag"
|
||||
* )
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly TagRepository $tagRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function appliesTo(
|
||||
string $description,
|
||||
): bool
|
||||
{
|
||||
return $this->tagRepository->findOneBy(['description' => $description]) !== null;
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,14 @@ use MyTube\Handling\Video\Handler\Query\Stream\StreamQueryBuilder;
|
||||
use MyTube\Handling\Video\Handler\Query\Stream\StreamQueryHandler;
|
||||
use MyTube\Handling\Video\Handler\Query\Thumbnail\ThumbnailQueryBuilder;
|
||||
use MyTube\Handling\Video\Handler\Query\Thumbnail\ThumbnailQueryHandler;
|
||||
use MyTube\Handling\Video\Analyzer\VideoTitleAnalyzer;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\AnalyzeTitleStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\BuildVideoStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\CheckVideoStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\SaveEntityStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\UploadFileStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPipeline;
|
||||
use MyTube\Handling\Video\Rule\VideoExistsRule;
|
||||
use MyTube\Handling\Video\Uploader\VideoUploader;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
use Reinfi\DependencyInjection\Factory\InjectionFactory;
|
||||
@ -17,6 +25,9 @@ use Reinfi\DependencyInjection\Factory\InjectionFactory;
|
||||
|
||||
return [
|
||||
'factories' => [
|
||||
/// Uploader
|
||||
VideoTitleAnalyzer::class => InjectionFactory::class,
|
||||
|
||||
/// Uploader
|
||||
VideoUploader::class => AutoWiringFactory::class,
|
||||
|
||||
@ -24,6 +35,9 @@ return [
|
||||
VideoBuilder::class => AutoWiringFactory::class,
|
||||
VideoImageBuilder::class => AutoWiringFactory::class,
|
||||
|
||||
/// Rule
|
||||
VideoExistsRule::class => InjectionFactory::class,
|
||||
|
||||
/// CQRS
|
||||
// Stream Query
|
||||
StreamQueryHandler::class => InjectionFactory::class,
|
||||
@ -37,5 +51,14 @@ return [
|
||||
// Read Details
|
||||
ReadDetailsQueryHandler::class => InjectionFactory::class,
|
||||
ReadDetailsQueryBuilder::class => AutoWiringFactory::class,
|
||||
|
||||
/// Pipeline
|
||||
// Upload
|
||||
CheckVideoStep::class => AutoWiringFactory::class,
|
||||
UploadPipeline::class => AutoWiringFactory::class,
|
||||
BuildVideoStep::class => AutoWiringFactory::class,
|
||||
AnalyzeTitleStep::class => AutoWiringFactory::class,
|
||||
UploadFileStep::class => AutoWiringFactory::class,
|
||||
SaveEntityStep::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
|
||||
66
src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php
Normal file
66
src/HandlingDomain/Video/src/Analyzer/VideoTitleAnalyzer.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Video\Analyzer;
|
||||
|
||||
use Exception;
|
||||
use MyTube\Data\Business\Entity\Tag;
|
||||
use MyTube\Data\Business\Entity\TagAlias;
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use MyTube\Data\Business\Repository\TagRepository;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
|
||||
|
||||
class VideoTitleAnalyzer
|
||||
{
|
||||
/**
|
||||
* @InjectDoctrineRepository(
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\Tag"
|
||||
* )
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly TagRepository $tagRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function analyze(
|
||||
Video $video
|
||||
): array {
|
||||
$title = $this->normalizeString($video->getTitle());
|
||||
|
||||
$tags = $this->tagRepository->findAll();
|
||||
$matches = [];
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
if (str_contains($title, $this->normalizeString($tag->getDescription()))) {
|
||||
$matches[] = $tag;
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var TagAlias $alias */
|
||||
foreach ($tag->getAliases() as $alias) {
|
||||
if (str_contains($title, $this->normalizeString($alias->getDescription()))) {
|
||||
$matches[] = $tag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Tag $match */
|
||||
foreach ($matches as $match) {
|
||||
$video->addTag($match);
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
private function normalizeString(string $string): string {
|
||||
return preg_replace(
|
||||
'/[^a-zA-Z0-9]+/',
|
||||
'',
|
||||
strtolower($string)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2,24 +2,27 @@
|
||||
|
||||
namespace MyTube\Handling\Video\Builder;
|
||||
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class VideoImageBuilder
|
||||
{
|
||||
private const FILE_NAME = 'thumbnail.png';
|
||||
|
||||
public function build(
|
||||
Video $video,
|
||||
UuidInterface $directoryId,
|
||||
string $timestamp = '00:00:10'
|
||||
): string|null
|
||||
{
|
||||
$videoId = $video->getId()->toString();
|
||||
|
||||
$targetPath = sprintf(
|
||||
'%s/%s/%s/%s',
|
||||
$directoryPath = sprintf(
|
||||
'%s/%s/%s',
|
||||
APP_ROOT,
|
||||
'var/filestore',
|
||||
$videoId,
|
||||
$directoryId->toString()
|
||||
);
|
||||
|
||||
$targetPath = sprintf(
|
||||
'%s/%s',
|
||||
$directoryPath,
|
||||
self::FILE_NAME
|
||||
);
|
||||
|
||||
@ -27,11 +30,14 @@ class VideoImageBuilder
|
||||
return null;
|
||||
}
|
||||
|
||||
$command = "cd /var/www/html/var/filestore/" . $videoId . "/" .
|
||||
" && " .
|
||||
"ffmpeg -i video.mp4 -ss " . $timestamp . " -vframes 1 " . self::FILE_NAME;
|
||||
$command = sprintf(
|
||||
"cd %s && ffmpeg -i video.mp4 -ss %s -vframes 1 %s",
|
||||
$directoryPath,
|
||||
$timestamp,
|
||||
self::FILE_NAME
|
||||
);
|
||||
|
||||
$output = shell_exec($command);
|
||||
shell_exec($command);
|
||||
|
||||
return $targetPath;
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Video\Exception;
|
||||
|
||||
use MyTube\Infrastructure\Exception\ErrorCode;
|
||||
use MyTube\Infrastructure\Exception\ErrorDomain;
|
||||
use MyTube\Infrastructure\Exception\Exception\MyTubeException;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
class VideoAlreadyExistsException extends MyTubeException {
|
||||
|
||||
private const MESSAGE = 'A Video with the Title %s does already exist!';
|
||||
|
||||
public function __construct(UploadedFileInterface $uploadedFile)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
self::MESSAGE,
|
||||
$uploadedFile->getClientFilename()
|
||||
),
|
||||
ErrorDomain::Video,
|
||||
ErrorCode::AlreadyExists
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class VideoNotFoundByIdException extends MyTubeException {
|
||||
|
||||
private const MESSAGE = 'The user with the Id %s was not found!';
|
||||
private const MESSAGE = 'The Video with the Id %s was not found!';
|
||||
|
||||
public function __construct(UuidInterface $id)
|
||||
{
|
||||
|
||||
@ -8,38 +8,26 @@ use MyTube\Data\Business\Entity\Video;
|
||||
use MyTube\Data\Business\Manager\MyTubeEntityManager;
|
||||
use MyTube\Handling\Video\Builder\VideoBuilder;
|
||||
use MyTube\Handling\Video\Builder\VideoImageBuilder;
|
||||
use MyTube\Handling\Video\Uploader\VideoUploader;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPipeline;
|
||||
use MyTube\Handling\Video\Uploader\VideoTitleAnalyzer;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Exception;
|
||||
|
||||
class UploadCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoBuilder $videoBuilder,
|
||||
private readonly MyTubeEntityManager $entityManager,
|
||||
private readonly VideoUploader $uploader,
|
||||
private readonly UploadPipeline $pipeline,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(UploadCommand $uploadCommand): Video
|
||||
{
|
||||
$uploadedFile = $uploadCommand->getUploadedFile();
|
||||
$payload = new UploadPayload();
|
||||
$payload->setUploadedFile($uploadCommand->getUploadedFile());
|
||||
|
||||
$video = $this->videoBuilder->build(
|
||||
$uploadedFile->getClientFilename()
|
||||
);
|
||||
$this->pipeline->handle($payload);
|
||||
|
||||
$this->uploader->upload(
|
||||
$uploadedFile,
|
||||
$video
|
||||
);
|
||||
|
||||
$this->entityManager->persist($video);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $video;
|
||||
return $payload->getVideo();
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,9 +18,11 @@ class ReadDetailsQueryHandler
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\Video"
|
||||
* )
|
||||
* @Inject("MyTube\Handling\Video\Builder\VideoImageBuilder")
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly VideoRepository $videoRepository,
|
||||
private readonly VideoImageBuilder $videoImageBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -38,6 +40,8 @@ class ReadDetailsQueryHandler
|
||||
throw new VideoNotFoundByIdException($videoId);
|
||||
}
|
||||
|
||||
$this->videoImageBuilder->build($video->getId());
|
||||
|
||||
return new ReadDetailsQueryResult($video);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload\Step;
|
||||
|
||||
use MyTube\Handling\Video\Analyzer\VideoTitleAnalyzer;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
|
||||
use teewurst\Pipeline\PipelineInterface;
|
||||
use teewurst\Pipeline\TaskInterface;
|
||||
|
||||
class AnalyzeTitleStep implements TaskInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoTitleAnalyzer $analyzer,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
$payload,
|
||||
PipelineInterface $pipeline
|
||||
): void
|
||||
{
|
||||
/** @var UploadPayload $uploadPayload */
|
||||
$uploadPayload = $payload;
|
||||
$video = $uploadPayload->getVideo();
|
||||
|
||||
$this->analyzer->analyze($video);
|
||||
|
||||
$pipeline->next()($payload, $pipeline);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload\Step;
|
||||
|
||||
use MyTube\Handling\Video\Builder\VideoBuilder;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
|
||||
use teewurst\Pipeline\PipelineInterface;
|
||||
use teewurst\Pipeline\TaskInterface;
|
||||
|
||||
class BuildVideoStep implements TaskInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoBuilder $videoBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
$payload,
|
||||
PipelineInterface $pipeline
|
||||
): void
|
||||
{
|
||||
/** @var UploadPayload $uploadPayload */
|
||||
$uploadPayload = $payload;
|
||||
$uploadedFile = $uploadPayload->getUploadedFile();
|
||||
|
||||
$video = $this->videoBuilder->build(
|
||||
$uploadedFile->getClientFilename()
|
||||
);
|
||||
|
||||
$uploadPayload->setVideo($video);
|
||||
$pipeline->next()($payload, $pipeline);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload\Step;
|
||||
|
||||
use MyTube\Handling\Video\Builder\VideoBuilder;
|
||||
use MyTube\Handling\Video\Exception\VideoAlreadyExistsException;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
|
||||
use MyTube\Handling\Video\Rule\VideoExistsRule;
|
||||
use teewurst\Pipeline\PipelineInterface;
|
||||
use teewurst\Pipeline\TaskInterface;
|
||||
|
||||
class CheckVideoStep implements TaskInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoExistsRule $existsRule,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws VideoAlreadyExistsException
|
||||
*/
|
||||
public function __invoke(
|
||||
$payload,
|
||||
PipelineInterface $pipeline
|
||||
): void
|
||||
{
|
||||
/** @var UploadPayload $uploadPayload */
|
||||
$uploadPayload = $payload;
|
||||
$uploadedFile = $uploadPayload->getUploadedFile();
|
||||
|
||||
if ($this->existsRule->appliesTo($uploadedFile)) {
|
||||
throw new VideoAlreadyExistsException($uploadedFile);
|
||||
}
|
||||
|
||||
$pipeline->next()($payload, $pipeline);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload\Step;
|
||||
|
||||
use MyTube\Data\Business\Manager\MyTubeEntityManager;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
use teewurst\Pipeline\PipelineInterface;
|
||||
use teewurst\Pipeline\TaskInterface;
|
||||
|
||||
class SaveEntityStep implements TaskInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MyTubeEntityManager $entityManager,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
$payload,
|
||||
PipelineInterface $pipeline
|
||||
): void
|
||||
{
|
||||
/** @var UploadPayload $uploadPayload */
|
||||
$uploadPayload = $payload;
|
||||
$video = $uploadPayload->getVideo();
|
||||
|
||||
$this->entityManager->persist($video);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload\Step;
|
||||
|
||||
use MyTube\Handling\Video\Builder\VideoImageBuilder;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
|
||||
use MyTube\Handling\Video\Uploader\VideoUploader;
|
||||
use teewurst\Pipeline\PipelineInterface;
|
||||
use teewurst\Pipeline\TaskInterface;
|
||||
|
||||
class UploadFileStep implements TaskInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoUploader $uploader,
|
||||
private readonly VideoImageBuilder $imageBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __invoke(
|
||||
$payload,
|
||||
PipelineInterface $pipeline
|
||||
): void
|
||||
{
|
||||
/** @var UploadPayload $uploadPayload */
|
||||
$uploadPayload = $payload;
|
||||
$video = $uploadPayload->getVideo();
|
||||
$uploadedFile = $uploadPayload->getUploadedFile();
|
||||
|
||||
$directoryPath = $this->uploader->upload($uploadedFile, $video->getId());
|
||||
$video->setDirectoryPath($directoryPath);
|
||||
|
||||
$this->imageBuilder->build($video->getId());
|
||||
|
||||
$pipeline->next()($payload, $pipeline);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload;
|
||||
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
|
||||
class UploadPayload
|
||||
{
|
||||
private UploadedFileInterface $uploadedFile;
|
||||
private Video $video;
|
||||
|
||||
public function getUploadedFile(): UploadedFileInterface
|
||||
{
|
||||
return $this->uploadedFile;
|
||||
}
|
||||
public function setUploadedFile(UploadedFileInterface $uploadedFile): void
|
||||
{
|
||||
$this->uploadedFile = $uploadedFile;
|
||||
}
|
||||
|
||||
public function getVideo(): Video
|
||||
{
|
||||
return $this->video;
|
||||
}
|
||||
public function setVideo(Video $video): void
|
||||
{
|
||||
$this->video = $video;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\Video\Pipeline\Upload;
|
||||
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\BuildVideoStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\AnalyzeTitleStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\CheckVideoStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\UploadFileStep;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\Step\SaveEntityStep;
|
||||
use teewurst\Pipeline\Pipeline;
|
||||
|
||||
class UploadPipeline extends Pipeline
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CheckVideoStep $checkVideoStep,
|
||||
private readonly BuildVideoStep $buildVideoStep,
|
||||
private readonly AnalyzeTitleStep $analyzeTitleStep,
|
||||
private readonly UploadFileStep $uploadFileStep,
|
||||
private readonly SaveEntityStep $saveEntityStep,
|
||||
) {
|
||||
parent::__construct([
|
||||
$this->checkVideoStep,
|
||||
$this->buildVideoStep,
|
||||
$this->analyzeTitleStep,
|
||||
$this->uploadFileStep,
|
||||
$this->saveEntityStep,
|
||||
]);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->tasks = [
|
||||
$this->checkVideoStep,
|
||||
$this->buildVideoStep,
|
||||
$this->analyzeTitleStep,
|
||||
$this->uploadFileStep,
|
||||
$this->saveEntityStep,
|
||||
];
|
||||
}
|
||||
}
|
||||
29
src/HandlingDomain/Video/src/Rule/VideoExistsRule.php
Normal file
29
src/HandlingDomain/Video/src/Rule/VideoExistsRule.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace MyTube\Handling\Video\Rule;
|
||||
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use MyTube\Data\Business\Repository\VideoRepository;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Reinfi\DependencyInjection\Annotation\InjectDoctrineRepository;
|
||||
|
||||
class VideoExistsRule
|
||||
{
|
||||
/**
|
||||
* @InjectDoctrineRepository(
|
||||
* entityManager="MyTube\Data\Business\Manager\MyTubeEntityManager",
|
||||
* entity="MyTube\Data\Business\Entity\Video"
|
||||
* )
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly VideoRepository $tagRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function appliesTo(
|
||||
UploadedFileInterface $uploadedFile,
|
||||
): bool
|
||||
{
|
||||
return $this->tagRepository->findOneBy(['title' => $uploadedFile->getClientFilename()]) !== null;
|
||||
}
|
||||
}
|
||||
@ -4,37 +4,31 @@ namespace MyTube\Handling\Video\Uploader;
|
||||
|
||||
use Exception;
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use MyTube\Handling\Video\Builder\VideoImageBuilder;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class VideoUploader
|
||||
{
|
||||
public function __construct(
|
||||
private readonly VideoImageBuilder $imageBuilder,
|
||||
) {
|
||||
}
|
||||
|
||||
public function upload(
|
||||
UploadedFileInterface $file,
|
||||
Video $video,
|
||||
): void {
|
||||
$targetPath = sprintf(
|
||||
UuidInterface $directoryId,
|
||||
): string {
|
||||
$directoryPath = sprintf(
|
||||
'%s/%s/%s/',
|
||||
APP_ROOT,
|
||||
'var/filestore',
|
||||
$video->getId()->toString(),
|
||||
$directoryId->toString(),
|
||||
);
|
||||
|
||||
if (file_exists($targetPath)) {
|
||||
if (file_exists($directoryPath)) {
|
||||
throw new Exception('File already exists');
|
||||
}
|
||||
|
||||
mkdir($targetPath, 0777, true);
|
||||
$video->setDirectoryPath($targetPath);
|
||||
mkdir($directoryPath, 0777, true);
|
||||
|
||||
$targetPath = $targetPath . 'video.' . substr(strrchr($file->getClientFilename(),'.'),1);
|
||||
$targetPath = $directoryPath . 'video.' . substr(strrchr($file->getClientFilename(),'.'),1);
|
||||
$file->moveTo($targetPath);
|
||||
|
||||
$this->imageBuilder->build($video);
|
||||
return $directoryPath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
<?php
|
||||
|
||||
use MyTube\Handling\Video\Builder\VideoBuilder;
|
||||
use MyTube\Handling\Video\Handler\Command\Upload\UploadCommandBuilder;
|
||||
use MyTube\Handling\Video\Handler\Command\Upload\UploadCommandHandler;
|
||||
use MyTube\Handling\Video\Handler\Query\Stream\StreamQueryBuilder;
|
||||
use MyTube\Handling\Video\Handler\Query\Stream\StreamQueryHandler;
|
||||
use MyTube\Handling\VideoList\Handler\Command\Upload\UploadCommandBuilder;
|
||||
use MyTube\Handling\VideoList\Handler\Command\Upload\UploadCommandHandler;
|
||||
use MyTube\Handling\VideoList\Handler\Query\ReadList\ReadListQueryBuilder;
|
||||
use MyTube\Handling\VideoList\Handler\Query\ReadList\ReadListQueryHandler;
|
||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||
@ -15,7 +12,10 @@ return [
|
||||
'factories' => [
|
||||
/// CQRS
|
||||
// Read List
|
||||
ReadListQueryHandler::class => InjectionFactory::class,
|
||||
ReadListQueryBuilder::class => AutoWiringFactory::class,
|
||||
ReadListQueryHandler::class => InjectionFactory::class,
|
||||
// Upload
|
||||
UploadCommandBuilder::class => AutoWiringFactory::class,
|
||||
UploadCommandHandler::class => AutoWiringFactory::class,
|
||||
],
|
||||
];
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\VideoList\Handler\Command\Upload;
|
||||
|
||||
class UploadCommand
|
||||
{
|
||||
public function __construct(
|
||||
private array $uploadedFiles
|
||||
) {
|
||||
}
|
||||
|
||||
public function getUploadedFiles(): array
|
||||
{
|
||||
return $this->uploadedFiles;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\VideoList\Handler\Command\Upload;
|
||||
|
||||
class UploadCommandBuilder
|
||||
{
|
||||
public function build(
|
||||
array $uploadedFiles
|
||||
): UploadCommand {
|
||||
return new UploadCommand(
|
||||
$uploadedFiles
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\VideoList\Handler\Command\Upload;
|
||||
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPayload;
|
||||
use MyTube\Handling\Video\Pipeline\Upload\UploadPipeline;
|
||||
use MyTube\Handling\VideoList\Model\UploadedFileResult;
|
||||
use MyTube\Infrastructure\Logging\Logger\Logger;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Throwable;
|
||||
|
||||
class UploadCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UploadPipeline $pipeline,
|
||||
private readonly Logger $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
public function execute(UploadCommand $uploadCommand): UploadCommandResult
|
||||
{
|
||||
$uploadedFiles = [];
|
||||
|
||||
/** @var UploadedFileInterface $uploadedFile */
|
||||
foreach ($uploadCommand->getUploadedFiles() as $uploadedFile) {
|
||||
try {
|
||||
$payload = new UploadPayload();
|
||||
$payload->setUploadedFile($uploadedFile);
|
||||
|
||||
$this->pipeline->reset();
|
||||
$this->pipeline->handle($payload);
|
||||
|
||||
$uploadedFiles[] = UploadedFileResult::fromVideo($uploadedFile, $payload->getVideo());
|
||||
} catch (Throwable $e) {
|
||||
$uploadedFiles[] = UploadedFileResult::fromException($uploadedFile, $e);
|
||||
$this->logger->exception($e);
|
||||
$this->logger->info($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
return new UploadCommandResult($uploadedFiles);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\VideoList\Handler\Command\Upload;
|
||||
|
||||
class UploadCommandResult
|
||||
{
|
||||
public function __construct(
|
||||
private readonly array $uploadedFileResults
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getUploadedFileResults(): array
|
||||
{
|
||||
return $this->uploadedFileResults;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MyTube\Handling\VideoList\Model;
|
||||
|
||||
use MyTube\Data\Business\Entity\Video;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Throwable;
|
||||
|
||||
class UploadedFileResult
|
||||
{
|
||||
public static function fromVideo(
|
||||
UploadedFileInterface $uploadedFile,
|
||||
Video $video
|
||||
): UploadedFileResult
|
||||
{
|
||||
return new UploadedFileResult(
|
||||
$uploadedFile,
|
||||
$video,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public static function fromException(
|
||||
UploadedFileInterface $uploadedFile,
|
||||
Throwable $exception
|
||||
): UploadedFileResult
|
||||
{
|
||||
return new UploadedFileResult(
|
||||
$uploadedFile,
|
||||
null,
|
||||
$exception
|
||||
);
|
||||
}
|
||||
|
||||
private readonly bool $success;
|
||||
|
||||
public function __construct(
|
||||
private readonly UploadedFileInterface $uploadedFile,
|
||||
private readonly ?Video $video,
|
||||
private readonly ?Throwable $exception
|
||||
) {
|
||||
$this->success = $this->video != null;
|
||||
}
|
||||
|
||||
public function getUploadedFile(): UploadedFileInterface
|
||||
{
|
||||
return $this->uploadedFile;
|
||||
}
|
||||
|
||||
public function getSuccess(): bool
|
||||
{
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
public function getVideo(): ?Video
|
||||
{
|
||||
return $this->video;
|
||||
}
|
||||
|
||||
public function getException(): ?Throwable
|
||||
{
|
||||
return $this->exception;
|
||||
}
|
||||
}
|
||||
@ -11,4 +11,6 @@ enum ErrorDomain : string {
|
||||
case Registration = 'Registration';
|
||||
case Product = 'Product';
|
||||
case Video = 'Video';
|
||||
case Tag = 'Tag';
|
||||
case TagAlias = 'TagAlias';
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user