Search code examples
phpeloquentlaravel-9

Laravel 9 - Relationship Update with Condition


I've experiencing a quite unique case, where, when doing bulk update (using where condition) from a relationship, it does update every record that match the condition, instead of a single row

Here's the sample code:

// Model

class Customer extends Model
{

    protected $table = 'customer';
    protected $primaryKey = 'id_cust';
    ...
    ...
    ...


    public function customer_address()
    {
        return $this->hasOne(CustomerAddress::class, 'customer_id', 'id_cust');
    }

    ...
    ...
    ...

}


class CustomerAddress extends Model
{
    ...
    ...
    ...

    public function customer()
    {
        return $this->belongsTo(Customer::class, 'customer_id', 'id_cust')->withDefault([
            'name' => ''
        ]);
    }
// controller

class CustomerController extends BaseCustomerController
{
    ...
    ...
    ...

    /**
     * Update the specified resource in storage.
     * @param Request $request
     * @param Customer $customer
     * @return Renderable
     */
    public function update(CustomerEditRequest $request, Customer $customer)
    {
        ...
        ...
        ...

        $insert_address['address_label'] = 'Default';
        $insert_address['recipient_name'] = $request->recipient_name;
        $insert_address['recipient_phone'] = $request->international_code . '' . $request->recipient_phone;
        $insert_address['province_id'] = $request->province_id;
        $insert_address['city_id'] = $request->city_id;
        $insert_address['district_id'] = $request->district_id;
        $insert_address['recipient_address'] = $request->recipient_address;

        if ($customer->customer_address) {
            $customer->customer_address->where('is_default', '1')->update($insert_address); // ---> here the possible of suspicious code
        } else {
            CustomerAddress::create([...$insert_address, 'is_default' => '1', 'customer_id' => $customer->id_cust]);
        }

        ...
        ...
        ...
    }
}

Notice, on CustomerController, function update, on this part $customer->customer_address->where('is_default', '1')->update($insert_address); I'm assuming that, it will only get customer_address data which related to $customer, and then update with, with condition is_default = 1 But, instead, it update all data that has is_default = 1

Why it's happening? what's wrong with that? When I'm using $customer->customer_address->update($insert_address);, it's correct and only update the related record

If it's indeed can't, how do I can still update it, while persisting ability of where condition?


--- PS: Please ignore some questionable code that you found out (like, it return `hasOne`, but why it also has condition of `is_default`, and others, there's other business rule that need that condition)

Solution

  • For those who stumble upon this same issue (or related to this somehow), after few hours of debugging with my colleagues, I found out that:

    • If we're using that way, $customer->customer_address->where(...), which a direct condition after a relationship, it will pull all the Model data (which is, customer_address), then filter it by the condition, instead of the data that related to its first relation.

    • I also try what my colleague suggest (same suggestion from @Takachi here, thanks for that! :)), which using $customer->customer_address()->where(...), it will also works, as, it seems, it call the query builder of the relation, and then, apply the where condition

    After spend some more time to get a documentation & materials on what happening, it seems the way that I've done is expected, here are some references I could found:

    Again, I'm using Laravel 9.x version, so quite unsure if this still happened on newer version. But it seems, the same thing still occurred, judging from the documentation on Laravel 10.x

    Anyway, thanks for coming here! Especially @Takachi for give me his cents! :)