Laravel recursive where for recursive relationship - laravel

I have a table hobbies like this:
-id
-name
-parent_id
and my model
public function sub_hobbies(){
return $this->hasMany(Hobbies::class, 'parent_id');
}
public function parent_hobbies(){
return $this->belongsTo(Hobbies::class, 'parent_id');
}
public function allsub(){
return $this->sub_hobbies()->with('allsub');
}
public function allparent(){
return $this->parent_hobbies()->with('allparent');
}
What i want is to get all hobbies which are not child or grand child of given hobbies
example i have this list:
-hobbies 1
-hobbies 11
-hobbies 12
-hobbies 121
-hobbies 122
-hobbies 13
-hobbies 2
-hobbies 21
-hobbies 22
-hobbies 221
-hobbies 222
-hobbies 23
-hobbies 3
-hobbies 31
-hobbies 32
-hobbies 321
-hobbies 322
-hobbies 33
if i give id of hobbies 1, i want all hobbies except hobbies 11, 12, 121, 122, 13

So i found a little way to make run what i want to do but i don't know if it's the right way:
// get hobbies and exlude given hobbies and his child line
public function scopeIsNotLine($query, $id){
// get all hobbies to exclude by id and his child line
$hobbies = Hobbies::with('allsub')->where('id', $id)->get()->toArray();
// transform result to array of id of hobbies to exclude
$exclude = collect($this->flatten($hobbies))->map(function ($item) {
return collect($item)
->only(['id'])
->all();
})->flatten()->all();
// get hobbies where id not in exluded hobbies
return $query->whereNotIn('id', $exclude)->whereDoesntHave('is_archive');
}
// transform nested result to simple array without nest hierarchie
private function flatten($array) {
$result = [];
foreach ($array as $item) {
if (is_array($item)) {
$result[] = array_filter($item, function($array) {
return ! is_array($array);
});
$result = array_merge($result, $this->flatten($item));
}
}
return array_filter($result);
}
Firts i retrieve the hobbies selected with his all child and grand child
Second, i transform the result into flatten array (list all without nested relationship) with the function flatten that i created
Third i transform again into array of id
Fourth i use this array in whereNotIn to retrieve all hobbies and exclude the hobbies selected and his child line
Fifth i used my scope like Hobbies::isNotLine('the-id-selected')->get();
In result i got all hobbies excluding the hobbies i selected, and his child and grand child

Related

How to merge categories table with items table and returtn array of each category with items

I have three tables and after each table is the defined eloquent relationship of each entity.
categories
id
name
1
cars
2
bikes
public function items()
{
return $this->belongsToMany(AccountItem::class, "category_item", "category_id", "item_id");
}
category_items
id
category_id
item_id
1
1
1
2
1
2
items
id
name
1
volks
2
bmw
public function categories()
{
return $this->belongsToMany(Category::class, "category_items", "item_id", "category_id");
}
I am stuck on how to get a function that will return an array of the data with each category and the corresponding items. Incase the category has null items it returns a empty array of the category.
I assume your relationship is correct. If so, you can do this to load.
$categories = Category::with('items')->get();
foreach ($categories as $category) {
$categoryArray = [
'id' => $category->id,
'name' => $category->name,
'items' => $category->items->toArray()
];
}

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!

Get Data through relation in laravel

I'm using this query to get data using relation
return Product::with('attributes')
->whereHas('attributes', function ($query) use ($attribute_id,$attribute_value){
$query->whereIn('attribute_id', $attribute_id);
$query->whereIn('value', $attribute_value);
})
->paginate(10);
$attribute_id and $attribute_value are arrays, i'm getting data using this relation but when $attribute_id and $attribute_value are empty then i'm not getting any result but it should return result through product table if there are no attributes.
I have changed it to something like this:
if(!empty($attribute_id))
{
$query->whereIn('attribute_id', $attribute_id);
}
if(!empty($attribute_value))
{
$query->whereIn('value', $attribute_value);
}
model relation :
public function attributes()
{
return $this->hasMany(ProductsAttribute::class, 'product_id ');
}
Table:
id | Title | Price
1 Title 1 5000
2 Product 2 7000
this is related to product_attribute table
id | product_id | attribute_id | attribute_name | value
1 1 5 Color Red
2 1 6 Size XL
3 2 5 Color Green
Is there any other way to make a check in query so that if attributes are not provided then atleast product data should return.
A cleaner way might be to use local scopes. That way, your controllers will look cleaner. It also makes the code easily testable. Here is how I might approach this.
NOTE: Code hasn't been tesed.
In your controller:
return Product::with([
'attributes' => function ($query) use ($attribute_id, $attribute_value) {
return $query->inAttributeId($attribute_id)
->inAttributeValue($attribute_value);
}
])->paginate(10);
In your ProductsAttribute model:
public function scopeInAttributeId($query, $attributeId)
{
if (empty($attributeId)) {
return $query;
}
return $query->whereIn('attribute_id', $attributeId);
}
public function scopeInAttributeValue($query, $attributeValue)
{
if (empty($attributeValue)) {
return $query;
}
return $query->whereIn('value', $attributeValue);
}
As you can see, the scopes modify the query only if there are values in the passed in array. If the passed in array is empty, it returns the original query.

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);
}

Laravel 5.8 create a recursive collection

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;
}

Resources