برنامه نویسی

به راحتی معیارهایی را به سوالات دکترین خود اضافه کنید

در این پست، من می خواهم به شما نشان دهم که چگونه می توانیم به راحتی معیارهایی را به سوالات دکترین خود اضافه کنیم.
بیایید با ایجاد مدلی شروع کنیم که داده‌هایی را که می‌خواهیم به عنوان معیار به پرس و جو اضافه کنیم را در خود نگه می‌دارد:

class ListContractsInput
{
    #[Assert\DateTime(message: 'Start at must be a valid datetime')]
    private ?string $startAt = null;

    #[Assert\DateTime(message: 'End at must be a valid datetime')]
    private ?string $endAt = null;

    public function getStartAt(): ?string
    {
        return $this->startAt;
    }

    public function setStartAt(?string $startAt): void
    {
        $this->startAt = $startAt;
    }

    public function getEndAt(): ?string
    {
        return $this->endAt;
    }

    public function setEndAt(?string $endAt): void
    {
        $this->endAt = $endAt;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که در این مدل می بینیم، ثابت است شروع کن در و خود در مقادیر و لازم است که آنها تاریخ معتبر باشند. اطلاعات بیشتر در مورد اعتبار سنجی سیمفونی در اینجا.

اکنون ببینیم چگونه می‌توانیم این داده‌های ورودی را پردازش کنیم تا آن‌ها را به عنوان معیاری به جستارهای خود اضافه کنیم.

ایجاد یک کلاس پایه که داده های ورودی را پردازش می کند

abstract class QueryBuilderCriteriaManager
{
    private SerializerInterface $serializer;

    public function __construct()
    {
        $this->serializer = new Serializer([new ObjectNormalizer()]);
    }

    /**
     * @throws ExceptionInterface
     */
    public function addCriteria(QueryBuilder $qb, string $alias, iterable|object $filters): void
    {
        if(!is_object($filters)){
            foreach ($filters as $key => $value) {
                $this->addToQb($qb, $alias, $key, $value);
            }
        }
        else{
            $criteriaData = $this->serializer->normalize($filters);
            foreach ($criteriaData as $propName => $value) {
                $this->addToQb($qb, $alias, $propName, $value);
            }
        }
    }

    protected function addToQb(QueryBuilder $qb, string $alias, string $key, mixed $value): void
    {
        $method = u('get_' . $key . 'Criteria')->camel()->toString();

        if( !empty($value) || $value === 0 || $value === '0' || $value === false) {
            if(method_exists($this, $method)){
                $this->$method($qb, $alias, $value);
            }
            else{
                $qb
                    ->andWhere($qb->expr()->eq("{$alias}.{$key}", ':' . $key))
                    ->setParameter($key, $value)
                ;
            }
        }

    }

    /**
     * @throws \Exception
     */
    protected function getAsDateTime(string|\DateTimeImmutable $date): \DateTimeImmutable
    {
        return ($date instanceof \DateTimeImmutable)
            ? $date
            : new \DateTimeImmutable($date)
            ;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید این کد را مرحله به مرحله تجزیه و تحلیل کنیم:

private SerializerInterface $serializer;

public function __construct()
{
    $this->serializer = new Serializer([new ObjectNormalizer()]);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اول از همه، ما یک سریال‌ساز سیمفونی در سازنده می‌سازیم زیرا در خطوط بعدی به آن نیاز خواهیم داشت.

public function addCriteria(QueryBuilder $qb, string $alias, iterable|object $filters): void
{
    if(!is_object($filters)){
         foreach ($filters as $key => $value) {
            $this->addToQb($qb, $alias, $key, $value);
         }
    }
    else{
         $criteriaData = $this->serializer->normalize($filters);
         foreach ($criteriaData as $propName => $value) {
            $this->addToQb($qb, $alias, $propName, $value);
         }
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون روش ایجاد می کنیم افزودن معیارها که پارامترهای زیر را دریافت می کند:

  • $qb: شی QueryBuilder که معیارهایی را به آن اضافه می کنیم.
  • نام مستعار $: نام مستعار موجودیت اصلی را جستجو کنید
  • $ فیلترها: معیارهایی که قرار است اضافه کنیم. می تواند یک شی (مانند مدل ما) یا یک تکرار باشد

اگر $ فیلترها پارامتر یک تکرارپذیر است، به عنوان کلید / مقادیر و روش استفاده روی تکرار شونده حلقه می‌زند addToQb برای اضافه کردن معیارها

اگر $ فیلترها پارامتر یک شی است، ابتدا شی را به یک آرایه سریال می کند و سپس آن را به عنوان کلید / مقدار حلقه می کند و همچنین استفاده می کند. addToQb برای اضافه کردن معیارها

protected function addToQb(QueryBuilder $qb, string $alias, string $key, mixed $value): void
{
    $method = u('get_' . $key . 'Criteria')->camel()->toString();

    if( !empty($value) || $value === 0 || $value === '0' || $value === false) {
        if(method_exists($this, $method)){
            $this->$method($qb, $alias, $value);
        }
        else{
            $qb
               ->andWhere($qb->expr()->eq("{$alias}.{$key}", ':' . $key))
               ->setParameter($key, $value)
            ;
        }
    }

}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

روش addToQb پارامترهای زیر را دریافت می کند:

  • $qb: سازنده پرس و جو که معیارهایی را به آن اضافه می کنیم
  • نام مستعار $: نام مستعار موجودیت اصلی را جستجو کنید
  • کلید $: فیلد (ویژگی موجودیت) را فیلتر می کنیم
  • ارزش دلار: مقداری که می خواهیم با آن فیلتر کنیم

قبل از توضیح addToQb، من می خواهم برجسته کنم که این کلاس QueryBuilderCriteriaManager یک کلاس انتزاعی است و توسط کلاس های دیگر توسعه می یابد که منطق هر معیاری را که می خواهیم اضافه کنیم تعریف می کند.

هر یک از این کلاس‌های فرزند (در ادامه نمونه‌ای را مشاهده خواهیم کرد) برای هر معیار با این قالب متدی تعریف می‌کند: دریافت{KeyName}معیارها

به عنوان مثال، با توجه به مدلی که در ابتدای این پست نشان دادیم، باید روش ها را تعریف کنیم getStartAtCriteria() و getEndAtCriteria()

با دانستن این موضوع، بیایید ببینیم این روش چگونه کار می کند

  • ابتدا، نام متد را طبق فرمتی که ما به تازگی دیدیم ایجاد می کند.
  • دوم، اگر مقدار صفر نباشد:
    • اگر متد در کلاس فرزند وجود داشته باشد، متدی را که دریافت می کند اجرا می کند QueryBuider،نام مستعار، و ارزش و در نهایت معیارها را اضافه می کند.
    • اگر روش وجود نداشته باشد، معیارها را به عنوان یک شرط مساوی اضافه می کند.

ایجاد کلاس کودک

بیایید ببینیم کلاس کودک ما چگونه به نظر می رسد:

class ContractsCriteriaManager extends QueryBuilderCriteriaManager
{
    /**
     * @throws \Exception
     */
    public function getStartAtCriteria(QueryBuilder $qb, string $alias, string|\DateTimeImmutable $value): void
    {
        $qb
            ->andWhere($qb->expr()->gte("{$alias}.createdAt",':start_at'))
            ->setParameter('start_at', $this->getAsDateTime($value))
        ;
    }

    /**
     * @throws \Exception
     */
    public function getEndAtCriteria(QueryBuilder $qb, string $alias, string|\DateTimeImmutable $value): void
    {
        $qb
            ->andWhere($qb->expr()->lte("{$alias}.createdAt",':end_at'))
            ->setParameter('end_at', $this->getAsDateTime($value))
        ;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که می بینیم، کلاس فرزند دو متد را به دنبال آخرین فرمت تعریف می کند:

  • getStartAtCriteria(): معیاری را اضافه می کند تا ایجاد شده در باید بزرگتر یا مساوی باشد شروع کن در ارزش
  • getEndAtCriteria(): معیاری را اضافه می کند تا ایجاد شده در باید کمتر یا مساوی باشد خود در ارزش

استفاده از آن در مخزن

بیایید نگاهی به روش مخزن زیر بیاندازیم:

public function getList(array|object $criteria, ?int $limit): array
{
     $criteriaManager = new ContractsCriteriaManager();
     $qb = $this->createQueryBuilder(self::ALIAS);

     if($limit){
         $qb->setMaxResults($limit);
     }

     $qb->orderBy(self::ALIAS . '.createdAt', 'desc');
     $criteriaManager->addCriteria($qb, self::ALIAS, $criteria);
     return $qb->getQuery()->getResult();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که می بینید، پس از اضافه کردن حد و سفارش توسط برای نمونه سازنده پرس و جو، از مدیر معیارها استفاده می کنیم تا سازنده پرس و جو خود را با داده های معیارهای نگهداری شده در پر کنیم. معیار $ هدف – شی.

اگر این را اجرا کنیم getList() روش و پرس و جو را اشکال زدایی می کنیم، DQL زیر را می بینیم:

SELECT c FROM App\Entity\Contract c WHERE c.startAt >= :start_at AND c.endAt <= :end_at ORDER BY c.createdAt desc
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر پارامترهای پرس و جو را تخلیه کنیم، آنها به شکل زیر خواهند بود:

Doctrine\Common\Collections\ArrayCollection {#721
  -elements: array:2 [
    0 => Doctrine\ORM\Query\Parameter {#682
      -name: "start_at"
      -value: DateTimeImmutable @1683128700 {#671
        date: 2023-05-03 15:45:00.0 UTC (+00:00)
      }
      -type: "datetime_immutable"
      -typeSpecified: false
    }
    1 => Doctrine\ORM\Query\Parameter {#681
      -name: "end_at"
      -value: DateTimeImmutable @1683304255 {#670
        date: 2023-05-05 16:30:55.0 UTC (+00:00)
      }
      -type: "datetime_immutable"
      -typeSpecified: false
    }
  ]
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این همه است، امیدوارم برای ایجاد رفتار معیارهای سفارشی شما مفید باشد و بتواند پرس و جوهای پیچیده را به روشی جدا از هم ایجاد کند.

می توانید این کد را از مخزن github من دانلود کنید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا