f7n
f7n

Reputation: 1684

Returning the first model from a hasMany relationship in Laravel

Is it possible to create a quick method to return the first model from a one-to-many relationship? Here is my code, from the model file:

public function books() {
    return $this->hasMany('App\Models\Book');
}

public function first_book() {
    return $this->book()->first();
}

This is the error I'm getting:

Call to undefined method Illuminate\Database\Query\Builder::addEagerConstraints()

The reason I want to use this is so that I can collect the first record using the with() method, for example:

$authors = Author::with('first_book')->select('*');

I'm using these records with Datatables.

Upvotes: 31

Views: 59645

Answers (6)

Humble Hermit
Humble Hermit

Reputation: 133

I know this is an old post but in case someone is looking for a quicker way to do this without modifying your Model class. You can grab the first record of a relationship directly from the Model hasMany relationship.

I have used this and it works well. For example if you have a hasMany relationship between Library and Books model;

$first_book = $library->books->first();

or to get the last book;

$last_book = $library->books->last();

Upvotes: 0

Richard
Richard

Reputation: 531

With laravel 9.x you can use the latestOfMany or oldestOfMany like so;

// your relationship
public function books() {
    return $this->hasMany('App\Models\Book');
}

// Get the first inserted child model
public function first_book() {
   return $this->hasOne('App\Models\Book')->oldestOfMany();

}

// Get the last inserted child model
public function last_book() {
   return $this->hasOne('App\Models\Book')->latestOfMany();

}

BONUS: If you are on php 5.5 or later, you can get the fully qualified class name by using the scope resolution operator, looks clean, ie;

// your relationship
public function books() {
    return $this->hasMany(Book::class);
}

// Get the first inserted child model
public function first_book() {
   return $this->hasOne(Book::class)->oldestOfMany();

}

// Get the last inserted child model
public function last_book() {
   return $this->hasOne(Book::class)->latestOfMany();

}

Link to the Laravel documentation

Upvotes: 9

Keshari Nandan
Keshari Nandan

Reputation: 1148

I might be late but for your future use and for other who want the same output try this one -

// If you need the last one

public function books() {
    return $this->hasOne('App\Models\Book')->latest();
}

// If you need the first entry -

public function books() {
        return $this->hasOne('App\Models\Book')->oldest();
    }

Upvotes: 57

Yaser Darzi
Yaser Darzi

Reputation: 1478

A one-to-one relationship is a very basic relation. For example

public function books()
    {
        return $this->hasOne('App\Models\Book');
    }

Upvotes: 2

Jerodev
Jerodev

Reputation: 33186

A relation that can be eager loaded has to return a query. The first() function returns an eloquent object.

The solution is to limit the number of results of this query like so:

public function first_book() {
    return $this->books()->take(1);
}

$author->first_book will still be a collection, but it will only contain the first related book in your database.

Upvotes: 15

thefallen
thefallen

Reputation: 9749

To use with() your method has to return a collection from a relation method, because your relation is hasMany. So what you could do is:

public function books() {
    return $this->hasMany('App\Models\Book');
}

public function first_book() {
    return $this->hasMany('App\Models\Book')->limit(1);
}

Which would return a collection with your first item, so you' still have to call first():

$authors = Author::with('first_book')->select('*');
$authors->first_book->first();

Upvotes: 7

Related Questions