laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.64k stars 11.04k forks source link

orWhere generating an OR when it previously would generate an AND #53628

Open stellar-scottreed opened 19 hours ago

stellar-scottreed commented 19 hours ago

Laravel Version

11.33.2

PHP Version

8.3.13

Database Driver & Version

MySQL

Description

I recently upgraded a legacy laravel application from 5.4 to laravel 11.

The following code:

$booking_boats = $this->current_location->booking_boats()
    ->with(['booking'])
    ->where(function ($query) {
        $query->where('booking_boats.status','Queued')
                ->orWhere([
                    ['booking_boats.status','=','On Water'],
                    ['booking_boats.checkout_date','!=',''],
                ]);
    })
    ->get();

Would previously generate a query like this:

SELECT  *
FROM `booking_boats`
INNER JOIN `bookings`
ON `bookings`.`id` = `booking_boats`.`booking_id`
WHERE `bookings`.`deleted_at` is null
AND `bookings`.`location_id` = ?
AND (`booking_boats`.`status` = ? or (`booking_boats`.`status` = ? AND `booking_boats`.`checkout_date` != ?))
AND `booking_boats`.`deleted_at` is null

The important part is: (booking_boats.status = ? AND booking_boats.checkout_date != ?)

In Laravel 11, the following query is generated:

SELECT  *
FROM `booking_boats`
INNER JOIN `bookings`
ON `bookings`.`id` = `booking_boats`.`booking_id`
WHERE `bookings`.`location_id` = ?
AND (`booking_boats`.`status` = ? or (`booking_boats`.`status` = ? or `booking_boats`.`checkout_date` != ?))
AND `booking_boats`.`deleted_at` is null
AND `bookings`.`deleted_at` is null

The important part is: (booking_boats.status = ? or booking_boats.checkout_date != ?)

So, previously using ->orWhere with an array syntax would generate an AND query, but now it generates an OR query.

This seems like a bug but if somehow I missed this in the laravel upgrade notes I would appreciate knowing what exactly was changed and what functions are impacted as there are hundreds of queries I will have to update.

Thank you.

Steps To Reproduce

Perform an ->orWhere using multiple array elements, ie:

 $query->where('booking_boats.status','Queued')
    ->orWhere([
        ['booking_boats.status','=','On Water'],
        ['booking_boats.checkout_date','!=',''],
]);
ahinkle commented 15 hours ago

Duplicate of https://github.com/laravel/framework/issues/53184.

stellar-scottreed commented 15 hours ago

Thanks for the context. I don't really see a resolution in the duplicate though? A PR was even added to revert the breaking change but it was closed. Per this comment sounds like I'm not the only one to have spent a lot of time debugging this without seeing any mention of it in the migration guide.

Does this change in behavior only effect orWhere or does it affect other queries too?

Is there an alternative to orWhere that will keep the same behavior?

Thank you.

stellar-scottreed commented 2 hours ago

I reviewed the PR that caused this breaking change. It looks like the intention of the PR was to make it so the boolean argument is respected - which makes sense, but shouldn't the default be what it has always been: "AND"? We've been relying on this behavior since 2017.

I agree with the comments made that this change should have been added to ->whereAny ->orWhereAny instead.

If this change is not reverted I am still hoping for an update to the migration guide on what functions are impacted/behavior changed. I didn't write this legacy application and there are many people out there who inherited old code bases and could use a clear path to maintaining the previous functionality.