Laravel eloquent, create collection based on Column value - php

I have an example table like the following:
| id | is_new | cate |
| 0 | 1 | [5] |
| 1 | 1 | [5] |
| 2 | 1 | [7] |
| 3 | 1 | [6] |
After I call the following function
$product = Products::where('is_new', 1)->get();
the collection has the following data structure
Collection {#1284 ▼
#items: array:4 [▼
0 => Products {#1285 ▶}
1 => Products {#1286 ▶}
2 => Products {#1287 ▶}
3 => Products {#1288 ▶}
]
}
I can also return a list of category id through:
$category = Category::where('type', 2)->pluck('id')->all();
I wonder is there any good way to achieve following structure (or similar) based on cate. I can use brute force way to create it by looping through the $category then add to new collection manually, I think there is a better way to do it.
Collection {#1284 ▼
#items: array:3 [▼
5 => array:2 [▼
0 => Products {#1285 ▶}
1 => Products {#1286 ▶}
]
6 => Products {#1287 ▶} or 6 => array:1 [ 0 => Products {#1287 ▶} ]
7 => Products {#1288 ▶}
]
}
Solution Update based on answer:
$product->groupBy(function($item) {
return $item['cate'][0]; //because the entry is a list
});

You should take a look at using something like groupBy to group your categories together. It can be done on a database level (assuming you have referential integrity) with foreign keys. You'll also get a guarantee that what is returned is an array for each category, which will make things more consistent (you won't need to make a check for if it is an array or an instance of Products, then).
You might need to change your database layout if your categories are stored like arrays, but for this you should use an intermediary table - look at many to many relationships.

Related

get array output as key value on doctrine querybuilder

I have two tables for example
student
id name
1 John
2 Doe
subject
id sub student
1 eng 1
2 maths 1
I have a query as follows on student repository
$this->createQueryBuilder('stud')
->select('stud.name,s.sub')
->leftJoin('AppBundle:subject', 's', 'WITH', 's.student = stud.id')
->getQuery()
->getArrayResult();
But I am getting 3 rows with 2 rows as john with different sub and one row as doe.
How could I make it two rows with the sub as an array on the result? I am new to query writing. Hope someone could help
Getting output
array:3 [▼
0 => array:2 [▼
"name" => John
"sub" => maths
]
1 => array:2 [▼
"name" => John
"sub" => eng
]
2 => array:2 [▼
"name" => Doe
"sub" => null
]
]
Expecting output
array:2 [▼
0 => array:2 [▼
"name" => John
"sub" => array:2['eng','maths']
]
2 => array:2 [▼
"name" => Doe
"sub" => null
]
]
The LEFT JOIN returns all records from the left table (here: student), and the matched records from the right table (here: subject). The result is NULL from the right side if there is no match(here: student with id:2).
Here, you should select from subject, then LEFT JOIN it with students. it solves your problem.

Sort collection (or array of objects) based on id and parent_id attributes

I came into a problem with sorting Laravel Collection based on object id and object's parent_id attribute.
Currently, I have a Collection of objects in following format:
Collection {#563 ▼
#items: array:20 [▼
0 => Object {#546 ▶}
1 => Object {#544 ▶}
2 => Object {#540 ▶}
3 => Object {#542 ▶}
4 => Object {#541 ▶}
5 => Object {#536 ▶}
]
}
Each object have following attributes:
#attributes: array:8 [▼
"id" => "70"
"name" => "Object Name 70"
"parent_id" => "75"
]
I need to sort that collection where parent will be first and then children. Then again parent and children. One object will have parent_id NULL. Something more like this:
Parent
Child
Child
Parent
Parent
Child
Child
Child
Parent
Sort the collection by parent_id ( lowest to highest ), then by id ( lowest to highest ) if the parent ids are equal.
This approach assumes that the first element in the collection should be the root parent ( the one with parent_id NULL ) and that in order for an object to be a parent of an object, the parent must exist first and therefore will always have a lower id than any of its children.

array values with where condition in laravel query

I have an array called assetIDs as below
$assetIDs = Collection {#505 ▼
#items: array:2 [▼
0 => 4
1 => 7
]
}
and I have the data in the table as below shown
and I'm doing query on the above table using this
$supplier_id = SupplierAsset::whereIn('asset_id',$asset_ids)->pluck('supplier_id');
and result for the above query is below
Collection {#510 ▼
#items: array:3 [▼
0 => 1
1 => 1
2 => 2
]
}
here whereIn is returning all the possible rows which satisfies the condition. Actually I need to get the result as like which supplier_id has both the values of assetIDs array.In my table supplier_id=1 has the both values 4 and 7 Just like below collection.
Collection {#510 ▼
#items: array:3 [▼
0 => 1
1 => 1
]
}
Can anybody suggest me the solution for this please?
You can try:
$supplier_id = SupplierAsset::whereIn('asset_id',$asset_ids)
->groupBy('supplier_id')
->havingRaw('count(distinct asset_id) = ' . count($assetIDs))
->pluck('supplier_id');
Here is the mysql you should do :
1- get all the id who have more than 1 supplier_id :
SELECT supplier_id, Count(distinct asset_id) as dist_asst
FROM Table
GROUP BY supplier_id
HAVING dist_asst > 1
2- then doing a join :
SELECT t1.supplier_id
FROM Table t1
INNER JOIN (SELECT supplier_id, Count(distinct asset_id) as dist_asst
FROM Table
GROUP BY supplier_id
HAVING dist_asst > 1) t2 on t2.supplier_id = t1.supplier_id

PHP- Array combine not working properly

I am trying to do array combine, but it is not working properly. I have one array called $models which consits of objects and it looks like this:
array:5 [▼
0 => Comment {#377 ▶}
1 => Thumb {#378 ▶}
2 => View {#379 ▶}
3 => Vote {#380 ▶}
]
Then since I am passing it to another function, I am adding one more object as an element like this:
array_push($models, new User);
And then I get an array that looks like this:
array:5 [▼
0 => Comment {#377 ▶}
1 => Thumb {#378 ▶}
2 => View {#379 ▶}
3 => Vote {#380 ▶}
4 => User {#399 ▶}
]
I am then doing foreach loop to get the total count in the DB for each model like this:
foreach ($models as $model){
$modelCounts[] = $model->count();
}
My $modelCounts than looks like this:
array:5 [▼
0 => 19
1 => 22
2 => 15
3 => 17
4 => 3
]
And then I am trying to do array_combine so that my objects are keys and the counts are values like this:
$result = array_combine($models, $modelCounts);
But something is not working right because when I do dd($result); I get:
array:1 [▼
"[]" => 3
]
But when I do it the other way around like this:
$result = array_combine($modelCounts, $models);
It works fine and I get:
array:5 [▼
19 => Comment {#377 ▶}
22 => Thumb {#378 ▶}
15 => View {#379 ▶}
17 => Vote {#380 ▶}
3 => User {#399 ▶}
]
But I need it the other way around and not like this.
Objects can't be used as key for associative arrays, only scalar values are allowed.
http://php.net/manual/en/language.types.array.php
Arrays and objects can not be used as keys. Doing so will result in a warning: Illegal offset type.
The reason why the first array_combine() fails is that an objcect cannot be usead as an array key.
You may want to create an array containing classes names first by using get_class() to get class's name and then combine it with $modelCounts
It should look something like this
foreach ($models as $model){
$modelNames[] = get_class($model);
}
$result = array_combine($modelNames, $modelCounts);

Php sorting based on ID and ParentID

i have the following array structure:
| ID | CategoryName | ParentID
_________________________________
1 | Movies | -1
_________________________________
2 | Series | -1
_________________________________
3 | ActionMovies | 1
_________________________________
4 | HorrorMovies | 1
_________________________________
5 | ComedySeries | 2
_________________________________
6 | TVShows | -1
My goal is to reach the following structure
Goal:
| ID | CategoryName | ParentID
_________________________________
1 | Movies | -1
_________________________________
3 | ActionMovies | 1
_________________________________
4 | HorrorMovies | 1
_________________________________
2 | Series | -1
_________________________________
5 | ComedySeries | 2
_________________________________
6 | TVShows | -1
Or explained in word:
Parent-Categories have ParentID = -1 (Example Movies)
All categories keep their original ID-Number
Should come listed directly after their ParentCategory (Example ActionMovies)
RootCategories without Children, come listed at the end of array. (Example TVShows)
How can i achieve this best with PHP? I have no access to the original Mysql query, so that is not an option :)
i have started with this code, but i am not sure it is the right path, and with minimal efforts/readability
$tmpList = Categories_Models_Main::getAllCategories();
$categoryData = array();
foreach ($tmpList as $index => $categoryObject) {
$categoryData[] = array('id' => $categoryObject->id,
'CategoryName' => $categoryObject->parentId,
'name' => $categoryObject->name);
}
///let us assume $categoryData is original state.
///Beginning of manipulation and re-sorting of $tmpList
foreach ($categoryData as $key => $value) {
$mainId[$key] = $value['id'];
$parentId[$key] = $value['parentId'];
}
array_multisort($parentId, $mainId, $categoryData);
Split the main array into two, one with those nodes that doesn't have childs and other with rests of items.
Do the sorting into array of items with array_multisort function and do too a sorting into array of categories without childs using the criteria that you want. The final step will be push the array without childs to the other.
This form isn't the most efficient, but to have a first approach is valid, after this, you can dispense time optimizing the code.
$input = array(
array("foo", "bar", "5"),
array("barr", "baz", "9"),
array("nyan", "nyu", "2")
);
usort($input, function($a, $b) {
if($a[2] < $b[2]) {
return -1;
} else if($a[2] > $b[2]) {
return 1;
} else {
return 0;
}
});
var_dump($input);
Do you mean something like this? The idea is to manually compare the contents of the array using condition you know. Since the thing what is to be sorted is unique, the logic how to sort must be always implemented. With PHP, PHP’s sorting functions most of time does it correctly.
I am pretty sure that this does not answer your question, but might give you some ideas how to make your life easier. This is javascript approach of the problem, but this way you won't even have to think about sorting anything. You just provide parents and children and it is sorted for you and ready to use. A pretty nice way in my opinion. Yet it is probably irrelevant to what you are trying to do, but maybe for some other similar problem you would see this as a solution: https://developers.google.com/chart/interactive/docs/gallery/orgchart
The only way to solve this problem is by using a bottom up approach (i am referring to the pyramids).
Explanation
We need to start with rows (entries) within an array that do not have parents. In my case, it's any element that has "parent_id = 0". Once we have those, we need to build children elements for each parent element.
The latter part would be done recursively by calling sorting method until the element which we are processing has not children. The results from the sorting method are passed down to the previous array point until the array is complete.
Code
Class Categories {
private $cats;
public function getAllCategoriesSorted() {
/**
* $this->cats = [
* 'id' => x, 'parent_id' => x
* ];
*/
$this->cats = Category::get()->toArray();
# find categories with no parents
$keys = array_keys(array_column($this->cats, 'parent_id'), 0);
$return = [];
# loop through each and populate each one
foreach ($keys as $key) {
$return[$this->cats[$key]['id']] = $this->sortCategories($this->cats[$key]);
}
dd($return);
}
private function sortCategories($currentElement) {
# we need to check if current element has any children
$keys = array_keys(array_column($this->cats, 'parent_id'), $currentElement['id']);
if ($keys === false || empty($keys)) {
# we are dealing with childless element, we should return it as it is
return $currentElement;
}
# we are dealing with element that has children, we need to loop through each child
$currentElement['children'] = [];
foreach ($keys as $key) {
$currentElement['children'][$this->cats[$key]['id']] = $this->sortCategories($this->cats[$key]);
}
return $currentElement;
}
}
Example result
array:2 [▼
65 => array:4 [▼
"id" => 65
"name" => "Parent 1"
"parent_id" => 0
"children" => array:14 [▼
66 => array:4 [▼
"id" => 66
"name" => "Child 1"
"parent_id" => 65
"children" => array:22 [▶]
]
87 => array:4 [▼
"id" => 87
"name" => "Child 2"
"parent_id" => 65
"children" => array:31 [▶]
]
117 => array:4 [▶]
118 => array:4 [▶]
120 => array:4 [▶]
124 => array:4 [▶]
125 => array:4 [▶]
127 => array:4 [▶]
225 => array:4 [▶]
305 => array:4 [▶]
434 => array:4 [▶]
321 => array:4 [▶]
348 => array:4 [▶]
468 => array:4 [▶]
]
]
64 => array:4 [▼
"id" => 64
"name" => "Parent 2"
"parent_id" => 0
"children" => array:5 [▼
128 => array:4 [▶]
132 => array:4 [▼
"id" => 132
"name" => "Child 3"
"parent_id" => 64
"children" => array:22 [▼
202 => array:3 [▶]
203 => array:3 [▼
"id" => 203
"name" => "Child 4"
"parent_id" => 132
]
204 => array:3 [▶]
205 => array:3 [▶]
206 => array:3 [▶]
207 => array:3 [▶]
208 => array:3 [▶]
209 => array:3 [▶]
210 => array:3 [▶]
211 => array:3 [▶]
212 => array:3 [▶]
213 => array:3 [▶]
214 => array:3 [▶]
215 => array:3 [▶]
216 => array:3 [▶]
217 => array:3 [▶]
218 => array:3 [▶]
220 => array:3 [▶]
221 => array:3 [▶]
222 => array:3 [▶]
223 => array:3 [▶]
224 => array:3 [▶]
]
]
134 => array:4 [▶]
394 => array:4 [▶]
454 => array:4 [▶]
]
]
]

Categories