diff --git a/app/composer.json b/app/composer.json index a785c5a8ce0aac79064866f9b72a32151e2726d3..b948b376167cfea0e64327c66fc05b38f7a8e8c8 100644 --- a/app/composer.json +++ b/app/composer.json @@ -4,7 +4,7 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=8.2", + "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*", "doctrine/dbal": "^3", diff --git a/app/migrations/Version20240427114630.php b/app/migrations/Version20240520053704.php similarity index 67% rename from app/migrations/Version20240427114630.php rename to app/migrations/Version20240520053704.php index 84378b72ef805ae806b666f1171a2a2956f7d2f8..3ba63898803868870954dc39857c3c6e3a9a98ca 100644 --- a/app/migrations/Version20240427114630.php +++ b/app/migrations/Version20240520053704.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20240427114630 extends AbstractMigration +final class Version20240520053704 extends AbstractMigration { public function getDescription(): string { @@ -20,9 +20,12 @@ final class Version20240427114630 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TABLE file (id UUID NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, size INT NOT NULL, type VARCHAR(255) NOT NULL, url VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE TABLE kitchens (id UUID NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); - $this->addSql('CREATE TABLE news (id UUID NOT NULL, type_id UUID DEFAULT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, sort INT NOT NULL, active BOOLEAN NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, preview_image VARCHAR(255) DEFAULT NULL, preview_text VARCHAR(1000) DEFAULT NULL, detail_image VARCHAR(255) DEFAULT NULL, detail_text VARCHAR(255) DEFAULT NULL, main_page_render BOOLEAN NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE TABLE news (id UUID NOT NULL, type_id UUID DEFAULT NULL, detail_image_id UUID DEFAULT NULL, preview_image_id UUID DEFAULT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, sort INT NOT NULL, active BOOLEAN NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, preview_text VARCHAR(1000) DEFAULT NULL, detail_text VARCHAR(255) DEFAULT NULL, main_page_render BOOLEAN NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE INDEX IDX_1DD39950C54C8C93 ON news (type_id)'); + $this->addSql('CREATE INDEX IDX_1DD39950CB7BCCB6 ON news (detail_image_id)'); + $this->addSql('CREATE INDEX IDX_1DD39950FAE957CD ON news (preview_image_id)'); $this->addSql('COMMENT ON COLUMN news.created_at IS \'(DC2Type:datetime_immutable)\''); $this->addSql('COMMENT ON COLUMN news.update_at IS \'(DC2Type:datetime_immutable)\''); $this->addSql('CREATE TABLE news_news_categories (news_id UUID NOT NULL, news_categories_id UUID NOT NULL, PRIMARY KEY(news_id, news_categories_id))'); @@ -31,15 +34,19 @@ final class Version20240427114630 extends AbstractMigration $this->addSql('CREATE TABLE news_categories (id UUID NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE TABLE news_type (id UUID NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE TABLE restaurant_types (id UUID NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); - $this->addSql('CREATE TABLE restaurants (id UUID NOT NULL, type_id UUID DEFAULT NULL, settlement_id UUID DEFAULT NULL, active BOOLEAN NOT NULL, sort INT NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, receipt VARCHAR(255) NOT NULL, receipt_info VARCHAR(255) NOT NULL, phone VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, tags VARCHAR(255) NOT NULL, site VARCHAR(255) NOT NULL, coordinates TEXT NOT NULL, preview_image VARCHAR(255) NOT NULL, detail_image VARCHAR(255) NOT NULL, gallery VARCHAR(255) NOT NULL, how_to_find VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE TABLE restaurants (id UUID NOT NULL, type_id UUID DEFAULT NULL, settlement_id UUID DEFAULT NULL, preview_image_id UUID DEFAULT NULL, detail_image_id UUID DEFAULT NULL, active BOOLEAN NOT NULL, sort INT NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, receipt VARCHAR(255) NOT NULL, receipt_info VARCHAR(255) NOT NULL, phone VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, tags VARCHAR(255) NOT NULL, site VARCHAR(255) NOT NULL, coordinates VARCHAR(1000) NOT NULL, how_to_find VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); $this->addSql('CREATE INDEX IDX_AD837724C54C8C93 ON restaurants (type_id)'); $this->addSql('CREATE INDEX IDX_AD837724C2B9C425 ON restaurants (settlement_id)'); + $this->addSql('CREATE INDEX IDX_AD837724FAE957CD ON restaurants (preview_image_id)'); + $this->addSql('CREATE INDEX IDX_AD837724CB7BCCB6 ON restaurants (detail_image_id)'); $this->addSql('COMMENT ON COLUMN restaurants.created_at IS \'(DC2Type:datetime_immutable)\''); $this->addSql('COMMENT ON COLUMN restaurants.update_at IS \'(DC2Type:datetime_immutable)\''); - $this->addSql('COMMENT ON COLUMN restaurants.coordinates IS \'(DC2Type:array)\''); $this->addSql('CREATE TABLE restaurants_kitchens (restaurants_id UUID NOT NULL, kitchens_id UUID NOT NULL, PRIMARY KEY(restaurants_id, kitchens_id))'); $this->addSql('CREATE INDEX IDX_716464694DCA160A ON restaurants_kitchens (restaurants_id)'); $this->addSql('CREATE INDEX IDX_71646469E043FCBC ON restaurants_kitchens (kitchens_id)'); + $this->addSql('CREATE TABLE restaurants_file (restaurants_id UUID NOT NULL, file_id UUID NOT NULL, PRIMARY KEY(restaurants_id, file_id))'); + $this->addSql('CREATE INDEX IDX_EBC930974DCA160A ON restaurants_file (restaurants_id)'); + $this->addSql('CREATE INDEX IDX_EBC9309793CB796C ON restaurants_file (file_id)'); $this->addSql('CREATE TABLE settlements (id UUID NOT NULL, active BOOLEAN NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, update_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, name VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, coordinates TEXT NOT NULL, PRIMARY KEY(id))'); $this->addSql('COMMENT ON COLUMN settlements.created_at IS \'(DC2Type:datetime_immutable)\''); $this->addSql('COMMENT ON COLUMN settlements.update_at IS \'(DC2Type:datetime_immutable)\''); @@ -62,12 +69,18 @@ final class Version20240427114630 extends AbstractMigration $this->addSql('DROP TRIGGER IF EXISTS notify_trigger ON messenger_messages;'); $this->addSql('CREATE TRIGGER notify_trigger AFTER INSERT OR UPDATE ON messenger_messages FOR EACH ROW EXECUTE PROCEDURE notify_messenger_messages();'); $this->addSql('ALTER TABLE news ADD CONSTRAINT FK_1DD39950C54C8C93 FOREIGN KEY (type_id) REFERENCES news_type (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE news ADD CONSTRAINT FK_1DD39950CB7BCCB6 FOREIGN KEY (detail_image_id) REFERENCES file (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE news ADD CONSTRAINT FK_1DD39950FAE957CD FOREIGN KEY (preview_image_id) REFERENCES file (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE news_news_categories ADD CONSTRAINT FK_34AB0102B5A459A0 FOREIGN KEY (news_id) REFERENCES news (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE news_news_categories ADD CONSTRAINT FK_34AB0102F7F178F1 FOREIGN KEY (news_categories_id) REFERENCES news_categories (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE restaurants ADD CONSTRAINT FK_AD837724C54C8C93 FOREIGN KEY (type_id) REFERENCES restaurant_types (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE restaurants ADD CONSTRAINT FK_AD837724C2B9C425 FOREIGN KEY (settlement_id) REFERENCES settlements (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE restaurants ADD CONSTRAINT FK_AD837724FAE957CD FOREIGN KEY (preview_image_id) REFERENCES file (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE restaurants ADD CONSTRAINT FK_AD837724CB7BCCB6 FOREIGN KEY (detail_image_id) REFERENCES file (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE restaurants_kitchens ADD CONSTRAINT FK_716464694DCA160A FOREIGN KEY (restaurants_id) REFERENCES restaurants (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE restaurants_kitchens ADD CONSTRAINT FK_71646469E043FCBC FOREIGN KEY (kitchens_id) REFERENCES kitchens (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE restaurants_file ADD CONSTRAINT FK_EBC930974DCA160A FOREIGN KEY (restaurants_id) REFERENCES restaurants (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE restaurants_file ADD CONSTRAINT FK_EBC9309793CB796C FOREIGN KEY (file_id) REFERENCES file (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); } public function down(Schema $schema): void @@ -75,12 +88,19 @@ final class Version20240427114630 extends AbstractMigration // this down() migration is auto-generated, please modify it to your needs $this->addSql('CREATE SCHEMA public'); $this->addSql('ALTER TABLE news DROP CONSTRAINT FK_1DD39950C54C8C93'); + $this->addSql('ALTER TABLE news DROP CONSTRAINT FK_1DD39950CB7BCCB6'); + $this->addSql('ALTER TABLE news DROP CONSTRAINT FK_1DD39950FAE957CD'); $this->addSql('ALTER TABLE news_news_categories DROP CONSTRAINT FK_34AB0102B5A459A0'); $this->addSql('ALTER TABLE news_news_categories DROP CONSTRAINT FK_34AB0102F7F178F1'); $this->addSql('ALTER TABLE restaurants DROP CONSTRAINT FK_AD837724C54C8C93'); $this->addSql('ALTER TABLE restaurants DROP CONSTRAINT FK_AD837724C2B9C425'); + $this->addSql('ALTER TABLE restaurants DROP CONSTRAINT FK_AD837724FAE957CD'); + $this->addSql('ALTER TABLE restaurants DROP CONSTRAINT FK_AD837724CB7BCCB6'); $this->addSql('ALTER TABLE restaurants_kitchens DROP CONSTRAINT FK_716464694DCA160A'); $this->addSql('ALTER TABLE restaurants_kitchens DROP CONSTRAINT FK_71646469E043FCBC'); + $this->addSql('ALTER TABLE restaurants_file DROP CONSTRAINT FK_EBC930974DCA160A'); + $this->addSql('ALTER TABLE restaurants_file DROP CONSTRAINT FK_EBC9309793CB796C'); + $this->addSql('DROP TABLE file'); $this->addSql('DROP TABLE kitchens'); $this->addSql('DROP TABLE news'); $this->addSql('DROP TABLE news_news_categories'); @@ -89,6 +109,7 @@ final class Version20240427114630 extends AbstractMigration $this->addSql('DROP TABLE restaurant_types'); $this->addSql('DROP TABLE restaurants'); $this->addSql('DROP TABLE restaurants_kitchens'); + $this->addSql('DROP TABLE restaurants_file'); $this->addSql('DROP TABLE settlements'); $this->addSql('DROP TABLE users'); $this->addSql('DROP TABLE messenger_messages'); diff --git a/app/src/News/Controller/NewsController.php b/app/src/News/Controller/NewsController.php index 34e1dbba36c09283223134479ddea3d751b61ee3..10d45300c9dc0cbbfb255a5dce0e67b9f5ccc3d7 100644 --- a/app/src/News/Controller/NewsController.php +++ b/app/src/News/Controller/NewsController.php @@ -2,14 +2,22 @@ namespace App\News\Controller; +use App\News\Request\NewsCreateRequest; use App\News\Request\NewsDetailRequest; +use App\News\Request\NewsFullUpdateRequest; use App\News\Request\NewsListingRequest; +use App\News\Request\NewsPartUpdateRequest; +use App\News\UseCase\NewsCreateUseCase; +use App\News\UseCase\NewsDeleteUseCase; +use App\News\UseCase\NewsFullUpdateUseCase; use App\News\UseCase\NewsGetDetailMainUseCase; use App\News\UseCase\NewsGetDetailUseCase; use App\News\UseCase\NewsGetListingUseUseCase; use App\News\UseCase\NewsGetMainUseCase; +use App\News\UseCase\NewsPartUpdateUseCase; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; #[Route('/api/v1/news')] @@ -20,6 +28,10 @@ class NewsController extends AbstractController private readonly NewsGetDetailUseCase $getDetailNews, private readonly NewsGetMainUseCase $getMainNews, private readonly NewsGetDetailMainUseCase $getDetailMainNews, + private readonly NewsCreateUseCase $createNews, + private readonly NewsFullUpdateUseCase $fullUpdate, + private readonly NewsPartUpdateUseCase $partUpdate, + private readonly NewsDeleteUseCase $delete, ) { } @@ -46,4 +58,28 @@ class NewsController extends AbstractController { return $this->getDetailNews->execute($request); } + + #[Route('/create', name: 'createNews', methods: ['POST'])] + public function createNews(NewsCreateRequest $request): Response + { + return $this->createNews->execute($request); + } + + #[Route('/update', name: 'updateNews', methods: ['PUT'])] + public function fillUpdateNews(NewsFullUpdateRequest $request): Response + { + return $this->fullUpdate->execute($request); + } + + #[Route('/partUpdate', name: 'partUpdateNews', methods: ['PATCH'])] + public function partUpdateNews(NewsPartUpdateRequest $request): Response + { + return $this->partUpdate->execute($request); + } + + #[Route('/delete/{detailId}', name: 'deleteNews', methods: ['DELETE'])] + public function deleteNews(NewsDetailRequest $request): Response + { + return $this->delete->execute($request); + } } diff --git a/app/src/News/Dto/NewsListDto.php b/app/src/News/Dto/NewsListDto.php index 833a93c6a3a508198c9f303d2bab3e082232c2c9..ddb216bbed0982abbb2c0483a09bc5ecbbe928a0 100644 --- a/app/src/News/Dto/NewsListDto.php +++ b/app/src/News/Dto/NewsListDto.php @@ -2,19 +2,19 @@ namespace App\News\Dto; -use App\Shared\Collection\ListingCollection; +use App\News\DtoCollection\NewsListingElementCollection; use App\Shared\Dto\PaginationDto; class NewsListDto { /** * @param PaginationDto $pagination - * @param ListingCollection $list + * @param NewsListingElementCollection $list * @param NewsFilterVariantsDto $filterVariants */ public function __construct( public PaginationDto $pagination, - public ListingCollection $list, + public NewsListingElementCollection $list, public NewsFilterVariantsDto $filterVariants, ) { } diff --git a/app/src/News/DtoCollection/NewsListingElementCollection.php b/app/src/News/DtoCollection/NewsListingElementCollection.php new file mode 100644 index 0000000000000000000000000000000000000000..0c266f19c82bef4ff01267f77887ba74d16376b2 --- /dev/null +++ b/app/src/News/DtoCollection/NewsListingElementCollection.php @@ -0,0 +1,16 @@ +collectionType, $data); + } +} \ No newline at end of file diff --git a/app/src/News/DtoFactory/NewsListDtoFactory.php b/app/src/News/DtoFactory/NewsListDtoFactory.php index 25613a79836d08f7cd125ebfbb0fb34c49b35f6c..4a0efcd8a0d5984ae1a78de8de26948e12177904 100644 --- a/app/src/News/DtoFactory/NewsListDtoFactory.php +++ b/app/src/News/DtoFactory/NewsListDtoFactory.php @@ -4,14 +4,14 @@ namespace App\News\DtoFactory; use App\News\Dto\NewsFilterVariantsDto; use App\News\Dto\NewsListDto; -use App\Shared\Collection\ListingCollection; +use App\News\DtoCollection\NewsListingElementCollection; use App\Shared\Dto\PaginationDto; class NewsListDtoFactory { public function create( PaginationDto $pagination, - ListingCollection $list, + NewsListingElementCollection $list, NewsFilterVariantsDto $filters, ): NewsListDto { return new NewsListDto( diff --git a/app/src/News/DtoFactory/NewsListingElementDtoFactory.php b/app/src/News/DtoFactory/NewsListingElementDtoFactory.php index 11d4412c17436b51ae6a2a1a3b05085fd99cd93d..6837bf2c253c9c5567b9bf06d81c72d779867ef3 100644 --- a/app/src/News/DtoFactory/NewsListingElementDtoFactory.php +++ b/app/src/News/DtoFactory/NewsListingElementDtoFactory.php @@ -3,7 +3,7 @@ namespace App\News\DtoFactory; use App\News\Dto\NewsListingElementDto; -use App\Shared\Collection\ListingCollection; +use App\News\DtoCollection\NewsListingElementCollection; use App\Shared\DtoFactory\FileDtoFactory; use App\Shared\Entity\News; use Ramsey\Collection\Collection; @@ -31,11 +31,11 @@ class NewsListingElementDtoFactory /** * @param Collection $news - * @return ListingCollection + * @return NewsListingElementCollection */ - public function createCollection(Collection $news): ListingCollection + public function createCollection(Collection $news): NewsListingElementCollection { - return new ListingCollection( + return new NewsListingElementCollection( $news->map(function (News $oneNews) { return $this->create($oneNews); }) diff --git a/app/src/News/EntityFabric/NewsEntityFabric.php b/app/src/News/EntityFabric/NewsEntityFabric.php new file mode 100644 index 0000000000000000000000000000000000000000..65f36c8dc2014eb2113327d4107c38ab11ffe50d --- /dev/null +++ b/app/src/News/EntityFabric/NewsEntityFabric.php @@ -0,0 +1,119 @@ + */ + private readonly Collection $newsSetters; + + public function __construct( + private readonly NewsRepository $newsRepository, + private readonly NewsTypeRepository $newsTypeRepository, + private readonly NewsCategoriesRepository $categoriesRepository, + private readonly FileRepository $fileRepository, + ) { + $this->newsSetters = $this->getSetters(); + } + + public function create(NewsCreateRequest $request): News + { + $news = new News(); + $news->setId(Uuid::uuid4()->toString()); + + foreach ($request as $key => $value) { + if ($value !== null) { + $this->newsSetters[$key]($value, $news); + } + } + + return $news; + } + + public function softUpdate(NewsFullUpdateRequest $request): News + { + $news = $this->newsRepository->find($request->id) ?? new News(); + + foreach ($request as $key => $value) { + if ($value !== null) { + $this->newsSetters[$key]($value, $news); + } + } + + return $news; + } + + public function hardUpdate(NewsPartUpdateRequest $request): News + { + $news = $this->newsRepository->find($request->id); + + if ($news === null) { + throw new NotFoundError('News entity for update not found'); + } + + foreach ($request as $key => $value) { + if ($value !== null) { + $this->newsSetters[$key]($value, $news); + } + } + + return $news; + } + + private function getSetters(): Collection + { + $newsSetters = new Collection('callable'); + + $newsSetters['id'] = function (string $id, News $news) { + $news->setId($id); + }; + $newsSetters['name'] = function (string $value, News $news) { + $news->setName($value); + }; + $newsSetters['code'] = function (string $value, News $news) { + $news->setCode($value); + }; + $newsSetters['active'] = function (bool $value, News $news) { + $news->setActive($value); + }; + $newsSetters['main_page_render'] = function (bool $value, News $news) { + $news->setMainPageRender($value); + }; + $newsSetters['preview_text'] = function (string $value, News $news) { + $news->setPreviewText($value); + }; + $newsSetters['detail_text'] = function (string $value, News $news) { + $news->setDetailText($value); + }; + $newsSetters['type_id'] = function (string $id, News $news) { + $news->setType($this->newsTypeRepository->find($id)); + }; + $newsSetters['categories_id'] = function (Collection $ids, News $news) { + foreach ($ids as $id) { + $news->addCategory( + $this->categoriesRepository->find($id) + ); + } + }; + $newsSetters['detail_image'] = function (string $id, News $news) { + $news->setDetailImage($this->fileRepository->find($id)); + }; + $newsSetters['preview_image'] = function (string $id, News $news) { + $news->setPreviewImage($this->fileRepository->find($id)); + }; + + return $newsSetters; + } +} diff --git a/app/src/News/Request/NewsCreateRequest.php b/app/src/News/Request/NewsCreateRequest.php index 084d87468120165a0977620b8440df592a448853..87298f0ec375f3402fd2c819ad5b586642c3d11a 100644 --- a/app/src/News/Request/NewsCreateRequest.php +++ b/app/src/News/Request/NewsCreateRequest.php @@ -3,7 +3,6 @@ namespace App\News\Request; use App\Shared\Abstraction\AbstractRequest; -use Ramsey\Collection\Collection; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Uuid; @@ -25,26 +24,24 @@ class NewsCreateRequest extends AbstractRequest #[Required] #[Type('bool')] - public bool $main_page_render; + public $main_page_render; - #[Uuid] - public ?string $preview_text; + public $preview_text; - #[Uuid] - public ?string $detail_text; + public $detail_text; #[Uuid] - public ?string $type_id; + public $type_id; #[Type('array')] #[All( new Uuid() )] - public$categories_id; + public $categories_id; #[Uuid] - public ?string $detail_image; + public $detail_image; #[Uuid] - public ?string $preview_image; + public $preview_image; } diff --git a/app/src/News/Request/NewsFullUpdateRequest.php b/app/src/News/Request/NewsFullUpdateRequest.php index b3b994aac7ab828e37e8d0d42faca18b6bbbddc4..0ec6fc903ed90ebb794f6727213ce48dc90d2b1c 100644 --- a/app/src/News/Request/NewsFullUpdateRequest.php +++ b/app/src/News/Request/NewsFullUpdateRequest.php @@ -3,7 +3,6 @@ namespace App\News\Request; use App\Shared\Abstraction\AbstractRequest; -use Ramsey\Collection\Collection; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Uuid; @@ -13,7 +12,7 @@ class NewsFullUpdateRequest extends AbstractRequest { #[Required] #[Uuid] - public $uuid; + public $id; #[Required] #[Type('string')] @@ -31,10 +30,8 @@ class NewsFullUpdateRequest extends AbstractRequest #[Type('bool')] public $main_page_render; - #[Uuid] public $preview_text; - #[Uuid] public $detail_text; #[Uuid] diff --git a/app/src/News/Request/NewsPartUpdateRequest.php b/app/src/News/Request/NewsPartUpdateRequest.php index b0a14239ee66efd3599f9c0252ca07ad107dfbb0..1a75fb8b51cc435d7d936f71104a788af9a12a05 100644 --- a/app/src/News/Request/NewsPartUpdateRequest.php +++ b/app/src/News/Request/NewsPartUpdateRequest.php @@ -3,7 +3,6 @@ namespace App\News\Request; use App\Shared\Abstraction\AbstractRequest; -use Ramsey\Collection\Collection; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Uuid; @@ -12,7 +11,8 @@ use Symfony\Contracts\Service\Attribute\Required; class NewsPartUpdateRequest extends AbstractRequest { #[Uuid] - public $uuid; + #[Required] + public $id; #[Type('string')] public $name; @@ -26,19 +26,17 @@ class NewsPartUpdateRequest extends AbstractRequest #[Type('bool')] public bool $main_page_render; - #[Uuid] public $preview_text; - #[Uuid] public $detail_text; #[Uuid] - public $type_uuid; + public $type_id; #[All( new Uuid() )] - public $categories_uuid; + public $categories_id; #[Uuid] public $detail_image; diff --git a/app/src/News/Service/NewsPrepareResponseService.php b/app/src/News/Service/NewsPrepareResponseService.php index b652b1d9970390c8dfa24409f88614501e67703b..7836146b6d3efaa1d7a0f2d1f0ddf442f504fd50 100644 --- a/app/src/News/Service/NewsPrepareResponseService.php +++ b/app/src/News/Service/NewsPrepareResponseService.php @@ -10,8 +10,12 @@ use App\News\DtoFactory\NewsDetailElementDtoFactory; use App\News\DtoFactory\NewsFilterVariantsDtoFactory; use App\News\DtoFactory\NewsListDtoFactory; use App\News\DtoFactory\NewsListingElementDtoFactory; +use App\News\EntityFabric\NewsEntityFabric; +use App\News\Request\NewsCreateRequest; use App\News\Request\NewsDetailRequest; +use App\News\Request\NewsFullUpdateRequest; use App\News\Request\NewsListingRequest; +use App\News\Request\NewsPartUpdateRequest; use App\Shared\DtoFactory\PaginationDtoFactory; use App\Shared\Error\NotFoundError; use App\Shared\Repository\NewsCategoriesRepository; @@ -27,12 +31,15 @@ class NewsPrepareResponseService private readonly NewsFilterVariantsDtoFactory $filterFactory, private readonly NewsListDtoFactory $listingFactory, private readonly NewsCategoryDtoFactory $categoryFactory, - private readonly NewsDetailElementDtoFactory $detailElementFactory + private readonly NewsDetailElementDtoFactory $detailElementFactory, + private readonly NewsEntityFabric $entityFabric, ) { } - public function bornListDto(NewsListingRequest $request): NewsListDto - { + public + function bornListDto( + NewsListingRequest $request + ): NewsListDto { $countOfNews = $this->news->getCountWithFilters( $request->news_category ); @@ -69,17 +76,21 @@ class NewsPrepareResponseService ); } - public function bornMainNews(): NewsListingElementDto + public + function bornMainNews(): NewsListingElementDto { return $this->listFactory->create($this->news->getMainNews()); } - public function bornDetailMainNews(): NewsDetailElementDto + public + function bornDetailMainNews(): NewsDetailElementDto { return $this->detailElementFactory->create($this->news->getMainNews()); } - public function bornDetailElement(NewsDetailRequest $request + public + function bornDetail( + NewsDetailRequest $request ): NewsDetailElementDto { $news = $this->news->find($request->detailId); @@ -89,4 +100,34 @@ class NewsPrepareResponseService return $this->detailElementFactory->create($news); } + + public + function unbirth( + NewsDetailRequest $request + ) { + $news = $this->news->find($request->detailId); + if ($news === null) { + throw new NotFoundError('News not found'); + } + + $this->news->remove($news); + } + + public function rebirth(NewsPartUpdateRequest $request):void + { + $news = $this->entityFabric->hardUpdate($request); + $this->news->createUpdate($news); + } + + public function reborn(NewsFullUpdateRequest $request): void + { + $news = $this->entityFabric->softUpdate($request); + $this->news->createUpdate($news); + } + + public function birth(NewsCreateRequest $request): void + { + $news = $this->entityFabric->create($request); + $this->news->createUpdate($news); + } } diff --git a/app/src/News/UseCase/NewsCreateUseCase.php b/app/src/News/UseCase/NewsCreateUseCase.php new file mode 100644 index 0000000000000000000000000000000000000000..28d045d168390ee70f4709789f75f321ee2a9d8f --- /dev/null +++ b/app/src/News/UseCase/NewsCreateUseCase.php @@ -0,0 +1,27 @@ +prepareRequestService->birth($request); + return new Response(); + } catch (Throwable) { + return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/app/src/News/UseCase/NewsDeleteUseCase.php b/app/src/News/UseCase/NewsDeleteUseCase.php new file mode 100644 index 0000000000000000000000000000000000000000..b927794aae3eb9c4bcecd4090717840b4506c712 --- /dev/null +++ b/app/src/News/UseCase/NewsDeleteUseCase.php @@ -0,0 +1,27 @@ +prepareRequestService->unbirth($request); + return new Response(); + } catch (Throwable) { + return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/app/src/News/UseCase/NewsFullUpdateUseCase.php b/app/src/News/UseCase/NewsFullUpdateUseCase.php new file mode 100644 index 0000000000000000000000000000000000000000..2d4075cde75788f0ec7be552071818c12405e86a --- /dev/null +++ b/app/src/News/UseCase/NewsFullUpdateUseCase.php @@ -0,0 +1,27 @@ +prepareRequestService->reborn($request); + return new Response(); + } catch (Throwable) { + return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } +} diff --git a/app/src/News/UseCase/NewsGetDetailMainUseCase.php b/app/src/News/UseCase/NewsGetDetailMainUseCase.php index 92725a121c29184bc53d4071b17c548a4e1ec7c4..a59f9580c7baef004c90d4741f29792c8b36355a 100644 --- a/app/src/News/UseCase/NewsGetDetailMainUseCase.php +++ b/app/src/News/UseCase/NewsGetDetailMainUseCase.php @@ -14,7 +14,7 @@ class NewsGetDetailMainUseCase ) { } - public function execute(): ?JsonResponse + public function execute(): JsonResponse { try { return new JsonResponse( @@ -24,4 +24,4 @@ class NewsGetDetailMainUseCase return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR); } } -} \ No newline at end of file +} diff --git a/app/src/News/UseCase/NewsGetDetailUseCase.php b/app/src/News/UseCase/NewsGetDetailUseCase.php index 2605bd94f84d89844e02a0089589e2190969bca5..0127279ce9d0789317c0ae83cd94a2463163d559 100644 --- a/app/src/News/UseCase/NewsGetDetailUseCase.php +++ b/app/src/News/UseCase/NewsGetDetailUseCase.php @@ -22,7 +22,7 @@ class NewsGetDetailUseCase { try { return new JsonResponse( - $this->responsePrepareService->bornDetailElement($request) + $this->responsePrepareService->bornDetail($request) ); } catch (NotFoundError $error) { $errorDto = $this->errorFactory->create($error); diff --git a/app/src/News/UseCase/NewsPartUpdateUseCase.php b/app/src/News/UseCase/NewsPartUpdateUseCase.php new file mode 100644 index 0000000000000000000000000000000000000000..2055035916a01c8230a1adaf9af6284f257cd78f --- /dev/null +++ b/app/src/News/UseCase/NewsPartUpdateUseCase.php @@ -0,0 +1,27 @@ +prepareRequestService->rebirth($request); + return new Response(); + } catch (Throwable) { + return new JsonResponse([], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } +} \ No newline at end of file diff --git a/app/src/Restaurants/Request/RestaurantFullUpdateRequest.php b/app/src/Restaurants/Request/RestaurantFullUpdateRequest.php index ee81e8a2ebf3c1350ca8725569ce44aff9183e26..74f52f9d365bc15b4d9e3f111905dbc7d8a5205c 100644 --- a/app/src/Restaurants/Request/RestaurantFullUpdateRequest.php +++ b/app/src/Restaurants/Request/RestaurantFullUpdateRequest.php @@ -12,7 +12,7 @@ class RestaurantFullUpdateRequest extends AbstractRequest { #[Uuid] #[Required] - public $uuid; + public $id; #[Type('bool')] #[Required] diff --git a/app/src/Restaurants/Request/RestaurantPartUpdateRequest.php b/app/src/Restaurants/Request/RestaurantPartUpdateRequest.php index b615abae1aeb563eb90dc0d985f1a43f9e7322ab..21f9c57e6aef1f39541388754a1d1683a98908bc 100644 --- a/app/src/Restaurants/Request/RestaurantPartUpdateRequest.php +++ b/app/src/Restaurants/Request/RestaurantPartUpdateRequest.php @@ -6,7 +6,6 @@ use App\Shared\Abstraction\AbstractRequest; use Symfony\Component\Validator\Constraints\All; use Symfony\Component\Validator\Constraints\Type; use Symfony\Component\Validator\Constraints\Uuid; -use Symfony\Contracts\Service\Attribute\Required; class RestaurantPartUpdateRequest extends AbstractRequest { @@ -26,7 +25,7 @@ class RestaurantPartUpdateRequest extends AbstractRequest public $code; #[Type('string')] - public $receipt; + public $check; #[Type('string')] public $receipt_info; diff --git a/app/src/Shared/Collection/ListingCollection.php b/app/src/Shared/Collection/ListingCollection.php deleted file mode 100644 index 9234de7c7b0f995f6d7451149ce31f361fab1005..0000000000000000000000000000000000000000 --- a/app/src/Shared/Collection/ListingCollection.php +++ /dev/null @@ -1,15 +0,0 @@ -collectionType, $data); - } -} diff --git a/app/src/Shared/Collection/ValidationErrorCollection.php b/app/src/Shared/Collection/ValidationErrorCollection.php index 346ae1ad35c8ec3f010e529a94da36f4cdb913a0..4b328ed9a5a2ad91bb39b531c8e5a2588967c813 100644 --- a/app/src/Shared/Collection/ValidationErrorCollection.php +++ b/app/src/Shared/Collection/ValidationErrorCollection.php @@ -2,12 +2,11 @@ namespace App\Shared\Collection; -use App\Shared\Dto\ErrorDto; -use App\Shared\Dto\FileDto; +use App\Shared\Dto\ValidateErrorDto; class ValidationErrorCollection extends DtoCollection { - private string $collectionType = ErrorDto::class; + private string $collectionType = ValidateErrorDto::class; public function __construct(array $data = []) { diff --git a/app/src/Shared/Entity/News.php b/app/src/Shared/Entity/News.php index d88a733362e78f0119bb8c0c6f08a167f536367d..8d891bab739883933b73e7dab5885b554c588535 100644 --- a/app/src/Shared/Entity/News.php +++ b/app/src/Shared/Entity/News.php @@ -3,12 +3,14 @@ namespace App\Shared\Entity; use App\Shared\Repository\NewsRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: NewsRepository::class)] +#[ORM\HasLifecycleCallbacks] class News { #[ORM\Id] @@ -242,4 +244,17 @@ class News return $this; } -} + + #[ORM\PrePersist] + public function setCreatedAtValue(): void + { + $this->createdAt = new DateTimeImmutable(); + $this->updateAt = new DateTimeImmutable(); + } + + #[ORM\PreUpdate] + public function setUpdateAtValue(): void + { + $this->updateAt = new DateTimeImmutable(); + } +} \ No newline at end of file diff --git a/app/src/Shared/EventListener/NewsPerCreate.php b/app/src/Shared/EventListener/NewsPerCreate.php new file mode 100644 index 0000000000000000000000000000000000000000..41a5332f13626d519d448613c559dde92f9205de --- /dev/null +++ b/app/src/Shared/EventListener/NewsPerCreate.php @@ -0,0 +1,28 @@ +getSort() === null) { + $news->setSort($news->getSort() + 1); + } + + if ($news->isMainPageRender()) { + $mainNews = $this->newsRepository->getMainNews(); + $mainNews?->setMainPageRender(false); + } + } +} diff --git a/app/src/Shared/Repository/NewsRepository.php b/app/src/Shared/Repository/NewsRepository.php index 4dfe2295098538879780a447dec6589198550bb4..4b55634282513c354fd9bfeacdaf71ac77d669d6 100644 --- a/app/src/Shared/Repository/NewsRepository.php +++ b/app/src/Shared/Repository/NewsRepository.php @@ -24,13 +24,42 @@ class NewsRepository extends ServiceEntityRepository parent::__construct($registry, News::class); } - public function getMainNews(): News + public function createUpdate(News $news): void + { + $entityManager = $this->getEntityManager(); + $entityManager->persist($news); + $entityManager->flush(); + $entityManager->clear(); + } + + public function remove(News $news): void + { + $this->getEntityManager()->remove($news); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); + } + + public function getMaxValueSort(): int { return $this->createQueryBuilder('n') + ->select('MAX(n.sort)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function getMainNews(): ?News + { + $mainNews = $this->createQueryBuilder('n') ->andWhere('n.mainPageRender = true') ->setMaxResults(1) ->getQuery() - ->getResult()[0]; + ->getResult(); + + if ($mainNews === []) { + return null; + } + + return $mainNews[0]; } public function getCountWithFilters(?string $categoryId): int