How to loop through children and grandchildren in laravel - php

I'm trying to find a way to do \App\Goal::find(1)->children and get returned all children, and childrens children.
With the database pasted below I want
\App\Goal::find(1)->children
to return
2, 4 and 5. Currently I can only do \App\Goal::find(1)->goals, which returns only 2 and 4
I have a database like this:
id user_id goal_id objective
1 1 NULL Get rich
2 1 1 Save $5
3 1 NULL Learn to cook
4 1 1 Save $10,000
5 1 4 Buy stocks
6 1 5 Buy 5x Intel
7 1 5 Buy 5x AMD
How would you go about creating the children() function in the Goal model?
Please ask if any important info is missing from this
Edit:
Goal.php:
public function user()
{
return $this->belongsToUser(User::class);
}
public function goals()
{
return $this->hasMany(Goal::class, 'goal_id')
}
When I run \App\Goals::find(1)->goals in the GoalController it returns the children of itself (with id 2 and 4 from the example db table), but not its grandchildren...
ID 5 from the example table has a parent of id 4, which itself has a parent id of 1.
So how could I get \App\Goal::find(1)->children or \App\Goal::find(1)->goals to return its grandchildren, and their children etc?

So, assuming the table you've posted is the goals table, related to App\Goal...
If you have these two relationships set up on the model:
public function children()
{
return $this->hasMany(Goal::class, 'parent_id');
}
public function childrenRecursive()
{
return $this->children()->with('children');
}
You can call Goal::with('childrenRecursive')->get() which will return a nested collection of all the children and children's children.
If you'd like all of the children in a flat array, you could do something like this:
public function getFamilyAttribute()
{
$family = collect([]);
$children = $this->children;
while (!is_null($children )) {
$family->push($children);
$children = $children->children;
}
return $family;
}

Related

How to sum field base on status field with eloquent laravel (with)

I have case sum of data
actual result: return 0
Expected result not return 0.
I have table like
Transaction
id
user_id
amount_load
status
1f
3edf
100000
1
Log Transaction
id
trx_id
amount_pay
status
32f
1f
10000
1
23d
1f
50000
1
12e
1f
1000
3
info about status:
ID
Meaning
0
not paid
1
paid not full
2
paided
3
refunded
My code on controller
$data = Transaction::select('user_id')->groupBy(‘user_id’)
->with([LogTransaction => function ($query) {
$query->sumRefund();
}]);
Model Transaction.php
public function scopeSelectDefault($q) {
return $q->selectRaw(‘id, user_id, amount_load, status’);
}
//RELATION
public function logTransaction()
{
return $this->hasMany('App\Models\LogTransaction', 'trx_id', 'id');
}
Model LogTransaction.php
public function scopeSumRefund($q) {
return $q->selectRaw(‘coalesce(SUM(amount_pay) FILTER (WHERE status = '3'),0) AS total_refund’);
}
// RELATION
public function transaction($query, $order)
{
return $this->belongsTo('App\Models\Transaction', 'id');
}
expected result: total_refund on relation resulting 1000 base status = 3
other information:
laravel ^6.0
postgres 12.0
please help
Firstly you can use the Laravel custom attribute option and you can define it in your Transactions Model like so
public function getTotalRefundAttribute() {
return $this->logTransaction ? $this->logTransaction->where('status', 3)->sum('amount_pay') : null;
}
And now when you get any Transaction instance it will have an extra attribute called total_refund where you can see the total amount. But make sure you actually have eloquent relations setup between Transaction and LogTransactions models

How to properly retrieve and store self referencing table relationship in Laravel?

Hi i'm just started to learn laravel and i have 2 database tables that i want to modify, the first one is the main table which is products and the other table is upsells which will connect two entity from products table:
products
id
name
price
1
Bag
300
2
Belt
100
3
ring
120
4
Hat
250
5
Scarf
125
upsells
id
product_id_1
product_id_2
1
2
1
2
2
4
3
2
5
4
5
4
5
5
1
the idea is product enitty can be connected to the one or more product using the upsells table. the product_id_1 will be the main product that will point to the product_id_2
Currently i'm using this method to retrieve the relationship:
public function getUpsells($mainProductId){
$upsells = Upsell::where('product_id_1', $mainProductId);
$results = array();
foreach($upsells as $upsell){
$results[] = $upsell->product_id_2;
}
return $results;
}
$mainProductId = 5;
$relatedProducts = array(2,3);
public function updateUpsells($mainProductId,$relatedProducts){
foreach($relatedProducts as $relatedProduct){
//create if not exists
$upsell = Upsell::where('product_id_1', $mainProductId)->
where('product_id_2', $relatedProduct)->first();
if(empty($upsell->id)){
$upsell = new Upsell;
$upsell->product_id_1 = $mainProductId;
$upsell->product_id_2 = $relatedProduct;
}
}
//delete if not in the array input
Upsell::where('product_id_1', $mainProductId)->
whereNotIn('product_id_2', $relatedProducts)->delete();
}
Is there any simplified method or any method that i'm missing based on laravel best practice ?
You can use eloquent relationships. Based on your code I think you have these relationships between your models:
Products hasMany upSells,
so in your Products.php you will have to add relation like this:
public function upSells(){
return $this->hasMany(UpSells::class, 'product_id_1', 'id');
}
This way you can fetch upsells from products like this:
$products = Product::with('upSells')->find(1);
The same thing you can do with your other relationships. You just have to define which will be the parent and which will be the child in the relationship.
For more information, you can go through this.
CURRENT SOLUTION WHICH IS NOT RECOMMENDED
public function getUpsells($mainProductId)
{
return Upsell::where('product_id_1', $mainProductId)->pluck('product_id_2'); //This will give you array of product_id_2.
}
$mainProductId = 5;
$relatedProducts = [2, 3];
public function updateUpsells($mainProductId, $relatedProducts)
{
Upsell::where('product_id_1', $mainProductId)->delete();
foreach($relatedProducts as $relatedProduct){
$upsells = [
[
'product_id_1' => $mainProductId,
'product_id_2' => $relatedProduct
]
];
}
Upsell::insert($upsells);
}
RECOMMENDED SOLUTION
Important docs,
https://laravel.com/docs/8.x/eloquent-relationships
https://laravel.com/docs/8.x/migrations
You have to make a relation in the product model make migration of pivot table.
public function upsells()
{
return $this->hasMany(Upsell::class, 'product_id_1', 'id')
}
then getUpsells() and updateUpsells() be like,
public function getUpsells($mainProductId)
{
$products = Product::with('upsells')->findOrFail($mainProductId);
return $products->upsells->product_id_2;
}
$mainProductId = 5;
$relatedProducts = [2, 3];
public function updateUpsells($mainProductId, $relatedProducts)
{
$product = Product::findOrFail($mainProductId);
$product->upsells()->sync($relatedProducts);
}

Laravel Eloquent multi level Self Join

How to do multi level self join in laravel 5.4
i have table like this.
ID name ParentId
1 abc 0
2 acd 1
3 ads 1
4 xyz 2
5 xxy 2
6 plm 3
7 ytr 4
8 lks 6
Now i need:
# if i call id 1 it will return full tree under it.
# if i call it empty it will return full tree
# i did this
public function getParent() {
return $this->belongsTo(self::class, 'ParentId','id');
}
public function getChild(){
return $this->hasMany(self::class, 'ParentId','id');
}
its giving me single brunch but i need full of them.
some one plz help.
public function chartLedgre($headId) {
$mainHead = self::where('id',$headId)->get();
if(count($mainHead[0]->childs) > 0){
foreach ($mainHead[0]->childs as $child){
if($child->chart_type == 'L'){
$this->data[] = $child->id;
}else{
$this->chartLedgre($child->id);
}
}
}else{
if($mainHead[0]->chart_type == 'L'){
$this->data[] = $mainHead[0]->id;
}
}
return $this->data;
}

Laravel Eloquent get json from nested mysql data

I have tabel like this
id name parent_id order_id
1 software 0 1
2 hardware 0 2
3 windows 1 3
4 linux 1 4
5 mouse 2 5
6 keyword 2 6
And I use Laravel 5.1 Eloquent
How to get data in model like this
[{"id":1,"children":[{"id":3},{"id":4}]},{"id":2,"children":[{"id":5},{"id":6}]}]
There is a best way to do this.
The package etrepat/baum is so awesome and easy. It has everything you need about nesting elements. Just add it to your composer dependencies and enjoy.
You can also add these methods to your Model and use them as relations.
public function parent() {
return $this->belongsTo(self::class, 'parent_id');
}
public function children() {
return $this->hasMany(self::class, 'parent_id');
}
Then you will simply say:
$results = MyModel::with('children')->get();
Update for comment:
$results = Category::select('id','name')->with([
'children' => function($query) {
$query->select('id', 'parent_id');
// You can customize the selected fields for a relationship like this.
// But you should select the `key` of the relationship.
// In this case it's the `parent_id`.
}
])->get();

Laravel parent / child relationship on the same model

The Setup And Dummy Data
I have a simple model called Category, which has the following schema:
|----------------------------------------------|
| cat_id | cat_name | parent_id |
|----------------------------------------------|
| 1 | Home | 0 |
|----------------------------------------------|
| 2 | Products | 1 |
|----------------------------------------------|
| 3 | Services | 1 |
|----------------------------------------------|
| 4 | Product A | 2 |
|----------------------------------------------|
| 5 | Product B | 2 |
|----------------------------------------------|
The Desired Output
So you can see that we would get a very straight forward hierarchy as follows:
Home
- Products
- Product A
- Product B
- Services
The Issue
I'm trying to map this relationship in Laravel 4.2, so that I can query a model and get its parent (it will always have a parent), and child categories if they exist.
I've defined the relationship in the Category model using:
public function children()
{
return $this->hasMany('Category', 'parent_id', 'cat_id');
}
public function parent()
{
return $this->belongsTo('Category', 'parent_id');
}
The Problem
I can get the parent name working, using
$category = Category::findOrFail($id);
return $category->parent->cat_name;
However, I don't understand how to get the child objects.
I've tried:
$category = Category::findOrFail($id);
$children = $category->children();
But when I dd($children) it doesn't output what I'd expect.
Calling the relationship function (->children()) will return an instance of the relation class. You either need to call then get() or just use the property:
$children = $category->children()->get();
// or
$children = $category->children;
Further explanation
Actually children() and children are something pretty different. children() just calls the method you defined for your relationship. The method returns an object of HasMany. You can use this to apply further query methods. For example:
$category->children()->orderBy('firstname')->get();
Now accessing the property children works differently. You never defined it, so Laravel does some magic in the background.
Let's have a look at Illuminate\Database\Eloquent\Model:
public function __get($key)
{
return $this->getAttribute($key);
}
The __get function is called when you try to access a property on a PHP object that doesn't actually exist.
public function getAttribute($key)
{
$inAttributes = array_key_exists($key, $this->attributes);
// If the key references an attribute, we can just go ahead and return the
// plain attribute value from the model. This allows every attribute to
// be dynamically accessed through the _get method without accessors.
if ($inAttributes || $this->hasGetMutator($key))
{
return $this->getAttributeValue($key);
}
// If the key already exists in the relationships array, it just means the
// relationship has already been loaded, so we'll just return it out of
// here because there is no need to query within the relations twice.
if (array_key_exists($key, $this->relations))
{
return $this->relations[$key];
}
// If the "attribute" exists as a method on the model, we will just assume
// it is a relationship and will load and return results from the query
// and hydrate the relationship's value on the "relationships" array.
$camelKey = camel_case($key);
if (method_exists($this, $camelKey))
{
return $this->getRelationshipFromMethod($key, $camelKey);
}
}
Then in getAttribute first is some code that checks for "normal" attributes and returns then. And finally, at the end of the method, if there's a relation method defined getRelationshipFromMethod is called.
It will then retrieve the result of the relationship and return that.
Set this in model and try :
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
public function grandchildren()
{
return $this->children()->with('grandchildren');
}

Categories