akvaskov's avatar

HasMany with custom relationship method name returns null

Hi! I have a model called Product. I want to add a relationship to another table Orders. Code below works fine. public function orders(): HasMany { return $this->hasMany(Order::class); } But if I want to add some where clause and rename the method, the result will be null:

public function lastOrders(): HasMany
{
    return $this->hasMany(Order::class, 'product_id', 'id')->
		where('date', '>', Carbon::today()->subDays(3));
}

Both tables called products and orders respectfully. The orders table has a product_id field.

What am I doing wrong?

0 likes
2 replies
tisuchi's avatar
tisuchi
Best Answer
Level 70

@akvaskov Sorry, the answer is a bit big, but it's critical to understand!

TL;DR

  • Your lastOrders() method is correct.
  • To retrieve data from lastOrders(), you must use $product->lastOrders()->get() to get the filtered results because you applied where.

It's funny though, but it's important to understand. Let's walk through a clear solution to your issue.

You have a custom relationship method lastOrders() that applies a where clause to filter orders made in the last 3 days. You want to retrieve those filtered orders from the Product model.

⚠️ Here’s the key point: When you create a custom relationship like lastOrders(), you're still returning a query builder, which requires methods like get(), first(), etc., to execute and retrieve the actual data.

public function lastOrders(): HasMany
{
    return $this->hasMany(Order::class, 'product_id', 'id')
                ->where('date', '>', Carbon::today()->subDays(3));
}

This method returns a HasMany relationship with a where condition. The important part is how you call this method.

How to retrieve data using lastOrders()

To actually retrieve the data from the lastOrders() relationship, you need to execute the query by calling methods like get(), or first(), or by eager loading.

For example,

$product = Product::find(1); // Assuming you're looking for Product ID 1
$lastOrders = $product->lastOrders()->get(); // This executes the query and retrieves the filtered orders

// Now, $lastOrders will contain all orders from the last 3 days.

// Or with eager loaing
$products = Product::with('lastOrders')->get();
// Each product will have a 'lastOrders' property with the related orders from the last 3 days

Why using orders() works, but lastOrders() returns null

When you call $product->orders, Laravel automatically returns the related orders without needing additional query execution (like get()). However, when you add a where clause in lastOrders(), you're modifying the relationship and returning a query builder. You need to explicitly tell Laravel to execute that query by calling methods like get() or first().

4 likes
akvaskov's avatar

@tisuchi many thanks for your reply! It's very usefull and really educational.

1 like

Please or to participate in this conversation.