add custom column to laravel excel - php

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

Related

Laravel DomPDF Unable to Create Multiple PDF

I have two data and I will make one data for each pdf. but the output that I produce is one pdf and only the first loop data is displayed. is there something wrong? thank you
public function emailPdf(Request $request){
DB::beginTransaction();
// $autocrm_class = new CRM;
$post = $request->all();
$post['id_brand'] = 1;
$post['date_today'] = "2020-02-07";
// $post['date_today'] = date('Y-m-d', strtotime($post['date_today']));
$data = BrandOutlet::where('id_brand', $post['id_brand'])->with('brandOutletDelivery.brandOutletTujuan');
$data->with(["brandOutletDelivery.brandOutletDeliveryItems" => function($q) use($post){
$q->whereDate('created_at', '=', $post['date_today'])->with('brandItem')->with('unit');
}]);
$data->with(["brandOutletOpnamePagi.brandOutletOpnameItems" => function($q) use($post){
$q->whereDate('created_at', [$post['date_today']])->with('brandItem')->with('unit');
}]);
$data->with(["brandOutletOpnameMalam.brandOutletOpnameItems" => function($q) use($post){
$q->whereDate('created_at', [$post['date_today']])->with('brandItem')->with('unit');
}]);
$data = $data->get();
$html = '';
foreach($data as $result){
$view = view('pdf.delivery', ['result' => $result]);
$html .= $view->render();
}
// return $html //I tried to debug by displaying it in View and the result is 2 data
$pdf = \PDF::loadHtml($html);
$sheet = $pdf->setPaper('a4', 'landscape');
$pdf->save('pdf/'.$result['id'].'.pdf');
return $sheet->stream();
}
$html = [];
foreach($data as $i=>$result){
$view = view('pdf.delivery', ['result' => $result]);
$html[$i]= $view->render();
}
foreach($html as $htm){
$pdf = \PDF::loadHtml($htm);
$sheet = $pdf->setPaper('a4', 'landscape');
$pdf->save('pdf/'.$result['id'].'.pdf');
return $sheet->stream();
}
please try this

How to set additive parameter to laravel collection map method

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

Need csv file upload to fetch all rows but currently fetching the first row

This is the code to the upload function in my controller.The problem at hand is that when i upload the csv file to my database I only get(fetch) the first row of the file and the rest of the rows are omitted.I need to be able to get all the rows from the file.Please assist in anyway you can...Thanks in advance
public function upload(Request $request)
{
//get file
//$allowed = array('csv');
$upload=$request->file('upload');
//$extension = File::extension($upload);
$filePath=$upload->getRealPath();
//open and read
$file=fopen($filePath,'r');
$header= fgetcsv($file);
$escapedHeader=[];
//validate
foreach ($header as $key => $value) {
$lheader= strtolower($value);
$escapedItem=preg_replace('/[^a-z]/', '', $lheader);
array_push($escapedHeader, $escapedItem);
}
//looping throught other columns
while ($columns=fgetcsv($file)) {
if ($columns[0]=="")
{
continue;
}
$data= array_combine($escapedHeader, $columns);
dd($data);
//setting type
foreach ($data as $key => $value) {
$value=($key=="phone" || $key=="nationalid" || $key=="staffsalary")?(integer)$value:(string)$value;
}
//table update
$firstname=$data['firstname'];
$lastname=$data['lastname'];
$email=$data['email'];
$phone=$data['phone'];
$nationalid=$data['nationalid'];
$staffid=$data['staffid'];
$stafftitle=$data['stafftitle'];
$staffsalary=$data['staffsalary'];
$employees= Employees::firstOrNew(['phone'=>$phone,'nationalid'=>$nationalid]);
$employees->firstname=$firstname;
$employees->lastname=$lastname;
$employees->email=$email;
$employees->staff_id=$staffid;
$employees->staff_title=$stafftitle;
$employees->staff_salary=$staffsalary;
$employees->employer_phone = Auth::user()->phone;
$employees->save();
return redirect()->route('home');
}
}
The main reason you're having this issue is because your return redirect()->route('home'); is inside your while loop. Just move it so it's on the outside and it should work fine.
Also, this is just an FYI but you could use Collections to clean your controller up a little but:
public function upload(Request $request)
{
$upload = $request->file('upload');
$csv = collect(array_map('str_getcsv', file($upload->getRealPath())));
$keys = array_map(function ($item) {
return preg_replace('/[^a-z]/', '', strtolower($item));
}, $csv->shift());
$csv
->map(function ($row) use ($keys) {
return array_combine($keys, $row);
})
->reject(function ($row) {
return empty(array_first($row));
})
->each(function ($row) {
$row = array_map(function ($value, $key) {
return in_array($key, ['phone', 'nationalid', 'staffsalary']) ? (integer)$value : (string)$value;
}, $row);
$employees = Employees::firstOrNew(['phone' => $row['phone'], 'nationalid' => $row['nationalid']]);
$employees->firstname = $row['firstname'];
$employees->lastname = $row['lastname'];
$employees->email = $row['email'];
$employees->staff_id = $row['staffid'];
$employees->staff_title = $row['stafftitle'];
$employees->staff_salary = $row['staffsalary'];
$employees->employer_phone = Auth::user()->phone;
$employees->save();
});
return redirect()->route('home');
}
Obviously, you don't have to use the code above, I just thought I'd show you an alternative way to write it.
I wasn't sure what if ($columns[0]=="") was there for but I've added that in with the reject() method.
Hope this helps!
Your problem is a different breakline for different OS. Use this construction for better:
// take care of all possible newline-encodings in input file
$NEWLINE_RE = '/(\r\n)|\r|\n/';
$csv = preg_replace($NEWLINE_RE,'===BREAK===', $old_csv);
foreach (explode('===BREAK===', $csv) as $k => $line){
if(strlen($line) > 0) $lines[] = stripcslashes(trim($line));
}

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

Using foreach in codeigniter view to generate select options

I ma using codeigniter to generate some html options,but i only get one result,first result from the table.
This is my controller
public function edit_event($id = 0){
$id = $this->uri->segment(3);
$current_id = $this->ion_auth->get_user_id();
$data['data'] = $this->as->the_event_edit($id);
$data['groups'] = $this->as->the_groups_list($current_id);
$this->load->view('editevent',$data);
}
This is my model
public function the_groups_list($current_id){
$query = $this->db->get_where('all_groups', array('group_owner' => $current_id));
foreach ($query->result() as $row)
{
$data = array(
'group_title' => $row->group_title,
'group_name' => $row->group_name,
'group_owner' => $row->group_owner
);
return $data;
}
}
This is the other model
public function as_event_edit($id){
$query = $this->db->get_where('all_scheduled_messages', array('id' => $id));
foreach ($query->result() as $row)
{
$data = array(
'as_title' => $row->as_title,
'as_event_name' => $row->as_event_name,
'as_owner' => $row->as_owner,
'as_type' => $row->as_type,
'as_target_dataset' => $row->as_target_dataset,
'as_timestamp' => $row->as_timestamp,
'as_time' => $row->as_time,
'as_day' => $row->as_day
);
return $data;
}
}
I am then using $groups['group_title'] in view and only the first group title gets displayed even though i have like 4 group titles from the other rows.
How can i return and pass an array that i can then to the view so as to use foreach to iterate and display the group titles?.
In your model you're not creating a multi-dimensional array. You need to add keys either using a counter:
$data = array();
$i=0;
foreach ($query->result() as $row){
$data[$i]['as_title'] = $row->as_title;
$data[$i]['as_event_name'] = $row->as_event_name;
$data[$i]['as_owner'] = $row->as_owner;
$data[$i]['as_type'] = $row->as_type;
$data[$i]['as_target_dataset'] = $row->as_target_dataset;
$data[$i]['as_timestamp'] = $row->as_timestamp;
$data[$i]['as_time'] = $row->as_time;
$data[$i]['as_day'] = $row->as_day;
$i++;
);
return $data;
or use the key of the incoming array
$data = array();
foreach ($query->result() as $id => $row){
$data[$id]['as_title'] = $row->as_title;
$data[$id]['as_event_name'] = $row->as_event_name;
$data[$id]['as_owner'] = $row->as_owner;
$data[$id]['as_type'] = $row->as_type;
$data[$id]['as_target_dataset'] = $row->as_target_dataset;
$data[$id]['as_timestamp'] = $row->as_timestamp;
$data[$id]['as_time'] = $row->as_time;
$data[$id]['as_day'] = $row->as_day;
);
return $data;
Change the model from $data = to $data[] = to insert each row and not only one.
In your view loop over the groups, like so:
foreach ($data as $group) {
echo "Title" . $groups['group_title'];
}
RTM: Adding Dynamic Data to the View
In the controller: rename your $data['data'] to $data['event']
$data['event'] = $this->as->the_event_edit($id);
$data['groups'] = $this->as->the_groups_list($current_id);
$this->load->view('editevent',$data);
In your view:
foreach ($event as $items) {
var_dump($items);
}
foreach ($groups as $group) {
var_dump($group);
}

Categories