issues/27: filter DTO, strip id from payloads, lifecycle updateAt
This commit is contained in:
committed by
Valeriy Petrov
parent
da5f7bb242
commit
76044381fd
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Article;
|
use App\Entity\Article;
|
||||||
use App\Repository\ArticleRepository;
|
use App\Repository\ArticleRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -37,7 +38,7 @@ final class ArticleController extends AbstractController
|
|||||||
#[Route('/list', name: 'article_list', methods: ['GET'])]
|
#[Route('/list', name: 'article_list', methods: ['GET'])]
|
||||||
public function list(Request $request, ArticleRepository $repository): JsonResponse
|
public function list(Request $request, ArticleRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginateWithLegacyMeta($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginateWithLegacyMeta($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Disease;
|
use App\Entity\Disease;
|
||||||
use App\Repository\DiseaseRepository;
|
use App\Repository\DiseaseRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -36,7 +37,7 @@ final class DiseaseController extends AbstractController
|
|||||||
#[Route('/list', name: 'disease_list', methods: ['GET'])]
|
#[Route('/list', name: 'disease_list', methods: ['GET'])]
|
||||||
public function list(Request $request, DiseaseRepository $repository): JsonResponse
|
public function list(Request $request, DiseaseRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\MedicalCenter;
|
use App\Entity\MedicalCenter;
|
||||||
use App\Repository\MedicalCenterRepository;
|
use App\Repository\MedicalCenterRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -36,7 +37,7 @@ final class MedicalCenterController extends AbstractController
|
|||||||
#[Route('/list', name: 'medical_center_list', methods: ['GET'])]
|
#[Route('/list', name: 'medical_center_list', methods: ['GET'])]
|
||||||
public function list(Request $request, MedicalCenterRepository $repository): JsonResponse
|
public function list(Request $request, MedicalCenterRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\News;
|
use App\Entity\News;
|
||||||
use App\Repository\NewsRepository;
|
use App\Repository\NewsRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -36,7 +37,7 @@ final class NewsController extends AbstractController
|
|||||||
#[Route('/list', name: 'news_list', methods: ['GET'])]
|
#[Route('/list', name: 'news_list', methods: ['GET'])]
|
||||||
public function list(Request $request, NewsRepository $repository): JsonResponse
|
public function list(Request $request, NewsRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Promo;
|
use App\Entity\Promo;
|
||||||
use App\Repository\PromoRepository;
|
use App\Repository\PromoRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -36,7 +37,7 @@ final class PromoController extends AbstractController
|
|||||||
#[Route('/list', name: 'promo_list', methods: ['GET'])]
|
#[Route('/list', name: 'promo_list', methods: ['GET'])]
|
||||||
public function list(Request $request, PromoRepository $repository): JsonResponse
|
public function list(Request $request, PromoRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\SiteService;
|
use App\Entity\SiteService;
|
||||||
use App\Repository\SiteServiceRepository;
|
use App\Repository\SiteServiceRepository;
|
||||||
use App\Service\Crud\CrudResponder;
|
use App\Service\Crud\CrudResponder;
|
||||||
@@ -36,7 +37,7 @@ final class SiteServiceController extends AbstractController
|
|||||||
#[Route('/list', name: 'site_service_list', methods: ['GET'])]
|
#[Route('/list', name: 'site_service_list', methods: ['GET'])]
|
||||||
public function list(Request $request, SiteServiceRepository $repository): JsonResponse
|
public function list(Request $request, SiteServiceRepository $repository): JsonResponse
|
||||||
{
|
{
|
||||||
$qb = $repository->createFilteredQueryBuilder($request->query->all());
|
$qb = $repository->createFilteredQueryBuilder(ContentFilterDto::fromRequest($request));
|
||||||
|
|
||||||
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
return $this->json($this->paginator->paginate($qb, $request), Response::HTTP_OK, [], [
|
||||||
'groups' => self::READ_GROUPS,
|
'groups' => self::READ_GROUPS,
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Content;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
final readonly class ContentFilterDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ?int $regionId = null,
|
||||||
|
public ?bool $active = null,
|
||||||
|
public ?string $alias = null,
|
||||||
|
public ?string $search = null,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fromRequest(Request $request): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
regionId: self::positiveInt($request->query->get('regionId', $request->query->get('region_id'))),
|
||||||
|
active: self::nullableBool($request->query->get('active')),
|
||||||
|
alias: self::nonEmptyString($request->query->get('alias')),
|
||||||
|
search: self::nonEmptyString($request->query->get('search', $request->query->get('q'))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function positiveInt(mixed $value): ?int
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '' || !is_numeric($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = (int) $value;
|
||||||
|
|
||||||
|
return $value > 0 ? $value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nullableBool(mixed $value): ?bool
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_bool($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nonEmptyString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if (!is_string($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
return $value !== '' ? $value : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\ArticleRepository;
|
use App\Repository\ArticleRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -12,8 +13,11 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
#[ORM\Table(name: 'article')]
|
#[ORM\Table(name: 'article')]
|
||||||
#[ORM\Index(name: 'idx_article_region_id', columns: ['region_id'])]
|
#[ORM\Index(name: 'idx_article_region_id', columns: ['region_id'])]
|
||||||
#[ORM\Index(name: 'idx_article_active', columns: ['active'])]
|
#[ORM\Index(name: 'idx_article_active', columns: ['active'])]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class Article
|
class Article
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[Groups(['article:read'])]
|
#[Groups(['article:read'])]
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
@@ -56,7 +60,7 @@ class Article
|
|||||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||||
private ?string $content = null;
|
private ?string $content = null;
|
||||||
|
|
||||||
#[Groups(['article:read', 'article:write'])]
|
#[Groups(['article:read'])]
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Entity\Behavior;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
trait UpdateTimestampTrait
|
||||||
|
{
|
||||||
|
#[ORM\PrePersist]
|
||||||
|
public function setInitialUpdateAt(): void
|
||||||
|
{
|
||||||
|
if ($this->updateAt === null) {
|
||||||
|
$this->updateAt = new \DateTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ORM\PreUpdate]
|
||||||
|
public function refreshUpdateAt(): void
|
||||||
|
{
|
||||||
|
$this->updateAt = new \DateTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\DiseaseRepository;
|
use App\Repository\DiseaseRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -11,8 +12,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||||||
#[ORM\Table(name: 'disease')]
|
#[ORM\Table(name: 'disease')]
|
||||||
#[ORM\Index(name: 'idx_disease_region_id', columns: ['region_id'])]
|
#[ORM\Index(name: 'idx_disease_region_id', columns: ['region_id'])]
|
||||||
#[ORM\Index(name: 'idx_disease_active', columns: ['active'])]
|
#[ORM\Index(name: 'idx_disease_active', columns: ['active'])]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class Disease
|
class Disease
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[Groups(['disease:read'])]
|
#[Groups(['disease:read'])]
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
@@ -43,7 +47,7 @@ class Disease
|
|||||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||||
private ?string $anons = null;
|
private ?string $anons = null;
|
||||||
|
|
||||||
#[Groups(['disease:read', 'disease:write'])]
|
#[Groups(['disease:read'])]
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\MedicalCenterRepository;
|
use App\Repository\MedicalCenterRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -9,8 +10,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||||||
|
|
||||||
#[ORM\Entity(repositoryClass: MedicalCenterRepository::class)]
|
#[ORM\Entity(repositoryClass: MedicalCenterRepository::class)]
|
||||||
#[ORM\Table(name: 'medical_center')]
|
#[ORM\Table(name: 'medical_center')]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class MedicalCenter
|
class MedicalCenter
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
#[ORM\Column(type: Types::INTEGER)]
|
#[ORM\Column(type: Types::INTEGER)]
|
||||||
@@ -42,7 +46,7 @@ class MedicalCenter
|
|||||||
private ?string $content = null;
|
private ?string $content = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
#[Groups(['medical_center:read', 'medical_center:write'])]
|
#[Groups(['medical_center:read'])]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'kod_uslug', type: 'jsonb', nullable: true)]
|
#[ORM\Column(name: 'kod_uslug', type: 'jsonb', nullable: true)]
|
||||||
|
|||||||
+5
-1
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\NewsRepository;
|
use App\Repository\NewsRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -11,8 +12,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||||||
#[ORM\Table(name: 'news')]
|
#[ORM\Table(name: 'news')]
|
||||||
#[ORM\Index(name: 'idx_news_region_id', columns: ['region_id'])]
|
#[ORM\Index(name: 'idx_news_region_id', columns: ['region_id'])]
|
||||||
#[ORM\Index(name: 'idx_news_active', columns: ['active'])]
|
#[ORM\Index(name: 'idx_news_active', columns: ['active'])]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class News
|
class News
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
#[ORM\Column(type: Types::INTEGER)]
|
#[ORM\Column(type: Types::INTEGER)]
|
||||||
@@ -44,7 +48,7 @@ class News
|
|||||||
private ?string $content = null;
|
private ?string $content = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
#[Groups(['news:read', 'news:write'])]
|
#[Groups(['news:read'])]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'link_el_price', type: Types::TEXT, nullable: true)]
|
#[ORM\Column(name: 'link_el_price', type: Types::TEXT, nullable: true)]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\PromoRepository;
|
use App\Repository\PromoRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -11,8 +12,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||||||
#[ORM\Table(name: 'promo')]
|
#[ORM\Table(name: 'promo')]
|
||||||
#[ORM\Index(name: 'idx_promo_region_id', columns: ['region_id'])]
|
#[ORM\Index(name: 'idx_promo_region_id', columns: ['region_id'])]
|
||||||
#[ORM\Index(name: 'idx_promo_active', columns: ['active'])]
|
#[ORM\Index(name: 'idx_promo_active', columns: ['active'])]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class Promo
|
class Promo
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
#[ORM\Column(type: Types::INTEGER)]
|
#[ORM\Column(type: Types::INTEGER)]
|
||||||
@@ -44,7 +48,7 @@ class Promo
|
|||||||
private ?string $content = null;
|
private ?string $content = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
#[Groups(['promo:read', 'promo:write'])]
|
#[Groups(['promo:read'])]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'jsonb', nullable: true)]
|
#[ORM\Column(type: 'jsonb', nullable: true)]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Behavior\UpdateTimestampTrait;
|
||||||
use App\Repository\SiteServiceRepository;
|
use App\Repository\SiteServiceRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -11,8 +12,11 @@ use Symfony\Component\Serializer\Annotation\Groups;
|
|||||||
#[ORM\Table(name: 'site_services')]
|
#[ORM\Table(name: 'site_services')]
|
||||||
#[ORM\Index(name: 'idx_site_services_region_id', columns: ['region_id'])]
|
#[ORM\Index(name: 'idx_site_services_region_id', columns: ['region_id'])]
|
||||||
#[ORM\Index(name: 'idx_site_services_active', columns: ['active'])]
|
#[ORM\Index(name: 'idx_site_services_active', columns: ['active'])]
|
||||||
|
#[ORM\HasLifecycleCallbacks]
|
||||||
class SiteService
|
class SiteService
|
||||||
{
|
{
|
||||||
|
use UpdateTimestampTrait;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
#[ORM\GeneratedValue(strategy: "IDENTITY")]
|
||||||
#[ORM\Column(type: Types::INTEGER)]
|
#[ORM\Column(type: Types::INTEGER)]
|
||||||
@@ -44,7 +48,7 @@ class SiteService
|
|||||||
private ?string $content = null;
|
private ?string $content = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(name: 'update_at', type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
#[Groups(['site_service:read', 'site_service:write'])]
|
#[Groups(['site_service:read'])]
|
||||||
private ?\DateTimeInterface $updateAt = null;
|
private ?\DateTimeInterface $updateAt = null;
|
||||||
|
|
||||||
#[ORM\Column(name: 'link_videoreviews', type: 'jsonb', nullable: true)]
|
#[ORM\Column(name: 'link_videoreviews', type: 'jsonb', nullable: true)]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Article;
|
use App\Entity\Article;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -20,9 +21,8 @@ class ArticleRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('a')->orderBy('a.id', 'DESC');
|
$qb = $this->createQueryBuilder('a')->orderBy('a.id', 'DESC');
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,107 +25,27 @@ use Doctrine\ORM\QueryBuilder;
|
|||||||
trait ContentFilterTrait
|
trait ContentFilterTrait
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
private function applyCommonFilters(QueryBuilder $qb, string $alias, array $filters): void
|
private function applyCommonFilters(QueryBuilder $qb, string $alias, ContentFilterDto $filters): void
|
||||||
{
|
{
|
||||||
$regionId = $this->extractIntFilter($filters, ['regionId', 'region_id']);
|
if ($filters->regionId !== null) {
|
||||||
if ($regionId !== null && $regionId > 0) {
|
|
||||||
$qb->andWhere("$alias.regionId = :regionId")
|
$qb->andWhere("$alias.regionId = :regionId")
|
||||||
->setParameter('regionId', $regionId);
|
->setParameter('regionId', $filters->regionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$active = $this->extractBoolFilter($filters, ['active']);
|
if ($filters->active !== null) {
|
||||||
if ($active !== null) {
|
|
||||||
$qb->andWhere("$alias.active = :active")
|
$qb->andWhere("$alias.active = :active")
|
||||||
->setParameter('active', $active);
|
->setParameter('active', $filters->active);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aliasFilter = $this->extractNonEmptyStringFilter($filters, ['alias']);
|
if ($filters->alias !== null) {
|
||||||
if ($aliasFilter !== null) {
|
|
||||||
$qb->andWhere("$alias.alias = :aliasValue")
|
$qb->andWhere("$alias.alias = :aliasValue")
|
||||||
->setParameter('aliasValue', $aliasFilter);
|
->setParameter('aliasValue', $filters->alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
$search = $this->extractNonEmptyStringFilter($filters, ['search', 'q']);
|
if ($filters->search !== null) {
|
||||||
if ($search !== null) {
|
|
||||||
$qb->andWhere("LOWER($alias.name) LIKE :search")
|
$qb->andWhere("LOWER($alias.name) LIKE :search")
|
||||||
->setParameter('search', '%' . mb_strtolower($search) . '%');
|
->setParameter('search', '%' . mb_strtolower($filters->search) . '%');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
* @param list<string> $keys
|
|
||||||
*/
|
|
||||||
private function extractIntFilter(array $filters, array $keys): ?int
|
|
||||||
{
|
|
||||||
foreach ($keys as $key) {
|
|
||||||
if (!array_key_exists($key, $filters)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = $filters[$key];
|
|
||||||
if ($value === null || $value === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_numeric($value)) {
|
|
||||||
return (int) $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
* @param list<string> $keys
|
|
||||||
*/
|
|
||||||
private function extractBoolFilter(array $filters, array $keys): ?bool
|
|
||||||
{
|
|
||||||
foreach ($keys as $key) {
|
|
||||||
if (!array_key_exists($key, $filters)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = $filters[$key];
|
|
||||||
if ($value === null || $value === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_bool($value)) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
* @param list<string> $keys
|
|
||||||
*/
|
|
||||||
private function extractNonEmptyStringFilter(array $filters, array $keys): ?string
|
|
||||||
{
|
|
||||||
foreach ($keys as $key) {
|
|
||||||
if (!array_key_exists($key, $filters)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = $filters[$key];
|
|
||||||
if (!is_string($value)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trimmed = trim($value);
|
|
||||||
if ($trimmed !== '') {
|
|
||||||
return $trimmed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Disease;
|
use App\Entity\Disease;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -23,9 +24,8 @@ class DiseaseRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('d')->orderBy('d.id', 'ASC');
|
$qb = $this->createQueryBuilder('d')->orderBy('d.id', 'ASC');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\MedicalCenter;
|
use App\Entity\MedicalCenter;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -23,9 +24,8 @@ class MedicalCenterRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('m')->orderBy('m.id', 'DESC');
|
$qb = $this->createQueryBuilder('m')->orderBy('m.id', 'DESC');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\News;
|
use App\Entity\News;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -27,9 +28,8 @@ class NewsRepository extends ServiceEntityRepository
|
|||||||
*
|
*
|
||||||
* Поддерживаемые фильтры: regionId, active (по умолчанию true), alias, search.
|
* Поддерживаемые фильтры: regionId, active (по умолчанию true), alias, search.
|
||||||
*
|
*
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('n')->orderBy('n.id', 'DESC');
|
$qb = $this->createQueryBuilder('n')->orderBy('n.id', 'DESC');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\Promo;
|
use App\Entity\Promo;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -23,9 +24,8 @@ class PromoRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('p')->orderBy('p.id', 'DESC');
|
$qb = $this->createQueryBuilder('p')->orderBy('p.id', 'DESC');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Dto\Content\ContentFilterDto;
|
||||||
use App\Entity\SiteService;
|
use App\Entity\SiteService;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
@@ -23,9 +24,8 @@ class SiteServiceRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
*/
|
*/
|
||||||
public function createFilteredQueryBuilder(array $filters): QueryBuilder
|
public function createFilteredQueryBuilder(ContentFilterDto $filters): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('s')->orderBy('s.id', 'ASC');
|
$qb = $this->createQueryBuilder('s')->orderBy('s.id', 'ASC');
|
||||||
|
|
||||||
|
|||||||
@@ -52,22 +52,17 @@ final class CrudResponder
|
|||||||
string $entityClass,
|
string $entityClass,
|
||||||
array $writeGroups,
|
array $writeGroups,
|
||||||
array $readGroups,
|
array $readGroups,
|
||||||
bool $allowIdFromPayload = false,
|
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
$payload = $this->decodePayload($request);
|
$payload = $this->decodePayload($request);
|
||||||
if ($payload === null) {
|
if ($payload === null) {
|
||||||
return $this->jsonError('Ожидается JSON-объект в теле запроса', Response::HTTP_BAD_REQUEST);
|
return $this->jsonError('Ожидается JSON-объект в теле запроса', Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
unset($payload['id']);
|
||||||
$deserializationPayload = $payload;
|
|
||||||
if (!$allowIdFromPayload) {
|
|
||||||
unset($deserializationPayload['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var T $entity */
|
/** @var T $entity */
|
||||||
$entity = $this->serializer->deserialize(
|
$entity = $this->serializer->deserialize(
|
||||||
$this->encodePayload($deserializationPayload),
|
$this->encodePayload($payload),
|
||||||
$entityClass,
|
$entityClass,
|
||||||
'json',
|
'json',
|
||||||
[
|
[
|
||||||
@@ -78,15 +73,6 @@ final class CrudResponder
|
|||||||
return $this->jsonError('Ошибка десериализации: ' . $e->getMessage(), Response::HTTP_BAD_REQUEST);
|
return $this->jsonError('Ошибка десериализации: ' . $e->getMessage(), Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// По умолчанию публичный CRUD не принимает id от клиента. Если системной
|
|
||||||
// интеграции понадобится внешний id, конкретный вызов должен явно передать true.
|
|
||||||
if ($allowIdFromPayload && isset($payload['id']) && method_exists($entity, 'setId')) {
|
|
||||||
$id = (int) $payload['id'];
|
|
||||||
if ($id > 0) {
|
|
||||||
$entity->setId($id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($validationResponse = $this->validate($entity)) !== null) {
|
if (($validationResponse = $this->validate($entity)) !== null) {
|
||||||
return $validationResponse;
|
return $validationResponse;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user