Eloquent relationship to get grandparents, great grandparents, etc - php

I have a table "people" with these fields :
Name
mother_id
father_id
In mother_id and father_id, I stock other Poeple ID
I have two relationships to get the parents
public function father()
{
return $this->belongsTo(Poeple::class, 'father_id');
}
public function mother()
{
return $this->belongsTo(Poeple::class, 'mother_id');
}
Since I can pick up the parents directly, I wonder if it is possible with Eloquent and Laravel to recover the parents of the parents, the parents of the grandparents, etc.
Is there a relationship to do that? Or a tip?
I do not know if it's possible to do:
I look if the child has a parent.
If he has a parent, I look if his parent has a grandparents
And so on (maximum 4)
At the end, I create a collection with all that. It's possible ?
Nested set does not seem to work for me. I have two parents.
Thank you

There is a simple trick you can achieve this.
use $appends
protected $appends = ['father'];
public function father()
{
return $this->belongsTo(Poeple::class, 'father_id');
}
public function mother()
{
return $this->belongsTo(Poeple::class, 'mother_id');
}
Above code will give you the nested results with every collection.

Related

Laravel 8: Can I make Eloquent scope but NOT against query builder but against related model?

I have a model - let's call it Parent.
Each Parent can have many children.
public function children(): HasMany
{
return $this->hasMany(Child::class, 'parent_id', 'id');
}
I want to make a scope for Parent called "active".
My requirement goes as follows:
Parent is active when it has at least 1 child.
I've done local scopes multiple times and I know that within Parent.php I can do something like:
public function scopeActive($query)
{
return $query->where(…);
}
But as you see, $query is a \Illuminate\Database\Eloquent\Builder which is not my case. I want to operate on related model somehow, not the DB query.
Can I do this in a clean way without fetching all parents with('child') and within ->filter() forgetting those that have no children? When there will be 1000 parents but only one will have children, it will simply waste DB resources (by fetching 999 redundant parents).
You can use the following query
public function scopeActive($query)
{
return $query->has('children');
}
One option is to use whereHas in scope:
public function scopeActive($query){
return $query->whereHas('children');
}
OR
public function scopeActive(){
return $this->whereHas('children');
}
And
Parent::active()->with('children')->get();
should give you all Parents who have children and along with respective children
Can you try something like:
public function getActiveAttribute() {
return $this->children()->count() > 1;
}
This can be used like,
$parent->active
And the query will only be made when you'll try to access the active property on the Parent model, not when you will be fetching all parents.
In Parent
public function getActiveAttribute() {
return $this-> children()->exists();
}
In code
$parent = new Parent;
if($parent->active){//true}
You can also use count
count($this->children); // 0 evaluates to false
a related thread here if you need:
Laravel Check If Related Model Exists

Eloquent nested complex relationship

I have some trouble with chaining relationships. I want to chain three of them, but this is not working properly:
return UserModel::with('cars.pieces.attributes')
I want to retrieve a user with its cars. He chose a car which have pieces and for each pieces he chose an attribute.
With only cars.pieces. I have my user, then the array of cars then the array of pieces for this car. When I add attributes, I have attributes not for pieces of cars of users but attributes for pieces whatever cars it is.
It seems like the relationship is only looking for the previous relation and not the whole packet.
public function cars(){
return $this->belongsToMany(CarsModel::class, 'user_cars', 'id_user','id_cars');
}
Then
public function pieces(){
return $this->belongsToMany(PiecesModel::class, 'cars_pieces', 'id_cars','id_pieces')
}
And finally :
public function attributes(){
return $this->belongsToMany(AttributeModel::class, 'user_cars_pieces_attributes', 'id_attribute', 'id_piece')
}
The last entity is using 4 fields for the primary key :
id_user, id_car, id_attribute, id_piece
What could be a way to retrieve attributes for pieces of cars of the user?
Thank you for helping!
You can pass a function to your eager loading attributes:
return UserModel::with(['cars.pieces.attributes' => function ($query) {
$query->where('id_user', DB::raw('users.id'))->where('id_car', DB::raw('cars.id'));
}]);
I haven't tested this but I think it should work.
Remember to import DB Facade: use Illuminate\Support\Facades\DB;

Laravel Eloquent - Get results from parents that has only one child

I'm trying to get all results that match a certain child id, and that the parent has only on child. My initial idea its to do it by hand with nested foreachs, but is there any method that does this in a simpler way?
My parent is called Proyectos and then I have a pivot table called PaisBeneficiarioProyecto, and the other parent is PaisBeneficiario. It's a many-to-many relation.
$monto_pais = Proyecto::all()->where('pais_beneficiarios', $id)->sum('monto_aprobado');
The code explained above does what you expect, but it's not the result that I need.
Here are my models:
Proyectos:
public function pais_beneficiarios()
{
return $this->belongsToMany(\App\Models\PaisBeneficiario::class, 'pais_beneficiario_proyecto', 'id_proyecto', 'id_pais_beneficiario')
->withPivot('monto_beneficio');
}
PaisBeneficiario:
public function pais_beneficiario()
{
return $this->belongsTo(\App\Models\PaisBeneficiario::class, 'id_pais_beneficiario');
}
public function proyecto()
{
return $this->belongsTo(\App\Models\Proyecto::class, 'id_proyecto');
}
PaisBeneficiarioProyectos:
public function proyectos()
{
return $this->belongsToMany(\App\Models\Proyecto::class, 'pais_beneficiario_proyecto', 'id_pais_beneficiario', 'id_proyecto')
->withPivot('monto_beneficio');
}
The value that I'm trying to sum is called monto_aprobado which is on Proyectos
The below query gives you the sum of monto_aprobado field of Proyecto models that jus have one pais_beneficiarios_count.
Proyecto::withCount('pais_beneficiarios')
->having('pais_beneficiarios_count', 1)
->sum('monto_aprobado');

Return array with all children from a parent table Eloquent

Im using LARAVEL 5.7 and i have 2 tables called "parent_table" with "parent_id,parent_name" and "children_table" with "children_id,children_name". And i have 2 models with the same names of the tables with the following code:
Parent Model
public function children()
{
return $this->hasMany('App\children', 'children_id');
}
Children Model
public function parent()
{
return $this->belongsTo('App\parent', 'parent_id');
}
I have a controller with this code
$data = App\parent::with("children")->get();
return $data;
But it returns me only the first children of every "parent". I want to know what i need to add to the code to get all the children of every parent.
I already try to get all the "parents" and the foreach all the "children" but it would be to many request to database. thanks!
You can do something like this:
#foreach($parents as $parent)
{
$parent->children();
}
This should return a collection from the Children model. For more info you can look here:
https://laravel.com/docs/5.7/eloquent-relationships#one-to-many
After several hours i found the solution. I didn´t know that it was important to make the select to the foreign key... that's why my query wasn´t working.
Final Parent Model
public function children()
{
return $this
->hasMany('App\children',"parent_id")
->select(
"children_id",
"children_nombre",
"parent_id"//I wasn't selecting this guy
)
//->with("children2") // With this i got the way to concatenate a lot of subchildren map :D
;
}
Final children model
public function parent()
{
return $this->belongsTo('App\parent',"parent_id","parent_id");
}
Controller
$data = App\parent::
select(
"parent_id",
"parent_nombre"
)
->with("children")
->get();
return $data;
I learn something new today, I hope this answer helps to other laravel learners too
Using with as you did in your exemple will work. Maybe you have to put 'children' in the $appends array on your model.
namespace App;
class Parent {
...
protected $appends = ['children'];
...
}
this will appends the children result when the model are transformed into array

Yii2. Models related

I have 3 models: Items, Serials and SerialsCategories. When I show Item form (to create or update) I need to show the serials which belongs to a categoryId selected in a previous step. A serial can belong to more than one category.
Right now I have on my Item model:
public function getSerialsTypeByCategory() {
return (new SerialType)->getByCategory($this->itemCategoryId);
}
On my SerialType model:
public function getByCategory($itemCategoryId) {
return SerialTypeItemCategory::find()->select(['serialTypeId'])->where(['itemCategoryId' => $itemCategoryId])->all();
}
This is working, it does what I need but ... Is this the proper way? is there a better way?
it's not wrong what you are doing. but there is something more -
check this link:
Working with Relational Data
if you use ->hasOne and ->hasMany to define relations, your model gains some extra benefits, like joining with lazy or eager loading:
Item::findOne($id)->with(['categories'])->all();
with a relation, you can also use ->link and ->unlink, to add/delete related data without having to think about linked fields.
Further, it is easy to define relations via junction table:
class Order extends ActiveRecord
{
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->viaTable('order_item', ['order_id' => 'id']);
}
}

Categories