How to get data by query builder in laravel - laravel

I use join in query builder and here is the output
[
{
"id": 7,
"name": "class 1",
"idSubject": 17,
"nameSubject": "Math"
},
{
"id": 7,
"name": "class 1",
"idSubject": 16,
"nameSubject": "history"
},
{
"id": 8,
"name": "class 2",
"idSubject": 15,
"nameSubject": "Computer"
},
{
"id": 8,
"name": "class 2",
"idSubject": 19,
"nameSubject": "geography"
}
]
You can see id and name of class 1, 2 appearing twice. So, how to make it appear once.
this is my query:
$data = DB::table('class')->join('class_subject','class.id','=','class_subject.class_id')
->join('subject','class_subject.subject_id','=','subject.id')
->select('class.id','class.name','subject.id as idSubject','subject.name as nameSubject') ->get();
I want it:
[
{
"id": 7,
"name": "class 1",
"subject": [{"idSubject":"17","nameSubject: "Math"},
{"idSubject":"16","nameSubject: "history"}]
}
]

The distinct method allows you to force the query to return distinct results:
Try this
$data = DB::table('class')->join('class_subject','class.id','=','class_subject.class_id')
->join('subject','class_subject.subject_id','=','subject.id')
->select('class.id','class.name','subject.id as idSubject','subject.name as nameSubject')->distinct()->get();

Related

Get certain attribute data from the result of a recursive query

The following example is used to populate a tree and use a table with a parent_id column.
The data is obtained with a recursive query.
$data = [{
"id": 1,
"name": "parent 1"
"note": "note 1",
}, {
"id": 2,
"name": " parent 2",
"note": "note 2",
"children": [{
"id": 21,
"name": "child A of 2",
"note": "note A of 2",
},{
"id": 22,
"name": "child B of 2",
"note": "note B of 2",
},{
"id": 23,
"name": "child C of 2",
"note": "note C of 2",
"children": [{
"id": 231,
"name": "child A of 23",
"note": "note A of 23",
"children": [{
"id": 2311,
"name": "child A of 231",
"note": "note A of 231",
"children": []
}]
}]
}]
}];
And the query:
$myData= Hierarchy::whereNull('parent_id')
->with('children')
->get();
So far so good.
Problem to solve:
It is necessary to obtain a simple (non-hierarchical) list of the id and name attributes of the parents and children.
Example:
"id": 1,
"name": "parent 1",
"id": 2,
"name": " parent 2",
"id": 21,
"name": "child A of 2",
"id": 23,
"name": "child C of 2",
"id": 231,
"name": "child A of 23",
"id": 2311,
"name": "child A of 231"
While this can be solved on the client side with javascript, I intended to do it with eloquent or PHP functions.
I made some attempts with the array_walk() and array_walk_recursive() PHP functions (without success).
Is there any way to solve with eloquent, bearing in mind that the number of children nodes can be infinite?
Thanks.
EDITED:
Example attempt with array_walk_recursive() PHP function
public function getList()
{
$myData= Hierarchy::whereNull('parent_id')
->with('children')
->get();
$data = array_walk_recursive($myData, "self::myFunction");
return response()->json(['success' => true, "data" => $data]);
}
public function myFunction($item, $key){
???
}
You can use the API Resouce recursively or use the recursive function to generate the hierarchy array.
Example with recursive function:
function makeHierarchy($values)
{
$result = [];
foreach($values as $item) {
$result[] = [
'id' => $item->id,
'name' => $item->name,
'children' => makeHierarchy($item->children),
];
}
return $result;
}
$values = Hierarchy::whereNull('parent_id')->with('children')->get();
$hierarchical = makeHierarchy($values);
If you want to get all values as a flat list:
$values = Hierarchy::get();
$result = [];
foreach($values as $item) {
$result[] = [
'id' => $item->id,
'name' => $item->name,
];
}
# now the result contains all the parents and children in a flat list
In the cleaner way:
$result = Hierarchy::select(['id', 'name'])->all();

Filter Data with Pivot Table Laravel Eloquent

I want to filter users based on their subscription_status which s stored in a pivot table.
I have Three tables users , subscription_packages , subscription_package_user
$user=User::with(['studentDetails','subscriptionsSatus.courses'])
->withPagination($offset,$perPage)
->get()
->sortBy('first_name')->values();
this code return the response is
[
{
"id": 44,
"first_name": "Abcd Test",
"last_name": "Test lastname",
"student_details": null,
"subscriptions_satus": [
{
"id": 1,
"name": "Gold",
"price": 3000,
"user_id": "2"
"pivot": {
"user_id": 44,
"subscription_package_id": 1,
"subscription_status": "on_free_trial",
"expires_on": null,
"id": 9
},
"courses": [
{
"id": 18,
"title": "English Grammar for Class 3",
"price": 400,
"strikethrough_price": null,
"status": "draft",
"user_id": 2,
"image": "http://127.0.0.1:8000/courses/1615702915.png",
"description": null,
"pivot": {
"subscription_package_id": 1,
"course_id": 18,
}
}
]
}
]
}]
i want to return only users who having subscription_status =$filter.
$filter='acive'or 'on_free_trail'
my model is
public function subscriptionsSatus()
{
return $this->belongsToMany(SubscriptionPackage::class)->withTimestamps()->withPivot('subscription_status','expires_on','id');
}
I havetried
$filter=$request->input('filter')??"active";
$user=User::with(['studentDetails','subscriptionsStatus.courses'])
->whereHas('subscriptionsStatus', function($query) use($filter){
$query->wherePivot('subscription_status','=',$filter);
})
->withPagination($offset,$perPage)
->get()
->sortBy('first_name')->values();
But Got error Column not found 'pivot'
You need to use wherePivot along with the orWhere like below:
public function subscriptionsStatus()
{
return $this->belongsToMany(SubscriptionPackage::class)
->withTimestamps()
->withPivot('subscription_status','expires_on','id')
->wherePivot(function($q){
return $q->where('subscription_status','=','active')
->orWhere('subscription_status','=','on_free_trail');
});
}
Update
Or in your controller:
$user=User::with(['studentDetails','subscriptionsStatus.courses'])
->whereHas('subscriptionsStatus', function($query) use($filter){
$query->withPivot('subscription_status')
->wherePivot('subscription_status','=',$filter);
})
->withPagination($offset,$perPage)
->get()
->sortBy('first_name')->values();

Flat Laravel collection to tree

I have a Laravel collection of pages - each page has a "parent_id" property. It resembles this.
"pages": [
{
"id": 1,
"title": "Page 1 Level 1",
"parent_id": 0
},
{
"id": 2,
"title": "Page 2 Level 2",
"parent_id": 1
},
{
"id": 3,
"title": "Page 3 Level 3",
"parent_id": 2
},
{
"id": 4,
"title": "Page 4 Level 1",
"parent_id": 0
},
{
"id": 5,
"title": "Page 5 Level 2",
"parent_id": 4
},
{
"id": 6,
"title": "Page 6 Level 3",
"parent_id": 5
},
{
"id": 7,
"title": "Page 7 Level 1",
"parent_id": 0
},
{
"id": 8,
"title": "Page 8 Level 2",
"parent_id": 7
}
]
What I am trying to do is format the output so they are nested with the correct hierarchy. So for example:
"pages": [
{
"id": 1,
"title": "Page 1 Level 1",
"parent_id": 0,
"children": [
{
"id": 2,
"title": "Page 2 Level 2",
"parent_id": 1,
"children": [
{
"id": 3,
"title": "Page 3 Level 3",
"parent_id": 2,
"children": []
}
]
},
]
},
{
"id": 4,
"title": "Page 4 Level 1",
"parent_id": 0,
"children": [
{
"id": 5,
"title": "Page 5 Level 2",
"parent_id": 4,
"children": [
{
"id": 6,
"title": "Page 6 Level 3",
"parent_id": 5,
"children": []
}
]
},
]
},
{
"id": 7,
"title": "Page 7 Level 1",
"parent_id": 0,
"children": [
{
"id": 8,
"title": "Page 8 Level 2",
"parent_id": 7,
"children": []
},
]
},
]
The hierarchy can be any number of levels deep. I have a nearly working version as shown below, but it does contain a bug. Whilst the various child objects are nesting with their parent, they also remain at the root level. So it looks like duplicates are actually placed in their nested positions.
Can anyone help me finish this off?
PageController
$pages = Page::with('children')->get();
Page
public function directChildren(): HasMany
{
return $this->hasMany($this, 'parent_id', 'id');
}
public function children(): HasMany
{
return $this->directChildren()->with('children'));
}
You could try something like this to nest these records:
$c = collect($data)->keyBy('id');
$result = $c->map(function ($item) use ($c) {
if (! ($parent = $c->get($item->parent_id))) {
return $item;
}
$parent->children[] = $item;
})->filter();
All your parent elements seem to have 0 as parent_id. If this is the case, the following query should work:
$pages = Page::where('parent_id', 0)->with('children')->get();
You are basically selecting all parent pages first, then attaching its children tree. Without the where, you are selecting all pages, and attaching their children.
The other option is to define a parent relationship in the model, and select parents with children using something like whereDoesntHave('parent'..)->with(children)
Building on #lagbox's answer:
/**
* Build a tree from a flat list of Pages.
*
* #param Collection|Page[] $pages
* #return Collection|Page[]
*/
public static function asTree(Collection $pages)
{
$pages = $pages->keyBy('id');
return $pages->map(function (Page $page) use ($pages) {
if (is_null($page->parent_id)) {
// or $page->parent_id === 0 if using 0 to indicate top-level pages
return $page;
}
/** #var Page $parent */
$parent = $pages->get($page->parent_id);
// manually set the relation to avoid re-querying the database
if (!$parent->relationLoaded('children')) {
$parent->setRelation('children', collect()->push($page));
} else {
$parent->children->push($page);
}
return null;
})
->filter()
->values();
}
I've also simplified the relationships to just a single hasMany referring to direct descendants only.
// Page.php
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function children()
{
return $this->hasMany(Page::class, 'parent_id', 'id');
}

Trying to get foraign key records where every connected records are deactive ( Laravel elequent query )

I have 2 tables
Category's ( id, name )
Sub_categories ( id, key, value, category_id )
I'm trying to get all Category's whose all sub_categories are deactivated ( means are soft-deleted )
let me explain more
i have sub_categories data like this
[
{
"id": 1,
"category_id": 1,
"key": "sub 1",
"value": "sub_1",
"deleted_at": null
},
{
"id": 2,
"category_id": 1,
"key": "sub 2",
"value": "1",
"deleted_at": null
},
{
"id": 4,
"category_id": 1,
"key": "sub 3",
"value": "1",
"deleted_at": "2019-07-09 06:06:01"
},
{
"id": 5,
"category_id": 2,
"key": "sub 1",
"value": "33",
"deleted_at": "2019-07-09 06:06:01"
},
{
"id": 6,
"category_id": 2,
"key": "sub 2",
"value": "33",
"deleted_at": "2019-07-09 06:06:01"
}
]
i want only category_id -> 2 ( where all sub_categories are softedeleted )
hear's category model code
public function subCategory() {
$this->makeVisible('deleted_at');
return $this->hasMany('App\SubCategory','category_id','id');
}
$categories = Categories::doesntHave('subCategory')->get();
you first have to define Relationship between Categorys and Sub_categories - https://laravel.com/docs/5.8/eloquent-relationships.
Then use query to get what you want https://laravel.com/docs/5.8/queries
Get IDs of categories with sub categories:
$categoryIdsWithSubCategories = SubCategory::get()->pluck('category_id')->toArray();
Get categories without sub categories:
$categoriesWithoutSubCategories = Category::whereNotIn('id', $categoryIdsWithSubCategories)->get();

How to join output of models in Laravel?

I have the following code:
$orders = OrderProduct::where(function ($query) use ($request) {
})->with('order_products')->orderBy('status', 'desc')->paginate(50)->toArray();
And order_products function is:
public function order_products()
{
return $this->hasMany("App\Order", "order_id", "order_id");
}
It gives me output result:
{
"user_id": "1",
"created_at": "2016-12-18 14:06:11",
"status": "2",
"note": "34535345",
"order_id": "2",
"address": "Kiev",
"courier_id": null,
"payment_type": "0",
"order_products": [
{
"id": 73,
"product_id": "1265",
"amount": "1"
},
{
"id": 74,
"product_id": "1266",
"amount": "1"
}
]
I need to join order_products with products_details, that to give title of product for each order like as:
"order_products": [
{
"id": 73,
"product_id": "1265",
"amount": "1",
"title": "Product name"
},
{
"id": 74,
"product_id": "1266",
"amount": "1",
"title": "Product name"
}
]
Instead this I get a new model output in response:
"products_details": [
{}, {}
]
How can I join two with models?
without joining, just using your first code:
in order_products, override the toArray() method.
function toArray() {
return [
id => $this->id,
product_id => $this->product_id,
amount => $this->amount,
title => $this->details->name
];
}
wich $this->details->name is the relation between order_products and products_details

Resources