How to format array with relations parent-child? - laravel

I have the typical structure of table in MySQL.
id | parent_id | name | object_id
1 0 G 1
2 1 T 1
3 1 R 1
How to build result array with values of parent/child when I select data by object_id?

If its laravel and the parent and the object are both Eloquent models you should be able to do something like this:
class Parent{
public function children(){
return $this->belongsToMany('App\Object', 'your_table', 'parent_id, 'object_id');
}
}
And then the child object class:
class object{
public function parent(){
return $this->belongsToMany('App\Parent', 'your_table', 'object_id, 'parent_id');
}
}
Look into eloquent relationships here: Eloquent relations
If its any other way you are looking for the comments above surely might help you in the correct direction.

Related

In Laravel I'm attempting to get the info from a table with a pivot

I currently have 3 tables:
medias, colors, media_colors
media_colors is the pivot table I'm trying to use it contains
media_id | color_id
------------------
1 | 1
in colors table I have
id | front
----------
1 | color
My model for medias contains:
public function mediaColors()
{
return $this->belongsToMany(Color::class, 'media_colors');
}
In a controller I attempt to query front like so:
$m = Media::find($estimate->media_id);
dd($m->mediaColors[0]->pivot->front);
This returns null. How would I be able to access this?
You can access the colors directly from the relation collection as marked in the comment.
$m = Media::find($estimate->media_id);
foreach($m->mediaColors as $color) {
//you can access all the colors here
$fronts[] = $color->front;
}
//or if you only want the first one (if it exists)
$m->mediaColors[0]->front
one easier way to get just the first value if you only need that
$m = Media::find($estimate->media_id);
$front = $m->mediaColors()->value('front');
// this will return null if no relation found without triggering an exception

related records of parent table in many to many relation in laravel

Table 1: categories: id,name,parent_id
id name parent_id
1 Vehicle null
2 Car 1
3 Sedan 2
Vehicle > Car > Sedan
Table 2: features: id, name
id name
1 type
2 cylinder
3 color
4 weight
Table 3: category_feature: category_id, feature_id
category_id feature_id
1 1
1 2
2 3
3 4
I could get all features by parent (category).
for example with this:
Category model:
public function features()
{
return $this->belongsToMany(Feature::class);
}
And:
$category = Category::find(1);
$features = $category->features()->get();
How can I get by a child category
all the features of the child and the features of the father's categories?
something like this:
$category = Category::find(3);
$features = $category->parent_features()->get();
I want it return these: type,cylinder,color,weight
On Categories model:
public function parent(){
return $this->belongsTo(\App\Category::class);
}
Then you can load it in one go:
$category = Category::with(['features', 'parent' => function($query){
$query->with('features');
}])->find(3);
And then you can pull the parent's features:
$features = $category->parent->features;
Or the original car's features:
$originalFeatures = $category->features;

Self referencing "ownerless" many_many relationships?

I have a many many relationship where I don't care who created the relationship, if the querying models ID is in either side of the relationship I need the other side.
--
Let's say I have this model
class Ingredient extends Model
{
public function complements()
{
return $this->belongsToMany(
self::class, 'ingredient_complements',
'ingredient_id', 'complement_id'
);
}
}
And I've created a few different instances as below
$ing1 = new Ingredient();
$ing2 = new Ingredient();
$ing3 = new Ingredient();
$ing4 = new Ingredient();
$ing5 = new Ingredient();
$ing6 = new Ingredient();
$ing1->save(); //2,3,4,5,6...
Next I go through and relate a few of them
$ing1->complements()->attach($ing2);
$ing2->complements()->attach($ing3);
$ing3->complements()->attach($ing4);
$ing4->complements()->attach($ing6);
$ing5->complements()->attach($ing6);
$ing6->complements()->attach($ing1);
So now we have a pivot table that looks like
| ing_id | cmp_id |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 4 | 5 |
| 5 | 6 |
| 6 | 1 |
So..
if I call $ing1->complements I'll get back ing2
if I call $ing2->complements I'll get back ing3
But..
if I call $ing2->complements I won't get back ing1
if I call $ing3->complements I won't get back ing2
I need to fix that.
I've got a second method that I'm using at the moment that I can call using $ing3->complementaryIngredients; which ends up executing something like
select * from `ingredients`
inner join `ingredient_complements` on `ingredient_id` = '3'
or `complement_id` = '3' and `ingredient_id` = `id`
or `complement_id` = `id` where `id` <> '3'
Which works, but I don't feel this is something that I should have to break out of the ORM for?
public function getComplementaryIngredientsAttribute()
{
return self::join('ingredient_complements', function(JoinClause $join){
$join->where('ingredient_id', $this->id);
$join->orWhere('complement_id', $this->id);
$join->on('ingredient_id', '=', 'id');
$join->orOn('complement_id', '=', 'id');
})->where('id', '<>', $this->id)->get();
}
You actually need to define bi-directional relationship, so your model should have 1 more method which will define the opposite direction.
public function ingredients()
{
return $this->belongsToMany(
self::class, 'ingredient_complements',
'complement_id', 'ingredient_id'
);
}
Then you will get the other side of your relation in this way $ing2->ingredients() and it will return what you are looking for.
Another approach is to create model for your pivot table so you can query that model to get both sides of your relationship

laravel Eloquent relationship using Distinct

I need show data with relationship using DISTINCT.
This is my sql data :
table pochettes
id |
49 |
table journees
id | formateurs_id | pochettes_id
1 | 3 | 49
2 | 4 | 49
3 | 3 | 49
table formateurs
id |
3 |
4 |
model Pochette
public function Journees()
{
return $this->hasMany('App\Journee','pochettes_id');
}
model Journee
public function Formateurs()
{
return $this->belongsTo('App\Formateur', 'formateurs_id');
}
I am used this code to show data with distinct formateurs because a Formateur can have many Journee in the same Pochette :
$pochette = Pochette::find(49);
foreach ($pochette->Journees->distinct('formateurs_id') as $formateur) {
echo $formateur->formateurs_id;
echo "<br>";
}
But this code dosn't work, I have this error message
**BadMethodCallException in Macroable.php line 81:
Method distinct does not exist.**
I would show this result :
3
4
not :
3
4
3
When you access an Eloquent relationship like a property, you will get an Eloquent Collection. However, distinct() is a Query Builder method and you need access to the Query Builder instance before returning the Collection. If you add () to the end of the relationship, you gain access to the Query Builder instance and can chain those Query Builder methods. In your case, you are actually looking for the groupBy() method and not distinct(). Don't forget to end the query with get().
foreach ($pochette->Journees()->groupBy('formateurs_id')->get() as $formateur) {
echo $formateur->id ;
echo "<br>";
}

Laravel Eloquent Relationships for Siblings

Relationship for Siblings are many to many self relationship. So, for many to many self relationship, we can define two functions in the model:
public function siblings()
{
return $this->belongsToMany('Student', 'student_sibling', 'student_id', 'sibling_id');
}
public function siblingOf()
{
return $this->belongsToMany('Student', 'student_sibling', 'sibling_id', 'student_id');
}
The first one returns the students who are siblings of the student. The reverse is also true for siblings. So, the second one returns the students of whom the student is a sibling.
So, we can merge both the collections to get a list of students who are siblings of the student. Here is my code in the controller method:
$siblingOf = $student->siblingOf;
$siblings = $student->siblings;
$siblings = $siblings->merge($siblingOf);
But there is more. Siblings relationship is a chain relationship unlike friends relationship. This mean, if X is a sibling of Y and Y is a sibling of Z, then Z is a sibling of X.
So, how to get the collection of all students who are sibling of a student?
I'm assuming that you have a Student model, with student_id as your primary key, and a pivot table that looks something like this:
+---------+------------+------------+
| id (PK) | student_id | sibling_id |
+---------+------------+------------+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 4 |
| 4 | 4 | 2 |
| 5 | 6 | 5 |
+---------+------------+------------+
In this example, we want to be able to discern that 1, 2, 3 and 4 are all siblings of each other. 5 and 6 are siblings as well.
One way to solve this is by gathering the result of the two belongsToMany() relationships in your model—as you did in your question—and then recursively iterating over the result, continuing to check the belongsToMany() functions until we run out of siblings.
In the Student model, define two functions:
public function getAllSiblings(){
// create a collection containing just the first student
$this->siblings = $this->newCollection()->make($this);
// add siblings (recursively) to the collection
$this->gatherSiblings($this->student_id);
// OPTIONAL: remove the first student, if desired
$this->siblings->shift();
return($this->siblings);
}
private function gatherSiblings($student_id){
$checkStudent = Student::find($student_id);
if ($checkStudent) {
// get related siblings from model, combine them into one collection
$siblingOf = $checkStudent->siblingOf()->get();
$siblings = $checkStudent->siblings()->get();
$siblings = $siblings->merge($siblingOf);
// iterate over the related siblings
$siblings->each(function($sibling)
{
// if we've found a new sibling who's
// not already in the collection, add it
if(!$this->siblings->contains($sibling->student_id)) {
$this->siblings->push($sibling);
// look for more siblings (recurse)
$this->gatherSiblings($sibling->student_id);
};
});
return;
}
}
In your controller, find the initial student, and then call getAllSiblings() from Student:
$student = Student::find($id);
$siblings = $student->getAllSiblings();
The result is a collection with all the siblings of the original student. So, if you run this for student 1, you will get a collection containing students 2, 3 and 4. (If you'd prefer to keep the original student as part of the siblings collection, so that running this for 1 returns 1, 2, 3 and 4, simply remove the optional step in getAllSiblings().)
From there, you can cast the collection to an array, or sort, etc. as needed.
Recursive relation could do this, BUT it will probably cause infinite loop (function nesting limit error).
Anyway this is how to setup such relation:
public function siblingsRecursive()
{
return $this->siblings()->with('siblingsRecursive');
}
public function siblings()
{
return $this->belongsToMany('Student', 'siblings');
}
Then you call, as simple as this:
$students = Student::with('siblingsRecursive');
I think what you might want is this:
public function siblings() {
return $this->hasMany(self::class, 'parent_id', 'parent_id');
}

Resources