Home > Mobile >  Laravel Eloquent getting data (with relationship) confusion
Laravel Eloquent getting data (with relationship) confusion

Time:10-31

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 country
  • dd(Country::findOrFail(32)->first()); // WRONG: returns first country in database, not the one with ID of 32, EXPECTED: single country object with selected country
  • dd(Country::findOrFail(32)->get()); // WRONG: returns all countries in database, EXPECTED: an array of objects with one country in it
  • dd(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 objects
  • dd(Country::findOrFail(32)->clients()); // WRONG: returns HasMany relationship, no data, EXPECTED: an array of clients as objects
  • dd(Country::findOrFail(32)->clients()->get()); // CORRECT: returns valid data, array of clients as objects
  • dd(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:

  1. find($id) takes an id and returns a single model. If no matching model exist, it returns null.
  2. findOrFail($id) takes an id and returns a single model. If no matching model exist, it throws an error1.
  3. first() returns the first record found in the database. If no matching model exist, it returns null.
  4. firstOrFail() returns the first record found in the database. If no matching model exist, it throws an error1.
  5. get() returns a collection of models matching the query.
  6. pluck($column) returns a collection of just the values in the given column. In previous versions of Laravel this method was called lists.
  7. 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.

  • Related