How to set additive parameter to laravel collection map method - php

In my laravel 5.7 I try to remake a small piece of application using collection
$selectedSubscriptions = [1,2,3];
$siteSubscriptionsList = [];
$siteSubscriptions = SiteSubscription
::getByActive(true)
->orderBy('id', 'asc')
->get();
foreach ($siteSubscriptions as $next_key => $nextSiteSubscription) {
$is_found = false;
foreach ($selectedSubscriptions as $next_value) {
if ($nextSiteSubscription->id == (int)$next_value) {
$is_found = true;
break;
}
}
$siteSubscriptionsList[] = (object)['name' => $nextSiteSubscription->name, 'id' => $nextSiteSubscription->id, 'checked' => $is_found];
}
like :
$selectedSubscriptions = [1,2,3];
$siteSubscriptionsList = SiteSubscription
::getByActive(true)
->orderBy('id', 'asc')
->get()
->map(function ( $item, $selectedSubscriptions ) {
$is_found = false;
foreach ($selectedSubscriptions as $next_value) {
if ($item->id == (int)$next_value) {
$is_found = true;
break;
}
}
return (object)['name' => $item->name, 'id' => $item->id, 'checked' => $is_found];
})
->all();
But I need to set additive parameter $selectedSubscriptions into map function, as it does not work, as I see that inside os a map
function has “0” value.
How correctly?

When passing data through to the ->map() closure, you use the use statement (which follows the function() call):
$selectedSubscriptions = [1,2,3];
$siteSubscriptionsList = SiteSubscription
::getByActive(true)
->orderBy('id', 'asc')
->get()
->map(function ($item, $key) use ($selectedSubscriptions) {
$is_found = false;
foreach ($selectedSubscriptions as $next_value) {
if ($item->id == (int)$next_value) {
$is_found = true;
break;
}
}
return (object)['name' => $item->name, 'id' => $item->id, 'checked' => $is_found];
})
->all();
The second parameter of ->map() will always be the variable of the $key.
Sidenote: You can reduce your foreach check for the id with the following:
$is_found = collect($selectedSubscriptions)->contains($item->id);

Related

Convert queries to expected array efficiently - Laravel

I have a dictionaries table looks like this:
Here is the array result that I want:
I've already achieved the result by these ugly code:
$dictionaries = Dictionary::all();
$carriers_array = [];
$carriers = $dictionaries->unique('Carrier');
foreach($carriers as $carrier) {
$carriers_array[] = $carrier->Carrier;
}
$DOM_INLs_array = [];
foreach($carriers_array as $carrier_item) {
$DOM_INLs = $dictionaries->where('Carrier', $carrier_item)->unique('DOM_INL');
foreach($DOM_INLs as $DOM_INL) {
$DOM_INLs_array[$carrier_item][] = $DOM_INL->DOM_INL;
}
}
$groups_array = [];
foreach($DOM_INLs_array as $carrier => $DOM_INLs) {
foreach($DOM_INLs as $DOM_INL) {
$groups = $dictionaries->where('Carrier', $carrier)->where('DOM_INL', $DOM_INL)->unique('Group');
foreach($groups as $group) {
$groups_array[$carrier][$DOM_INL][] = $group->Group;
}
}
}
$routes_array = [];
foreach($groups_array as $carrier => $DOM_INLs) {
foreach($DOM_INLs as $DOM_INL => $groups) {
foreach($groups as $group) {
$routes = $dictionaries->where('Carrier', $carrier)->where('DOM_INL', $DOM_INL)->where('Group', $group)->unique('Route');
foreach($routes as $route) {
$routes_array[$carrier][$DOM_INL][$group][] = $route->Route;
}
}
}
}
$sectors_array = [];
foreach($routes_array as $carrier => $DOM_INLs) {
foreach($DOM_INLs as $DOM_INL => $groups) {
foreach($groups as $group => $routes) {
foreach($routes as $route) {
$sectors = $dictionaries->where('Carrier', $carrier)->where('DOM_INL', $DOM_INL)->where('Group', $group)->where('Route', $route)->unique('Sector');
foreach($sectors as $sector) {
$sectors_array[$carrier][$DOM_INL][$group][$route][] = $sector->Sector;
}
}
}
}
}
dd($sectors_array);
But it runs very slow, I want another way to get the same result but more efficient. Can anyone help?
This looks like a bunch of nested maps and groupBys with a pluck at the end:
Arrow functions, PHP 7.4+:
$dictionaries->groupBy('Carrier')
->map(fn ($i) => $i->groupBy('DOM_INL')
->map(fn ($i) => $i->groupBy('Group')
->map(fn ($i) => $i->groupBy('Route')
->map->pluck('Sector')
)
)
);
With anonymous functions:
$dictionaries->groupBy('Carrier')->map(function ($i) {
return $i->groupBy('DOM_INL')->map(function ($i) {
return $i->groupBy('Group')->map(function ($i) {
return $i->groupBy('Route')->map->pluck('Sector');
});
});
});
If you only needed these results grouped in this way and didn't need the leaves to just be the 'Sector' values this would be 1 call to groupBy:
$dictionary->groupBy(['Carrier', 'DOM_INL', 'Group', 'Route'])

How to query users and group them in age range using laravel eloquent

I want to query users and group them by age range
This is what I have done so far
User::applicant()->get()
->groupBy(function ($item) {
return Carbon::parse($item->dob)->age;
})
->map(function ($collection) {
return $collection->count();
});
This is what I have got from above query
Now want to fetch collection and order by age range
18-24: 1,
25-35: 5,
36-45: 89,
46+ : 84
This is a untested solution (which will need refactoring:
$groups = ['18-24' =>, '25-35', ..., '45'];
$applicants = User::applicant()->get();
$groups = collect($groups)
->map(function ($range, $key) use ($applicants) {
$rangeLimits = explode('-', $range);
$count = $applicants->filter(function ($applicant, $key) use ($rangeLimits) {
$age = Carbon::parse($applicant->dob)->age;
$verdict = $age >= $rangeLimits[0];
if (isset($rangeLimits[1])) {
$verdict = $age <= $rangeLimits[1];
}
return $verdict
})->count();
$range = ! isset($rangeLimits[1]) ? $range . '+' : $range;
return [ $range => $count ];
})->flatten()->all();
First you need to create an array of groups which you need, the last group should not have a +.
You then get all the applicants.
Then, you loop through each group, and find out if applicants are within its range, and get the count. This is mapped to the resulting array.
I'm gonna use a combnination of map() and mapToGroups(), I'm pretty sure there must be a simpler way, but this was fun to me:
$ranges = [ // the start of each age-range.
'18-24' => 18,
'25-35' => 25,
'36-45' => 36,
'46+' => 46
];
$output = User::applicant()
->get()
->map(function ($user) use ($ranges) {
$age = Carbon::parse($user->dob)->age;
foreach($ranges as $key => $breakpoint)
{
if ($breakpoint >= $age)
{
$user->range = $key;
break;
}
}
return $user;
})
->mapToGroups(function ($user, $key) {
return [$user->range => $user];
})
->map(function ($group) {
return count($group);
})
->sortKeys();
dd($output);
The idea behind this is to add an attribute to each record with the value correspondant to their age range, then group them by this key creating a set of arrays of users grouped by range, to finally count the elements for each subarray inside this key.
This should return something like:
=> Illuminate\Support\Collection {#2948
all: [
"25-35" => 1,
"36-45" => 2,
"46+" => 1,
],
}
Try this
$users = \DB::table('users')
->select(DB::raw('concat(10*floor(age/10), \'-\', 10*floor(age/10) + 9) as `range`, count(*) as `numberofusers`'))
->groupBy('range')
->get();

reading query whereClause conditions as JSON and converting it to SQL query in PHP Laravel

I'm using PHP laravel framework
what I want is accessing query whereClause parameters as JSON and converting it to a string query for reading records from database
example:
converting this:
{"working_years":{">":2,"<":6},"car_name":"someName","car_company":"companyName"}
to:
Select * from my_table where working_years > 2 AND working_years < 5 AND car_name=someName AND car_company = companyName
the important part for me is "where" conditions
here is my code:
// $query=$request->where;
//for now we access it from a string:
$query = '{"working_years":{">":2,"<":6},"car_name":"someName","car_company":"companyName"}';
if (isset($query)) {
$i = 0;
$whereFieldsAndVals = array([]);// ex:-> [10][name,abc]
try {
$operator = array([]);// ex:-> [10][=,>]
foreach (json_decode($query) as $key => $value) {
if (is_object($value)) {//has custom conditions
$j = 0;
foreach ($value as $k => $v) {
$operator[$i][$j] = $k;
$whereFieldsAndVals[$i][1] = $v;
$j++;
}
} else {
$whereFieldsAndVals[$i][1] = $value;
}
$whereFieldsAndVals[$i][0] = $key;
$i++;
}
} catch (\Exception $exception) {
return $this->customError($exception->getMessage(), 30, 500);
}
}
return $operator;
//this will return:
//[[">","<"]]
//return $whereFieldsAndVals;
//this will return:
//[{"1":6,"0":"working_years"},{"1":"someName","0":"car_name"},{"1":"companyName","0":"car_company"}]
}
finally got the solution
according to Jarek Tkaczyk answer in this thread:
How to create multiple where clause query using Laravel Eloquent?
we can run our where clause queries using eloquent in this template:
where([
['column_1', '=', 'value_1'],
['column_2', '<>', 'value_2'],
[COLUMN, OPERATOR, VALUE],
...
])
here is my new code:
if (isset($query)) {
$fullQuery = [];
/*
https://stackoverflow.com/questions/19325312/how-to-create-multiple-where-clause-query-using-laravel-eloquent
template:
where([
[COLUMN, OPERATOR, VALUE],
['column_1', '=', 'value_1'],
['column_2', '<>', 'value_2'],
*/
try {
// $query = '{"working_years":{">":2,"<":5,"<>":3},"car_name":"bmw","car_company":"am84"}';
$i = 0;
foreach (json_decode($query,true) as $key => $value) {
if (is_array($value)) {//has custom where condition
$count=count($value);
$j = 0;
foreach ($value as $whereKey => $whereValue) {
$fullQuery[$i+$j][0]=$key;
$fullQuery[$i+$j][1]=$whereKey;
$fullQuery[$i+$j][2]=$whereValue;
$j++;
}
if ($count>0)
$i+=($count-1);
} else {
$fullQuery[$i][0]=$key;
$fullQuery[$i][1]='=';
$fullQuery[$i][2]=$value;
}
$i++;
}
} catch (\Exception $exception) {
return $this->customError($exception->getMessage(), 30, 500);
}
return $table->where($fullQuery)->first();
}

add custom column to laravel excel

I am using maatwebsite/excel, I want to know if it's possible to add custom column when I export my data as CSV or not?
Explanation
I am successfully exporting my products data, but my products have other option which is not stored in my products table such as: specification.
my specifications are stored in 2 different tables named specifications where is parent like CPU and subscpecifications where child's are stored like: Core i5.
another table i am using to store child's id and products id in order to relate each product to their subspecifications.
Sounds Complecated right? :) here i provide ugly map to get the logic :)
Now, What I try to do is:
Add extra column to my csv file and include all specifications of each product.
sample:
Codes
here is my current export function
public function export(Request $request) {
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['defaultname'][$key];
}
}
$products = Product::select($getRealInput)->get();
Excel::create('products', function($excel) use($products, $request) {
$excel->sheet('sheet 1', function($sheet) use($products, $request){
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getCustomInput[$key] = $input['customname'][$key];
}
}
$sheet->fromArray($products, null, 'A1', false, false);
$sheet->row(1, $getCustomInput);
});
})->export('csv');
return redirect()->back();
}
Questions
Is that possible?
If yes, Base on my function above, how do I do it?
Thanks in advance.
UPDATE 1
I have added this code to my function
$allRows = array();
$data = array();
foreach($products as $product){
$specs = $product->subspecifications;
foreach($specs as $spec){
$data[] = $spec->specification->title;
$data[] = $spec->title;
}
}
array_push($allRows , $data);
and changed this line:
$sheet->fromArray($products, null, 'A1', false, false);
to
$sheet->fromArray($allRows, null, 'A1', false, false);
now here is what I have:
here is my full function currently:
public function export(Request $request) {
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['defaultname'][$key];
}
}
$products = Product::select($getRealInput)->get();
Excel::create('products', function($excel) use($products, $request) {
$excel->sheet('sheet 1', function($sheet) use($products, $request){
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getCustomInput[$key] = $input['customname'][$key];
}
}
// test code of adding subspacifications
$allRows = array();
$data = array();
foreach($products as $product){
$specs = $product->subspecifications;
foreach($specs as $spec){
$data[] = $spec->specification->title;
$data[] = $spec->title;
}
}
array_push($allRows , $data);
$sheet->fromArray($allRows, null, 'A1', false, false);
//
// $sheet->fromArray($products, null, 'A1', false, false);
$sheet->row(1, $getCustomInput);
});
})->export('csv');
return redirect()->back();
}
UPDATE 2
Well tonight I've played with my codes a lot and FINALLY :) I got what I needed, here is how:
//codes...
// Here is you custom columnn logic goes
foreach($products as $product){
$specifications = DB::table('products')
->where('products.id', $product->id)
->join('product_subspecification', 'product_subspecification.product_id', '=', 'products.id')
->join('subspecifications', 'subspecifications.id', '=', 'product_subspecification.subspecification_id')
->select('subspecifications.title')
->pluck('title');
$product['specifications'] = rtrim($specifications,',');
}
//
$sheet->fromArray($products, null, 'A1', false, false);
$sheet->row(1, $getCustomInput);
//... rest of the codes
This will give me my products specifications, however there is 3 little issues:
I do not have heading for my specifications in CSV file
Products without specification shows [] instead of nothing
products with specification also covers them with [] and ""
Here I provided screenshot for better understanding:
You need to prepare custom column Specifications by looping through products. Here is your fix,
public function export(Request $request) {
$headers[] = [
'Id',
'Title',
'Specifications',
];
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['defaultname'][$key];
}
}
$products = Product::select($getRealInput)->with('subspecifications')->get()->toArray();
Excel::create('products', function($excel) use($headers,$products, $request) {
$excel->sheet('sheet 1', function($sheet) use($headers,$products, $request){
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getCustomInput[$key] = $input['customname'][$key];
}
}
// Here is you custom columnn logic goes
foreach($products as $product){
$specs = "";
$specifications = DB::table('products')
->where('products.id', $product->id)
->join('product_subspecification', 'product_subspecification.product_id', '=', 'products.id')
->join('subspecifications', 'subspecifications.id', '=', 'product_subspecification.subspecification_id')
->select('subspecifications.title')
->pluck('title');
foreach($specifications as $spec){
$specs = $specs .','.$spec;
}
$product['specifications'] = ltrim($specs,',');
}
//
$mergedProducts = array_merge($headers, $products);
$sheet->fromArray($mergedProducts, null, 'A1', false, false);
$sheet->row(1, $getCustomInput);
});
})->export('csv');
return redirect()->back();
}
Update
As per your sheet image I can assume you have only three columns Id, Title and Specifications, you can change header array according to the columns you are getting from DB.
Yes its possible.
create array for row ie. data = array();
push cell data to array
you can fetch relation data using eloquent or join as well, here I am fetching inside loop.
Updated Function as below:
I tried to match with your data structure
public function export(Request $request) {
$input = $request->except('_token');
foreach ($input['cb'] as $key => $value) {
if ($value== 'on') {
$getRealInput[$key] = $input['defaultname'][$key];
}
}
$products = Product::select($getRealInput)->get();
Excel::create('products', function($excel) use($products, $request) {
$excel->sheet('sheet 1', function($sheet) use($products, $request){
// test code of adding subspacifications
$allRows = array();
array_push($allRows , ['id', 'title', 'specifications']); // Added title row
$data = array();
foreach($products as $product){
$data[] = $product->id; // Added product fields
$data[] = $product->title;
$specs = $product->subspecifications;
$spec_details = "";
foreach($specs as $spec){
$spec_details .= $spec->specification->title.':'. $spec->title. ' '; // appended specification:subspecification
}
$data[] = $spec_details;
}
array_push($allRows , $data);
$sheet->fromArray($allRows, null, 'A1', false, false);
//
// $sheet->fromArray($products, null, 'A1', false, false);
//$sheet->row(1, $getCustomInput); // commented
});
})->export('csv');
return redirect()->back();
}
I do not have heading for my specifications in CSV file
To solve the issue, you can define header and use use array_merge(). For I.E.
$headers[] = [
'Title',
'Specifications',
];
$products= array_merge($headers, $products);
Products without specification shows [] instead of nothing
products with specification also covers them with [] and ""
For 2nd and 3rd point you can use implode() to get rid of []
$product['specifications'] = implode(',', $specifications);
Hope this helps
It works for me. very simple
// Headings//
$headers[] = ['Id', 'Name'];
// 2 Rows //
$data[0] = ['1', 'John'];
$data[1] = ['2', 'Roger'];
Excel::create('report', function($excel) use($headers, $data) {
$excel->sheet('sheet 1', function($sheet) use($headers, $data){
$merged_records = array_merge($headers, $data);
$sheet->fromArray($merged_records, null, 'A1', false, false);
});
})->export('csv');

Have search using foreach loop not return on first match

I want to get all records with the same refid but my foreach loop is just searching until it find the first record and halts.
$data = Projekt1Db::select('refid', 'userid', 'passwd', 'uid', 'gid', 'homedir')->get();
$inputs = \Request::all();
foreach ($data as $id) {
if ($id->refid == $inputs['refid']){
return view('searchfound', [
'id' => $id]);
}
}
if($inputs !== $id){
return redirect()->back()->with('message', 'Refid not found');
}
}
There are more records with the same refid, but it just gives me the first record with that refid.
In the view:
<b>Refid:</b> {{$id->refid}}<br>
<b>Userid:</b> {{$id->userid}}<br>
<b>Password:</b> {{$id->passwd}}<br>
<b>UID:</b> {{$id->uid}}<br>
<b>GID:</b> {{$id->gid}}<br>
<b>homedir:</b> {{$id->homedir}}<br><br>
Use the code below for your php code:
$data = Projekt1Db::select('refid', 'userid', 'passwd', 'uid', 'gid', 'homedir')->get();
$inputs = Request::all();
$id_array = [];
foreach ($data as $id) {
if ($id->refid == $inputs['refid']){
$id_array[] = $id;
}
}
if(count($id_array) == 0){
return redirect()->back()->with('message', 'Refid not found');
}
else{
return view('searchfound', ['id' => $id_array]);
}
and the layout code below:
#foreach($id as $val)
<b>Refid:</b> {{$val->refid}}<br>
<b>Userid:</b> {{$val->userid}}<br>
<b>Password:</b> {{$val->passwd}}<br>
<b>UID:</b> {{$val->uid}}<br>
<b>GID:</b> {{$val->gid}}<br>
<b>homedir:</b> {{$val->homedir}}<br><br>
#endforeach
Basic, change
foreach ($data as $id) {
if ($id->refid == $inputs['refid']){
return view('searchfound', [
'id' => $id]);
}
}
to
$data = array();
foreach ($data as $id) {
if ($id->refid == $inputs['refid']){
$data[] = $id;
}
}
if (length($data) > 0) {
var_dump($data);
} else {
// nothing found
}
Try this:
$newData = [];
foreach ($data as $id) {
if ($id->refid == $inputs['refid']){
$newData[] = $id;
}
}
return view('searchfound', [
'newData' => $newData]);
#foreach($newData as $val)
<b>Refid:</b> {{$val->refid}}<br>
<b>Userid:</b> {{$val->userid}}<br>
<b>Password:</b> {{$val->passwd}}<br>
<b>UID:</b> {{$val->uid}}<br>
<b>GID:</b> {{$val->gid}}<br>
<b>homedir:</b> {{$val->homedir}}<br><br>
#endforeach
Better you can use where condition if you have many records of same refidid
$data=Projekt1Db::where('refid','=', $inputs->refid);
foreach ($data as $id) {
}
then you can loop easily right

Categories