I am a bit confused on how to properly pull the data from Eloquent Models. I did read the documentation, but this is not mentioned anywhere or I missed it.
Let's assume we have two models, Client
and Country
(note: I only added relevant code, otherwise this question would be quite long):
Client:
<?php
namespace App\Models\Client;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int id
* @property int country_id
*/
class Client extends BaseModel
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
...
'country_id',
...
];
/**
* Return Country relationship
*
* @return BelongsTo
*/
public function country(): BelongsTo
{
return $this->belongsTo(Country::class);
}
}
Country:
<?php
namespace App\Models\Country;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int id
*/
class Country extends BaseModel
{
public function clients(): hasMany
{
return $this->hasMany(Client::class);
}
}
Nothing special, two normal Models. Now, when I try to pull the data I have a few issues:
dd(Country::findOrFail(32));
// CORRECT: returns selected countrydd(Country::findOrFail(32)->first());
// WRONG: returns first country in database, not the one with ID of 32, EXPECTED: single country object with selected countrydd(Country::findOrFail(32)->get());
// WRONG: returns all countries in database, EXPECTED: an array of objects with one country in itdd(Country::with('clients')->findOrFail(32)->first());
// WRONG: returns first country in the database and not the one with ID of 32, relationship is also not correct, EXPECTED: object with selected country with valid relationship (clients) as array of objectsdd(Country::findOrFail(32)->clients());
// WRONG: returns HasMany relationship, no data, EXPECTED: an array of clients as objectsdd(Country::findOrFail(32)->clients()->get());
// CORRECT: returns valid data, array of clients as objectsdd(Country::findOrFail(32)->clients()->first());
// CORRECT: returns first client for selected country
So, why are all WRONG wrong ones? Am I interpreting the documentation wrongly? The most frustrating is dd(Country::with('clients')->findOrFail(32)->first())
since now I have no means on how to filter based on selected country and then also provide a list of clients in that country. I thought that Eloquent is quite advanced, so I assume that I am doing something wrong and would appreciate some guidance.
Please note that in latest example I also tried reversing query as dd(Country::findOrFail(32)->with('clients')->first())
, same result.
CodePudding user response:
In my opinion, you are mixing up a few things here. For example, combining FindOrFail() with first() is double. Look here, that's a good explanation I found:
find($id)
takes an id and returns a single model. If no matching model exist, it returns null.findOrFail($id)
takes an id and returns a single model. If no matching model exist, it throws an error1.first()
returns the first record found in the database. If no matching model exist, it returns null.firstOrFail()
returns the first record found in the database. If no matching model exist, it throws an error1.get()
returns a collection of models matching the query.pluck($column)
returns a collection of just the values in the given column. In previous versions of Laravel this method was called lists.toArray()
converts the model/collection into a simple PHP array.
Source: https://stackoverflow.com/a/33027466/14807111
CodePudding user response:
The method description for the Builder method FindOrFail()
is:
* Find a model by its primary key or throw an exception.
In your usecase this method is calling the find()
method and executing:
$this->whereKey($id)->first($columns);
This will return the first model and by default all of its columns. Therefore any other first()
afterwards will introduce another query.
Your first three "Wrongs" are because of execution order, the query executes the findOrFail first and afterwards it calls first()
on the whole query again.
The fourth "WRONG" happens because your relationship returns a hasMany relationship, this is normal behaviour. You could either call ->get()
afterwards or call ->clients
without the ()
to receive the collection.
After all you are using the first()
method wrong. You either use find()
, findOrFail()
or you use ->first()
. But you should not use them together (there might be some edge cases where you have to, but genreally speaking, don't!).
Using find()
or findOrFail()
will already return the first() element that matched the ID
.