Eloquent Parent-Child Relationship on Same Model

Eloquent Parent-Child relationship on same model

You would have to recursively get the children if you have an unknown depth like that.

Another option is to use the nested sets model instead of the adjacency list model. You can use something like baum/baum package for Laravel for nested sets.

"A nested set is a smart way to implement an ordered tree that allows for fast, non-recursive queries." - https://github.com/etrepat/baum

With this package you have methods like getDescendants to get all children and nested children and toHierarchy to get a complete tree hierarchy.

Wikipedia - Nested Set Model

Baum - Nested Set pattern for Laravel's Eloquent ORM

Managing Hierarchical Data in MySQL

Laravel parent / child relationship on the same model

Calling the relationship function (->children()) will return an instance of the relation class. You either need to call then get() or just use the property:

$children = $category->children()->get();
// or
$children = $category->children;

Further explanation

Actually children() and children are something pretty different. children() just calls the method you defined for your relationship. The method returns an object of HasMany. You can use this to apply further query methods. For example:

$category->children()->orderBy('firstname')->get();

Now accessing the property children works differently. You never defined it, so Laravel does some magic in the background.

Let's have a look at Illuminate\Database\Eloquent\Model:

public function __get($key)
{
return $this->getAttribute($key);
}

The __get function is called when you try to access a property on a PHP object that doesn't actually exist.

public function getAttribute($key)
{
$inAttributes = array_key_exists($key, $this->attributes);

// If the key references an attribute, we can just go ahead and return the
// plain attribute value from the model. This allows every attribute to
// be dynamically accessed through the _get method without accessors.
if ($inAttributes || $this->hasGetMutator($key))
{
return $this->getAttributeValue($key);
}

// If the key already exists in the relationships array, it just means the
// relationship has already been loaded, so we'll just return it out of
// here because there is no need to query within the relations twice.
if (array_key_exists($key, $this->relations))
{
return $this->relations[$key];
}

// If the "attribute" exists as a method on the model, we will just assume
// it is a relationship and will load and return results from the query
// and hydrate the relationship's value on the "relationships" array.
$camelKey = camel_case($key);

if (method_exists($this, $camelKey))
{
return $this->getRelationshipFromMethod($key, $camelKey);
}
}

Then in getAttribute first is some code that checks for "normal" attributes and returns then. And finally, at the end of the method, if there's a relation method defined getRelationshipFromMethod is called.

It will then retrieve the result of the relationship and return that.

Eloquent relationship between two models that belong to the same model

public function parents()
{
return $this->hasManyThrough(Parent::class, Family::class, 'id', 'family_id', 'family_id', 'id');
}

Laravel - Getting parent data depending on child association

Run doesn't have a user_id column, and I believe a whereHas is what you want here as you are trying to only get runs where a certain user has a time not all runs with only the times for that user

$runs = Run::whereHas('times', function ($builder) use ($user) { 
$query->where('user_id', $user->id);
})->get();

Additionally if you want to eager load the times for that user and that user only you can add a constrainted with

$runs = Run::whereHas('times', function ($builder) use ($user) { 
$query->where('user_id', $user->id);
})->with(['times' => function ($query) use ($user) {
$query->where('user_id', $user->id);
}])->get();

edit:

The relationship between Runs and Users is effectively a many to many relationship with Times acting as a pivot model. Laravel supports this type of arrangement so let's set that up.

use Illuminate\Database\Eloquent\Relations\Pivot;

class Time extends Pivot
{
public function run()
{
return $this->belongsTo('\App\Run');
}

public function user()
{
return $this->belongsTo('\App\User');
}
}
class User extends Model
{
public function times()
{
return $this->hasMany('\App\Time');
}

public function runs()
{
return $this->belongsToMany('\App\Run')->using('\App\Time');
}
}
class Run extends Model
{
public function times()
{
return $this->hasMany('\App\Time');
}

public function users()
{
return $this->belongsToMany('\App\User')->using('\App\Time');
}
}

So now you can query all the runs for a specific user, and because it's using Time as a pivot table you know that the user will have a time for all the runs it returns

User::find(1)->runs();

All users who participated in a run

Run::find(1)->users();

belongsToMany with parent-child relationship in Laravel 5

Extend your models:

class Category {
public function children() {
return $this->hasMany(Category::class, 'parent_id');
}
}

class Game {
public function getAllCategoriesAttribute() {
$result = collect();
$children = function($categories) use(&$result, &$children) {
if($categories->isEmpty()) return;
$result = $result->merge($categories);
$children($categories->pluck('children')->collapse());
};
$children($this->categories);
return $result;
}
}

Then you can access the categories like this:

Game::find($id)->allCategories;

How to create a parent / children relationship in laravel?

I was selecting the columns like this

CategoryModel::select('id', 'name')->with('parent')->get();

I did not know that I also should select the parent_id.
Now It's working fine with this

public function parent()
{
return $this->hasOne(CategoryModel::class, 'id', 'parent_id');
}

Laravel parent/children relationship on it's own model

This issue has been reported and fixed in 5.0 https://github.com/laravel/framework/pull/8193

Unfortunately there is no back port for the version 4.

However if you want to apply the fix yourself you can see the list of modifications here : https://github.com/laravel/framework/pull/8193/files

Be carefull, modifying the framework's code base is at risk but there will be no more bug fixes on Laravel 4.x version, only security fixes for a few more month.



Related Topics



Leave a reply



Submit