Structuring siblings relation on same model in Laravel - laravel

I have student model. I want to add siblings from existing students table on user profile page. I am selecting sibling from a dropdown. Also I want to add the parents of old student(sibling) to the new student(sibling). For student parent I have many-to-many relationship and I am saving student_id and parent_id in pivot table
How can I structure and add this sibling part?

I think the best way to represent this family relations (parents , children , siblings) is to put a nullable parent_id in students table,
so when this parent_id is null this mean that the object is a main parent otherwise it's just a child.
After that you can define some relations :
public function children()
{
return $this->hasMany(self::class, "parent_id");
}
public function parent()
{
return $this->belongsTo(self::class, "parent_id");
}
and for the siblings , I don't know if there is a way to write a relation , but you can return the siblings like so :
$siblings = Student::where("parent_id", $this->parent_id)->get();

Related

How to get products of a "main category" if a product is related to only one "subcategory"?

How do I get all products of a “main category” if a product is related to only one “subcategory”? A product is only related to a sub-category, while a sub-category is always part of the main category. So I want to have all products in the main category. A query like the below would not work or return no products because no product is related to category #1.
Categories::where(['id' => 1])->products();
Models/Category.php
public function parent(): BelongsTo
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function children(): HasMany
{
return $this->hasMany(Category::class, 'parent_id');
}
Models/Product.php
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
What do I need to do/change to get all products of the main category (preferably without checking if the category with ID #1 is the main category)?
One common solution to this problem of querying a hierarchical data structure is a Closure Table. There are many discussions of this pattern online, so I won't attempt to restate fully, but the short summary is that you store every path between each object and all of its ancestors along with the depth between them. This gives you a table with columns (ancestor_id, descendant_id, depth), so you can join through that table to collect all objects linked to any of a given descendant's ancestors, or any ancestor's descendants.
Here is an example query for how this works in practice to query all descendants of a given ancestor category, possibly with some syntax issues because I don't have a real database to run this against.
SELECT products.* FROM products
INNER JOIN category_closure ON products.category_id = category_closure.descendant_id
WHERE category_closure.ancestor_id = 1;
We currently use this solution for virtually the exact same problem, products assigned anywhere within a hierarchy of categories. However, we are doing this in a Doctrine project, so implementation details of our solution likely wouldn't help here.
There are existing libraries to make this easier in Laravel, such as https://github.com/franzose/ClosureTable (I can't vouch for quality, just found now in a search).
You can use hasManyThrough relation. You check from this link
Add this code to your Category model:
public function products(){
return $this->hasManyThrough(
Product::class,
Category::class,
"parent_id",
"category_id",
"id",
"id"
);
}
Maybe, I am wrong with your foreign and primary keys. Please check it.

Eloquent custom relationship hasMany (foreign field contains text concatenated by foreign key)

I have this database structure. 2 tables: shipment_out, stock_move.
shipment_out has the typical primary key integer id field.
stock_move has a field named shipment which is string type. This field can have these values:
"stock_shipment_out,1512",
"stock_shipment_in,65400",
"sale.line,358",
(...)
The thing is the table stock_move is related to a multiple tables based on the same field, so it has this text before.
In this case I want to define the relationship: shipment_out hasMany stock_move.
So I need to join by stock_move.shipment has this value: 'stock_shipment_out,{id}'.
So how can I define this relationship? Would be something like:
public function stockMoves()
{
return $this->hasMany(StockMove::class, 'shipment', 'stock.shipment.out,id');
}
I can achieve this relationship with query builder:
$shipments = ShipmentOut
::join('public.stock_move', DB::raw('CONCAT(\'stock.shipment.out,\',public.stock_shipment_out.id)'), '=', 'stock_move.shipment')
->where('stock_shipment_out.id', '=', $shipmentOut);
But I need on a relationship too...
To solve this problem I had to define a custom attribute, and then I can define the relationship with this field.
public function getStockMoveShipmentAttribute()
{
return "stock.shipment.out,{$this->id}";
}
public function stockMoves()
{
return $this->hasMany(StockMove::class, 'shipment', 'stock_move_shipment')
}
Now I can use this relationship, but it's only one-direction...
If I want to define the same relationship as the inverse it doesn't work.
I opened another question explaining it: Laravel relationship based on custom attribute not working both directions

One To Many with pivot not possible. How to redesign relationships?

I would like to do the following:
Product can have one Supplier. The Supplier contains some data like a name. Additionally there is a pivot-value that should be stored for Supplier that is assigned to Product (e.g. a delivery_service-string).
Example:
Product: A yummy Banana
Supplier: Banana Inc
-> If the Product "A yummy Banana" is supplied by Supplier it should be delivered by DHL. The important thing: You can not add DHL as a field to Supplier, as each Product to Supplier-relation should have it's own delivery-service-field.
As there can be many Products but each Product can only have one Supplier I thought about something like this:
Product
Schema:
- id
- supplier_id
Relation
public function supplier()
{
return $this->belongsTo(Supplier::class);
}
Supplier
Schema:
- id
- name
Relation
public function products()
{
return $this->hasMany(Product::class);
}
This works, but unfortunately I can not store pivot data in the supplier() relation.
At the moment I could imagine to store the value not in a pivot table but in a new row in the Product's schema. But I don't think this is the best way.
Any suggestions? :-)
One item have many childs
This is one-to-many
You need to store item_id in your child table
Item Table
id, name
Child Table
id, item_id, type
If you have a case EG
Each child can have a favorite item then you will have to create a many-to-many relation between them. (Which will be a separate relation)
Create a table "child_item" (pivot)
Child Item Table
child_id, item_id
then create a relation in your laravel
class User extends Model{
...
public function favorites(){
return $this->belongsToMany(Items::class); // Include the path using "use" on top
}
}
So now you get your 2 relations between child and item
One item can have many children
Many children can mark items as favorites

Eloquent custom "belongs to" relationship on multiple tables

I have the following DB structure:
Table Vehicles: id, car_id, plane_id
Table Cars: id, model...
Table Planes: id, model...
When a new record added to the table Vehicles, if it is a Car, the car_id will be set, while the plane_id will be left empty, and vice-versa, I know it's a bad structure, but it is legacy and I can't change it.
So I want to define a relationship in the Vehicle model where it can retrieve the Car object or the Plane object according to which key is empty, the car_id or the plane_id. And btw, I've already defined two relationships that will retrieve the Car object and the Plane object separately.
public function carVehicle()
{
return $this->belongsTo(Car::class, 'car_id');
}
public function planeVehicle()
{
return $this->belongsTo(Plane::class, 'plane_id');
}
This would be best handled by a Polymorphic relationship (see https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationship for details), but there's ways around this if your current model doesn't match the structure and you can't change it.
You could have a third method that adds both to a Collection and returns the first() one (since you say one of car_id or plane_id will always be null):
Vehicle.php:
public function getChildVehicleAttribute(){
return collect([$this->carVehicle, $this->planeVehicle])
->filter(function($record) {
return $record != null;
})->first();
}
Then, you'd access via the following query:
$vehicle = Vehicle::with(['carVehicle', 'planeVehicle'])->first()->child_vehicle;
// OR
$vehicles = Vehicle::with(['carVehicle', 'planeVehicle'])->get();
foreach($vehicles AS $vehicle){
$childVehicle = $vehicle->child_vehicle;
// dd($childVehicle, etc.)
}
The with() clause would eagerload both relationships so $this->carVehicle and $this->planeVehicle don't trigger additional database calls, and calling child_vehicle on any Vehicle instance would return either a Car or a Plane (or null if neither is defined)

How do I fetch all parents in a self relationship?

I have a User table with a self relationship.
the table has the fields like it follows:
id
parent_id (FK to User)
name
A User can have a parent, and this parent can have a parent and such on until infinite(although I will probably go like 3-4 levels tops the application sets no limit).
How do I fetch all the genealogical tree from a user that has no children(bottom of tree user) excluding siblings.
For instance:
I have user1, his parent is parent1 which has a grandparent1. If grandparent1 has other children(like a supposedly parent2) I don't want to fetch that. Just user1->parent1->grandparent1. Is there any way I can loop associations until it is not found?
EDIT1: I really want to use QueryBuilder for this. Making a foreach calling a parent() method until it returns null will probably not be the best solution due to optimization
Try with this:
class User extends Model {
public function parent()
{
return $this->belongsTo('App\User', 'parent_id');
}
public function children()
{
return $this->hasMany('App\User', 'parent_id');
}
}

Resources