Symfony Doctrine построитель запросов находит сущность с отношением «многие к одному» - PullRequest
1 голос
/ 19 марта 2020

Я создаю веб-сайт с Symfony для проекта, который будет действовать как веб-сайт "booking.com", но намного проще и с некоторыми вариациями.

Некоторые ошибки в полях базы данных, но не очень важные для моего вопроса.

enter image description here

Как видите, эти таблицы касаются двух сущностей: квартира и визит. Клиент может попросить посетить квартиру. Это отношения много к одному.

У меня есть форма поиска для поиска квартир с критериями. Я хочу показывать только квартиры, которые не посещались между датами прибытия и отъезда, которые предоставил пользователь. Так что я закончил тем, что сделал в ApartmentRepository функцию для управления этим и другими делами.

Проблема в том, как я могу получить эти квартиры?

Вот черновик этой функции, который, конечно, не закончен и не идеален (если у вас есть какие-то комментарии, чтобы улучшить его, это было бы здорово!).

public function findByCustom($parameters): ?array
{
   $query =  $this->createQueryBuilder('a');

   foreach ($parameters as $key=> $parameter){
       if($key != 'keywords' and $key!= 'priceMin' & $key!='priceMax' and $key!="typeAp" and $key!="searchVisitDate") $query->orWhere('a.'.$key." = '".$parameter."'");

       if($key == "typeAp")
       {
           $typeApQuery = "";
           foreach ($parameters[$key] as $index => $type)
           {
               if($index !== count($parameters[$key])-1)
               {
                   $typeApQuery.=" a.typeAp = '".$type."' or";
               }
               else
               {
                   $typeApQuery.= " a.typeAp = '".$type."'";
               }
           }
           $query->andWhere($typeApQuery);
       }
   }

   $query->andWhere('a.rentPrice >='.$parameters['priceMin']." and a.rentPrice <= ".$parameters['priceMax']);
   $withoutInner = $query;
   $query
       ->join('App\Entity\Visit', 'v', Join::ON, 'a = v.apartment')
       ->where("v.date between '2020-03-15' and '2020-03-19'");
    $query->getQuery()->getResult();
    $sorted = $withoutInner->andWhere($this->createQueryBuilder('a')->expr()->notIn('a.id', $query));

   return array($sorted);

Конечно, квартира имеет коллекцию посещений и посещений как поле под названием «квартира», которое относится к объекту квартиры.

Я действительно не нашел способ сделать это, и я хочу избежать SQL, чтобы улучшить мое понимание Doctrine.

Спасибо за вашу помощь, потому что я застрял прямо сейчас: /

РЕДАКТИРОВАТЬ 1: забыл упомянуть, что я хочу получить квартиры, которые не посещают между требуемыми датами или что дона вообще не было посещений

РЕДАКТИРОВАТЬ 2:

public function findByCustom($parameters): ?array
{
   $query =  $this->createQueryBuilder('a');
    $withoutInner = $this->createQueryBuilder("a");

   foreach ($parameters as $key=> $parameter){
       if($key != 'keywords' and $key!= 'priceMin' & $key!='priceMax' and $key!="typeAp" and $key!="searchVisitDate")
       {
           $withoutInner->orWhere('a.'.$key." = '".$parameter."'");
           $query->orWhere('a.'.$key." = '".$parameter."'");
       }


       if($key == "typeAp")
       {
           $typeApQuery = "";
           foreach ($parameters[$key] as $index => $type)
           {
               if($index !== count($parameters[$key])-1)
               {
                   $typeApQuery.=" a.typeAp = '".$type."' or";
               }
               else
               {
                   $typeApQuery.= " a.typeAp = '".$type."'";
               }
           }
           $withoutInner->andWhere($typeApQuery);
           $query->andWhere($typeApQuery);
       }
   }

   $query->andWhere('a.rentPrice >='.$parameters['priceMin']." and a.rentPrice <= ".$parameters['priceMax']);
   $withoutInner->andWhere('a.rentPrice >='.$parameters['priceMin']." and a.rentPrice <= ".$parameters['priceMax']);
   $query
       ->join('App\Entity\Visit', 'v', Join::WITH, 'a.id = v.apartment')
       ->where("v.date between '2020-03-15' and '2020-03-19'");

    $query->getQuery()->getResult();
    $sorted = $withoutInner->andWhere($this->createQueryBuilder('a')->expr()->notIn('a.id', $query));

С помощью этой функции я получаю doctrine ошибку:

Error: Method Doctrine\Common\Collections\ArrayCollection::__toString() must not throw an exception, caught ErrorException: Catchable Fatal Error: Object of class Doctrine\ORM\EntityManager could not be converted to string

Ответы [ 2 ]

1 голос
/ 19 марта 2020

Я не проверял это сам, но что-то вроде этого должно работать:

$query
    ->leftJoin('App\Entity\Visit', 'v', Join::WITH, 'a = v.apartment AND v.date between "2020-03-15" and "2020-03-19"')
    ->where('a.visits IS EMPTY');

Идея состоит в том, чтобы использовать leftJoin и выбирать только те результаты, которые не имеют соответствующей записи в visit Таблица.

0 голосов
/ 22 марта 2020

Я нашел решение, чтобы получить все квартиры, которые не имеют арендной платы (где объединение является нулевым). Да, я изменил визиты для аренды, потому что это было ошибкой в ​​моем основном вопросе.

 $query->leftJoin('a.rents', 'r',Join::WITH,'a = r.apartment')
        ->where(
            $query->expr()->andX($query->expr()->isNull('r')))
        ->orWhere($query->expr()-> **);

У меня есть атрибут в моей квартире, который имеет все арендные ставки текущей квартиры. Поэтому я использую это, чтобы сделать соединение. С выражением isNull я заставляю его работать на квартиры без посещения.

**: Я хочу иметь возможность получить квартиры, в которых нет мест, где пользователь указал требуемую дату прибытия не между r.arrival и r.departure. Это дало бы мне все квартиры без арендной платы и те, которые можно бесплатно забронировать.

Я думал о том, чтобы сделать еще один запрос и сделать notIn, но я не знаю, как это сделать.

Спасибо.

РЕДАКТИРОВАТЬ и решение:

Я сам узнал, как это сделать. Это абсолютно не лучший подход и не лучшее решение, но у меня нет времени для моего проекта, поэтому мне нужно было это сделать.

$rents = $this->getEntityManager()->createQueryBuilder()
        ->select("a.id")->from('App:Rent', 'r')
        ->andWhere('r.arrival between :arrival and :departure')
        ->leftJoin('r.apartment', 'a')
        ->setParameters(array("arrival"=>\DateTime::createFromFormat('Y-m-d', "2020-04-20")->setTime(00,00,00), "departure"=>\DateTime::createFromFormat('Y-m-d',"2020-04-31")->setTime(00,00,00)))
        ->getQuery()->getArrayResult();


    $rentsSorted = array();
    foreach ($rents as $rent)
    {
        if(!in_array( $rent['id'],$rentsSorted))
        {
            $rentsSorted[] = $rent['id'];
        }
    }
    if(count($rentsSorted)>0)
    {
        $withRentsNotBetweenDates->andWhere('a1.rentPrice >=' . $parameters['priceMin'] . " and a1.rentPrice <= " . $parameters['priceMax'])
            ->andWhere($withRentsNotBetweenDates->expr()->notIn('a1.id', $rentsSorted));
    }
    else
    {
        $withRentsNotBetweenDates->andWhere('a1.rentPrice >=' . $parameters['priceMin'] . " and a1.rentPrice <= " . $parameters['priceMax']);
    }

Конечно, я собираюсь изменить параметры до требуемых дат, указанных в моей форме.

Этот код позволяет мне получать все арендные ставки, которые существуют в эти требуемые даты. Затем я сохраняю каждый идентификатор квартиры в массиве, который я передам в не для исключения этих квартир, потому что у них есть арендная плата в этот период.

Если у вас есть какие-либо недостатки или лучший способ сделать это, не стесняйтесь xD.

Хорошего дня.

...