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

ignaciodev's avatar

HasOneThrough() not working with Inertia?

Hello, I am facing a bit of a weird issue... I have a ProductVariant model, that has one Merchant through a Product like so:

public function merchant(): HasOneThrough
{
    return $this->hasOneThrough(
        Merchant::class,
        Product::class,
        'id',
        'id'
    );
}

This Merchant has a getCurrencyAttribute() method, that I can access from the ProductVariant without issues: $productVariant->merchant->currency;, and I use it to format the ProductVariant price, like this:

protected $with = [
    'merchant'
];

protected $appends = [
    'priceFormatted',
];

public function getPriceFormattedAttribute()
{
    return "{$this->merchant->currency->symbol} {$this->price}";
}

On my controller, I am fetching the Product like this:

$product = Product::with(['merchant', 'variants'])
    ->where('slug', $slug)
    ->firstOrFail()
;

All good so far. From my controller I can do $product->variants[0]->priceFormatted and it works. But as soon as I return an Inertia render like this:

return Inertia::render('ProductPage', [
    'product' => $product,
]);

I get this error:

Attempt to read property "currency" on null.

0 likes
9 replies
christian-qode's avatar

I think the relation is not loaded by default. You can use with or load methods to eager load the relations.

ignaciodev's avatar

@christian-qode Thank you for the reply. My model does have the protected $with = [ 'merchant' ]; attribute set. I have also tried calling the load() method on the controller.

What's bizarre is that, I can call the method before returning the Inertia render... so Im guessing it's an issue when converting to JSON by Inertia?

christian-qode's avatar

@ignaciodev You have a 'with $merchant' on the product variation, but you're calling the $product->variants. Is the 'variants' relation also eager loaded?

When you call the method before returning to Inertia, Laravel will lazy load it. But I think it's not loaded yet and therefore not converted to JSON. You can disable lazy loading completely to verify this (see https://laracasts.com/series/jeffreys-larabits/episodes/3)

ignaciodev's avatar

@christian-qode yes, it's eager loaded here: Product::with(['merchant', 'variants']). Does that not suffice?

ederson's avatar

@ignaciodev it should be ok

i believe the problem is in your js code and not the relation. It is unclear what currency is . Could you post the relevant js code ?

I find this vuejs/devtools very helpfull. I can check the data passed to vue very easy.

ignaciodev's avatar

@ederson On the JS (React) I'm doing nothing other than console.log(product) so far.. plus the error looks like is being thrown from the ProductVariant model in Laravel.

As per the getCurrencyAttribute, it's just returning an object from the Merchant country code:

public function getCurrencyAttribute()
{
    return Currency::getByCountryCode($this->country_code);
}

Which returns something like this:

[
	'code' => 'EUR',
	'text' => 'euro'
	'symbol' => '€'
]
ederson's avatar

@ignaciodev the exception page should show you where the property currency is called.

Probably there is a product without a merchant so when getPriceFormattedAttribute() is called tries to access currency on null as the error says.

try this to verify

public function getPriceFormattedAttribute()
{
	try {
			 return "{$this->merchant->currency->symbol} {$this->price}";
	} catch (Exception $e) {
    		dd($this,$e);
	}
}
ignaciodev's avatar

@ederson It says the error is coming from the ProductVariant model, from:

public function getPriceFormattedAttribute()
{
    return "{$this->merchant->currency->symbol} {$this->price}";
}

But I only get this error if I return an Inertia render, which is what's strange. If I keep working from within the controller, everything works as expected .. :/

ederson's avatar

@ignaciodev In the controller you won't get this error unless you try to access the PriceFormatted attr of the model without Merchant. When you return the Inertia render all accessors are executed and then the exception is thrown.

Add the try catch block in the accessor to see the model that causes the error and i#m pretty sure you ll see tha the relation is null

Please or to participate in this conversation.