Проблема с запросом при сравнении дат в проекте на основе Laravel

Я работаю над проектом по аренде квартир. В рамках возможностей приложения владелец квартиры может выбрать дни, в которые квартира будет доступна для сдачи в аренду гостям. Я пишу поисковый запрос, чтобы получить квартиры, где он, среди прочего, проверяет дни, доступные для аренды. У меня есть следующие таблицы: номера, бронирования, дни, day_room (сводная таблица). Поскольку я здесь новичок, пожалуйста, дайте мне знать, если мне нужно опубликовать что-нибудь еще, чтобы вы лучше поняли мою проблему.

В этом запросе, когда я бы написал статическую дату, например, «2024-06-27», в последнем методе Where, она работала бы нормально и давала желаемые результаты, но при использовании $date->format('Y-m-d') это не так. ничего не нахожу. Однако в созданном SQL-запросе я не вижу какой-либо разницы, то есть в формате дат.

    namespace App\Http\Controllers;

    use App\Models\Room;
    use App\Models\Booking;
    use Carbon\CarbonPeriod;
    use Carbon\Carbon;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\DB;

    class SearchController extends Controller
    {
        public function index()
        {
        // Validate data later

        $city = request('city');
        $guests_number = request('guests_number');
        $object_type = request('object_type');
        $checkIn = request('checkIn');
        $checkOut = request('checkOut');

        $interval = CarbonPeriod::create($checkIn, $checkOut);

        $rooms = Room::query()
            ->with(['photos', 'bookings', 'days'])
            ->where('rooms.city_id', '=', $city)
            ->where('rooms.beds_num', '>=', $guests_number)
            ->where('rooms.object_type_id', '=', $object_type)
            ->join('bookings', 'bookings.room_id', '=', 
              'rooms.id')
            ->where(function ($query) use ($checkIn, $checkOut) {
                $query->where('checkIn', '>=', $checkOut)
                    ->orWhere('checkOut', '<=', $checkIn);
            })
            ->whereExists(function ($query) use ($interval) {
                $query
                    ->from('day_room')
                    ->join('days', 'days.id', '=', 'day_room.day_id')
                    ->whereColumn('day_room.room_id', 'rooms.id')
                    ->where(function ($query) use ($interval) {
                        foreach ($interval as $date) {
                            $query->where('days.day', '=', $date->format('Y-m-d'));
                            // when testing if I write static date like here, then it works.
                            // $query->where('days.day', '=', '2024-06-27');
                        }
                    });
            })
            ->get('rooms.*');

        return view('results', ['rooms' => $rooms]);
    }

Какие даты вы принимаете в качестве $checkIn и $checkOut? Включает ли это указанную дату? 27.06.2024

Dhruv Pandya 17.06.2024 19:04

Вместо перебора объекта $interval вы можете использовать предложение whereBetween(). При этом не должно быть никакой разницы между $date->format('Y-m-d') и '2024-06-17', если предположить, что одна итерация вашего $interval действительно содержит эту дату, как предлагает Дхрув.

Tim Lewis 17.06.2024 19:11

Просто передайте объект даты Carbon. Laravel достаточно умен, чтобы преобразовать его соответствующим образом. Убедитесь, что ваша модель выполнена правильно.

miken32 17.06.2024 20:43

Вы можете добавить ->ddRawSql() в конце, чтобы увидеть фактически созданный запрос. Что может быть полезно для отладки и оптимизации. Или используйте что-то вроде debugbar. Я думаю, проблема в запросе, я думаю, вы пытаетесь сопоставить несколько days.day в одной ячейке. Только одна может быть правдой, поэтому, если вы жестко запрограммируете только одну дату, она будет работать.

René 17.06.2024 21:08

В foreach вы вставляете несколько условий, связанных оператором AND: условия никогда не будут удовлетворены. Вместо этого вам следует использовать orWhere(), но, как предложил @TimLewis,whereBetween() является лучшим выбором.

TUPKAP 18.06.2024 04:04

@DhruvPandya, да, при тестировании я использовал 27 июня 2024 г. как $checkIn и 29 июня 2024 г. как $checkOut. Обе даты доступны в таблице БД.

Makhmud 18.06.2024 17:21

@TimLewis, я пробовал разные способы использованияwhereBetween() в моем запросе, ни один из которых не дал требуемого результата, что, по меньшей мере, сбивает с толку.

Makhmud 18.06.2024 17:22

@TimLewis, спасибо. Я отредактирую вопрос и опубликую ответ. Все еще новичок в stackoverflow. :)

Makhmud 18.06.2024 17:24

Без проблем; добро пожаловать в ТАК 🙂 Кроме того, да, трудно сказать, почему whereBetween() не сработало бы для вас, но в конечном итоге, если у вас есть решение, которое дает правильные результаты, это более важно; оптимизация может прийти позже. Ваше здоровье!

Tim Lewis 18.06.2024 17:27

@TimLewis, спасибо! Я слышал, что у Laravel отличное сообщество, но получить помощь из первых рук — это потрясающе, это действительно вдохновляет меня учиться больше и работать усерднее. Ребята, вы потрясающие! Удачи в ваших проектах!

Makhmud 18.06.2024 17:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Laravel Scout - это популярный пакет, который предоставляет простой и удобный способ добавить полнотекстовый поиск в ваше приложение Laravel. Он...
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
0
10
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо всем за ваши отзывы в разделе комментариев. @Rene и @Tupkap заставили меня задуматься в правильном направлении - я проверял несколько дней в одной ячейке. Я исправил это, переместив цикл foreach. Ниже приведена отредактированная версия кода, которая у меня работает. Думаю, это не самый изящный способ решения проблемы, но так как квартиры не бронируются на длительный срок (обычно на пару дней и не дольше 1-2 недель).

    namespace App\Http\Controllers;

use App\Models\Room;
use App\Models\Booking;
use Carbon\CarbonPeriod;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class SearchController extends Controller
{
    public function index()
    {
        // Validate data later

        $city = request('city');
        $guests_number = request('guests_number');
        $object_type = request('object_type');
        $checkIn = request('checkIn');
        $checkOut = request('checkOut');

        $interval = CarbonPeriod::create($checkIn, $checkOut);

        $rooms = Room::query()
            ->with(['photos', 'bookings', 'days'])
            ->where('rooms.city_id', '=', $city)
            ->where('rooms.beds_num', '>=', $guests_number)
            ->where('rooms.object_type_id', '=', $object_type)
            ->join('bookings', 'bookings.room_id', '=', 'rooms.id')
            ->where(function ($query) use ($checkIn, $checkOut) {
                $query->where('checkIn', '>=', $checkOut)
                    ->orWhere('checkOut', '<=', $checkIn);
            })
            ->where(function ($query) use ($interval) {
                foreach ($interval as $date) {
                    $query->whereExists(function ($query) use ($date) {
                        $query
                            ->from('day_room')
                            ->join('days', 'days.id', '=', 'day_room.day_id')
                            ->whereColumn('day_room.room_id', 'rooms.id')
                            ->where('days.day', '=', $date->format('Y-m-d'));
                    });
                }
            })
            ->get('rooms.*');

        return view('results', ['rooms' => $rooms]);
    }

Другие вопросы по теме