Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Loots's avatar
Level 4

updateOrCreate for belongsTo relationship

Hi!

I want to create a package for adding addresses to models. But I don't want to use Polymorphic relationships because in my opinion, they should be avoided when not necessary. So on the Models that have an address. I want to use a belongTo relationship:

    public function billingAddress()
    {
        return $this->belongsTo(Address::class)->withDefault();

    }

billing_address_id is a nullable field and by default it will just return an empty Address Model so you don't need conditionals. My question is: how can I best update this nullable field? I created a updateBillingAddress method on the model:

    public function updateBillingAddress($array)
    {
        if (!$this->billing_address_id) {
            $this->billing_address_id = $this->billingAddress()->create($array)->id;
        }
        else {
            $this->billingAddress()->first()->fill($array)->save();
        }

        $this->save();
    }

But I have a feeling this should be easier. I looked through the documentation, updateOrCreate looks promising, but it's not perfect:

    public function updateBillingAddress($array)
    {
        $this->billing_address_id = $this->billingAddress()->updateOrCreate([], $array)->id;

        $this->save();
    }

I still have to assign the id to the billing_address_id, while it would make sense that this happens automatically... Also I still have to call the save() method afterwards, although this isn't a big deal because it will only be saved when the address didn't exist yet and the model is dirty. One more 'problem' is that it will run a query to search for the address even if the current billing_address_id is null, which makes no sense.

So I expect there to be an update method:

$aModel->billingAddress()->updateOrCreate2(['house_number' => 1]);

That also assigns the new id to the billing_address_id and only tries to find an existing address if the billing_address_id is not null... And also saves the current model if the address had to be created.

0 likes
3 replies
rodrigo.pedra's avatar
Level 56

So I expect there to be an update method...

There isn't. You have to perform those steps manually.

This is how I would do this:

public function updateBillingAddress($array)
{
    $billingAddress = $this->billingAddress->fill($array)->save();

    $this->billingAddress()->associate($billingAddress);
    $this->save();

    return $billingAddress;
}

As you use ->withDefault() in your relationship definition you already have a new instance there.

If you want to avoid the implicit select query when you know beforehand there is no billing address, you would need to do something very similar to your first code sample.

public function updateBillingAddress($array)
{
    $billingAddress = is_null($this->billing_address_id)  
        ? new Address()
        : $this->billingAddress;

    $billingAddress->fill($array)->save();

    $this->billingAddress()->associate($billingAddress);
    $this->save();

    return $billingAddress;
}
1 like
Loots's avatar
Level 4

Actually, your first code segment is perfect! There will be no select query because indeed, it wil use the default empty one when public_address_id is null. There is no extra query fired because of this, and it's concise.

I did update a little bit, because the save() method returns a bool. So you get this:

    public function publicAddress(): BelongsTo
    {
        return $this->belongsTo(Address::class)->withDefault();
    }

    public function updatePublicAddress(array $values): bool
    {
        $this->publicAddress->fill($values)->save();

        $this->publicAddress()->associate($this->publicAddress);
        return $this->save();
    }

I still have to add some validation, but other than that, it works perfectly! Thanks for your help! I would have looked way to long just to be sure such a method doesn't exist...

1 like

Please or to participate in this conversation.