Laravel 5.8 create a recursive collection - laravel

I have a table that contains branches, departments and teams. A branch can have many departments, and a department can have many teams. The class is called 'Organisation`.
The organisations table:
id parent_id type name
1 null b Branch1
2 null b Branch2
3 1 d Dep1
4 2 d Dep2
5 3 t Team1
6 4 t Team2
I am trying to generate a method in the class that will recursively return all children records for a given position in the tree.
I have a relationship up called children:
/**
* Children
*/
public function children()
{
return $this->hasMany(Organisation::class, 'parent_id', 'id');
}
I have created a method called tree in the class as follows:
/**
* Organisation Tree
*/
public function tree()
{
$merged = new Collection;
foreach($this->children as $child) {
$merged->merge($child->tree());
}
return $merged;
}
So for example:
$o = Organisation::find(1);
$treeCollection = $o->tree();
And the $treeCollection should contain:
id parent_id type name
1 null b Branch1
3 1 d Dep1
4 3 t Team1
But the above method returns and empty collection and it does not fail. What did I miss?

This was my solution:
/**
* Organisation Tree
*/
public function tree()
{
$merged = new Collection;
$merged = $merged->merge($this->children);
foreach($this->children as $child) {
$merged = $merged->merge($child->tree());
}
return $merged;
}

Related

Laravel createMany on createMany while preserving relation

I have the same issue as this question asked. However, I would like to insert the data on the nested createMany with it's relation, and not insert multiple entries like:
id question_id answer
1 1 Good
2 1 Meh
3 2 Good
4 2 Meh
So if the question_id is 1 and the answer on that question is Good and for question_id 2, the answer on that question is Meh so that result should be:
id question_id answer
1 1 Good
2 2 Meh
Basically what I have is:
A User hasMany orders
An Order hasMany sub orders
A SubOrder hasMany sub order products
Here are the relations:
User Model:
class User extends Model
{
public function orders() {
return $this->hasMany(Order::class);
}
}
Order Model:
class Order extends Model
{
public function user() {
return $this->belongsTo(User::class);
}
public function subOrders() {
return $this->hasMany(SubOrder::class);
}
}
SubOrder Model:
class SubOrder extends Model
{
public function order() {
return $this->belongsTo(Order::class);
}
public function subOrderProducts() {
return $this->hasMany(SubOrderProducts::class);
}
}
SubOrderProducts Model:
class SubOrderProducts extends Model
{
public function subOrder() {
return $this->belongsTo(SubOrder::class);
}
}
Here are the tables:
orders table:
id name
sub_orders table:
id order_id date price
sub_order_products table:
id sub_orders_id product_id
Then on the store method:
$order = auth()->user()->orders()->create($request->validated());
$order_subs = $order->subOrders()->createMany($request->orders);
foreach ($order_subs as $order_sub) {
$order_sub->subOrderProducts()->createMany($request->orders);
}
The $request->orders data is:
{
"name": "My Order",
"orders": [
{
"date": "2022-05-17",
"product_id": [1],
"price": 1
},
{
"start_date": "2022-05-18",
"product_id": [2],
"price": 2
}
]
}
This means that the order has two sub orders, with their corresponding products. I end up getting:
orders table:
id name
1 Super Order
sub_orders table:
id order_id date price
1 1 2022-05-17 1
2 1 2022-05-18 2
sub_order_products table:
id sub_orders_id product_id
1 1 1
2 1 2
3 2 1
4 2 2
My desired output would be to preserve the relation on the sub_order_products table, save and match the correct IDs. That would be:
sub_order_products table:
id sub_orders_id product_id
1 1 1
2 2 2
I'm thinking to change request->orders data and have some IDs for the sub_orders_id or maybe nest the createMany methods but I can't figure out how to. How to achieve this? Thanks!

How to establish a dynamic model relationship in Laravel?

I need to establish a dynamic relationship but I can't.
The table design is as follows.
Page Table
id
title
1
Hello world
2
Contact
Category Table
id
title
1
Electronics
2
Sports
Blog Table
id
title
1
First blog
Links Type
id
name
1
Page
2
Category
3
Blog
Links Table
id
type_id
relation_id
slug
1
1
1
page/hello-world
2
1
2
page/contact
3
2
1
category/electronics
3
2
2
category/sports
3
3
1
blog/first-blog
CODE
Controller:
$links = Links::with('title')->get();
return response()->json($links);
// I need to get the "title" key.
Links Model:
public function title() {
switch($this->type_id) {
case '1':
return $this->hasOne(Page::class, 'id', 'relation_id');
break;
case '2':
return $this->hasOne(Category::class, 'id', 'relation_id');
break;
case '3':
return $this->hasOne(Blog::class, 'id', 'relation_id');
break;
}
}
This state does not work because the model has not yet been formed.
Error Output
Call to a member function addEagerConstraints() on null
How should I go about it?
Thanks.
What you are doing can be achieved by using polymorphic relationships.
pages
id - integer
name - string
categories
id - integer
title - string
blogs
id - integer
title - string
link_types
id - integer
name - string
links
id - integer
link_type_id - integer
linkable_id - integer
linkable_type - string
Page, Category and Blog model should define these relationships
public function link_types()
{
return $this->morphToMany(LinkType::class, 'linkable', 'links')->using(Link::class);
}
public function links()
{
return $this->morphMany(Link::class, 'linkable')
}
LinkType model should define these relationships
public function pages()
{
return $this->morphedByMany(Page::class, 'linkable', 'links')->using(Link::class);
}
public function categories()
{
return $this->morphedByMany(Category::class, 'linkable', 'links')->using(Link::class);
}
public function blogs()
{
return $this->morphedByMany(Blog::class, 'linkable', 'links')->using(Link::class);
}
Link model should define these relationships
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Link extends MorphPivot
{
/**
* Get the parent linkable model (blog, category, page).
*/
public function linkable()
{
return $this->morphTo();
}
public function link_type()
{
return $this->belongsTo(LinkType::class, 'link_type_id');
}
}
Eloquent Relationships - One To Many Polymorphic Relations
Eloquent Relationships - Many To Many Polymorphic Relations
$link = Link::with('linkable')->first();
$link->linkable->title;

How to synchronise child table data on hasMany relationship?

I have 2 table products and variants, with a hasMany relationship, the structure is below :
Product Model :
public function variants()
{
return $this->hasMany(Variant::class);
}
Variant Model :
public function product()
{
return $this->belongsTo(Product::class);
}
Product Table :
| id name image manufacturer
| 1 T-Shirt t-s.jpg docomo
| 2 Short Skirt s-skirt.jpg docomo
Variant Table :
| id product_id name price sku quantity
| 1 1 S 30 ts-s 100
| 2 1 M 32 ts-m 100
| 3 1 XL 35 ts-xl 100
| 4 2 S 23 sk-s 100
| 5 2 M 25 sk-m 100
| 6 2 XL 28 sk-xl 100
I can save data on Variant model (child table) from Product model as :
public function store(Request $request)
{
$q = new Product;
$q->name = $request->name;
$q->save();
// Below child table
$q->variants()->createMany($request->variant);
}
I can store data, but the problem is that, how can I update child table? [It can be create, update or delete]
I tried with sync() method, but didn't work. Its for manyToMany relationship.
public function update(Request $request)
{
$q = Product::findOrFail($request->id);
$q->name = $request->name;
$q->save();
// Below child table update
// $q->variants()->sync($request->variant); // not worked
}
sync() works in manyToMany relationship. I need the same for hasMany relationship.
How can I do that?
Unfortunately there is no sync method for hasMany relations. It's pretty simple to do it by yourself. You can simple delete the rows and insert them all again.
public function update(Request $request, Product $product)
{
$product->update(['name' => $request->name]);
// Below child table update
$product->variants()->delete();
$product->variants()->sync($request->variant);
}

how to fetch record in one table with self join in laravel

in categories table 3 fields
id categoryName parent_id
1 Architect 0
2 Vendor 0
3 Res Architect 1
4 Electrician 2
in my model
public function parent()
{
return $this->belongsTo(Category::class, 'id');
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
in controller
SELECT t2.categoryName FROM categories t1, categories t2
WHERE t1.id = id
AND t2.parent_id = t1.id
i have no idea to fetch record with self join into one table in laravel
please help
You have to modify the parent relationship a little bit, it should be parent_id because child category reference parent category by using parent_id field.
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
You can do it like this
Get all parent categories with its children
$parentCategories = Category::with('children')->where('parent_id', 0)->get();
Get all child with their parent
$childCategories = Category::with('parent')->where('parent_id','>', 0)->get();
Now fetch it like this
foreach($parentCategories as $parentCategory){
dd($parentCategory->children); //children for parent category
}
Similarly for child categories
foreach($childCategories as $childCategory){
dd($childCategory->parent); //parent of child category
}
Fetch Electrician (As per your request in comment)
$electrician = Category::with('parent')->where('id', 4)->first();
dd($electrician->parent); //this will be Vendor
OR if you want to fetch all child of Vendor then
$vendor = Category::with('children')->where('id', 2)->first();
dd($vendor->children); //children of vendor

belongsToMany multiple foreign keys, laravel

Good morning
I currently have a table that is related to 3 tables.
It has occurred to me to do "belongsToMany", but I do not know how to do it with 3 relationships
Tables:
schools_series (relation)
id
cycle_id
school_id
serie_id
cycle
id
active (true / false)
** more columns **
school
id
** more columns **
serie
id
** more columns **
Principal Model:
School
class School extends Model implements Presentable
{
use PresentableTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'network_id',
'fantasy_name',
'social_name',
'email',
'sim_web_code',
'state_registration',
'cnpj',
'status',
'start_date',
];
public function series()
{
return $this->belongsToMany(Serie::class, 'schools_series')->where('id_cycle', 'xxxx');
}
I need to be able to take out the "school series" depending on the "cycle_id" that has "active" in cycle
example SQL:
SELECT ss.* FROM schools as s
INNER JOIN schools_series as ss
ON ss.school_id = s.id
INNER JOIN cycle as c
ON ss.cycle_id = c.id AND c.active = 1
WHERE s.id = 115
Solution:
public function series()
{
return $this->belongsToMany(Serie::class, 'schools_series')->withPivot('cycle_id')->join('cycle', 'cycle.id', '=', 'schools_series.cycle_id')->where('cycle.active', '=', 1);
}

Resources