stefan7's avatar

Get all parent categories

Hi,

I have a category model with parent_id attribute. I am trying to write a function that gives me all the parents. dd($parent) returns only string: "parent_id" What am I missing? And what is here the difference between $parent = $this->parent(); and$parent = $this->parent; ??

    public function parent()
    {
        return $this->belongsTo('App\Models\Category', 'parent_id', 'id');
    }

    public function getAllParents()
    {
        $parents = collect([]);
        $parent = $this->parent;

        while(!is_null($parent)) {
            $parents->push($parent);
            $parent = $parent->parent;
        }

        return $parents;
    }

dd($this) returns object

Category {#399 ▼
  #parent: "parent_id"
  #appends: array:1 [▶]
  +fillable: array:3 [▶]
  #connection: "mysql"
  #table: null
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:7 [▼
    "id" => 7
    "parent_id" => 3
    "en_name" => "Category 7"
    "de_name" => "Kategorie 7"
    "slug" => "category7"
    "created_at" => "2019-06-03 11:27:37"
    "updated_at" => "2019-06-03 11:27:37"
  ]

I tried also this

$parent = $this->parent();

Which returns me, but is not accessible

BelongsTo {#419 ▼
  #child: Category {#399 ▶}
  #foreignKey: "parent_id"
  #ownerKey: "id"
  #relation: "parent"
  #query: Builder {#402 ▶}
  #parent: Category {#399 ▶}
  #related: Category {#418 ▶}
  #withDefault: null
}
0 likes
1 reply
ChristophHarms's avatar

Your code to retrieve all parents looks correct to me. At what point do you dd($parent) and it only contains the string?

Difference between $this->parent and $this->parent():

If you call the relation method, it returns an object of a relation class (in your case a BelongsTo object). Relation classes extend the abstract Relation class and are basically a wrapped query builder with the constraints etc. set to reflect the type of relation. This way, you can for example add constraints to the relation by using it like a query builder:

class Manufacturer extends Model
{
    public function products()
    {
        return $this->hasMany(Product::class);
    }
}

[...]

$manufacturer1 = Manufacturer::find(1);

$productsQuery = $manufacturer1->products();

$blueProductsQuery = $productsQuery->where('color', 'blue');

// add more constraints as you like

$blueProducts = $blueProductsQuery->get();

Note that no query is sent to the DB until you call get().

On the other hand, if you call the relation property, the relation is actually resolved via the Model's magic __get() method which itself uses the getAttribute() method from the Illuminate\Database\Eloquent\Concerns\HasAttributes trait, which actually resolves the relationship and queries the DB (if the relation isn't already loaded into the model) for all related models without further constraints:

$products = $manufacturer1->products // DB is queried here, $products contains all products of manufacturer 1

Just look through the Eloquent code if you want to learn how this works exactly. it is quite easy to understand once you get the hang of it. In general, most of Laravel's "magic" (i.e.: Things Laravel/Eloquent does where you think "wow, that's really convenient, I wonder how they do that") is done with PHP magic methods like __call() or __get().

EDIT:

To further clarify, $manufacturer1->products is basically the same as $manufacturer1->products()->get() except that the former will only query the DB if the relation isn't already loaded while the latter will query the DB in any case.

Please or to participate in this conversation.