Export whole table to CSV using laravel - php

I am new to laravel and having a tough time figuring out a way to export one table to csv.
I have tried the following code in the controller class, but it gives me an error:
public function get_export()
{
$table = Cpmreport::all();
$file = fopen('file.csv', 'w');
foreach ($table as $row) {
fputcsv($file, $row);
}
fclose($file);
return Redirect::to('consolidated');
}
Model Class for Cpmreport:
class Cpmreport extends Eloquent
{
public static $table='cpm_report';
}
The error :
Message:
fputcsv() expects parameter 2 to be array, object given
Location:
C:\xampp\htdocs\cpm_report\application\controllers\cpmreports.php on line 195
Any help would be appreciated.

Easy way
Route::get('/csv', function() {
$table = Cpmreport::all();
$output='';
foreach ($table as $row) {
$output.= implode(",",$row->toArray());
}
$headers = array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="ExportFileName.csv"',
);
return Response::make(rtrim($output, "\n"), 200, $headers);
});

fputcsv($file, $table); should be fputcsv($file, $row), shouldn't it?
And convert the object to an array using Eloquent's to_array()method: http://laravel.com/docs/database/eloquent#to-array
public function get_export()
{
$table = Cpmreport::all();
$file = fopen('file.csv', 'w');
foreach ($table as $row) {
fputcsv($file, $row->to_array());
}
fclose($file);
return Redirect::to('consolidated');
}

Select query of MySQL data.
$data = \DB::connection('mysql')->select($select);
Call following function:
query_to_csv($data, 'data.csv');
function data_to_csv($data, $filename)
{
$fp = fopen($filename, 'w');
foreach ($data as $row) {
fputcsv($fp, $row);
}
fclose($fp);
}
0.1 Million records takes 1 second to create.

this is better and simple.
$file_name = "abc";
$postStudent = Input::all();
$ck = DB::table('loan_tags')->select('LAN')->where('liabilitiesId', $postStudent['id'])->get();
$i = 0;
foreach ($ck as $row) {
$apps[$i]['LAN'] = $row->LAN;
$apps[$i]['Account_number'] = $postStudent['account_number'];
$apps[$i]['Bank_Name'] = $postStudent['bank_name'];
$i++;
}
ob_end_clean();
ob_start();
Excel::create($file_name, function($excel) use($apps){
$excel->sheet('Sheetname', function($sheet) use($apps){
$sheet->row(1, array(
'LAN', 'Account number' , 'Bank Name'
));
$k = 2;
foreach ($apps as $deta) {
$sheet->row($k, array($deta['LAN'], $deta['Account_number'], $deta['Bank_Name']
));
$k++;
}
});
})->download('xlsx');

Related

How to divide one big function into small functions in php for best practices?

I have created a function exportFile that export file into .csv and .txt for different platforms Amazon, Magento and catch from the database. Since, the function is getting big because of long headers as well as some repeated loops how can I divide this function into separate functions for best practices?
function exportFile ($fileName, $platform, $option) {
$test_db = new MysqliDb (TEST_DB_HOSTNAME, TEST_DB_USERNAME, TEST_DB_PASSWORD, TEST_DB_DATABASE);
$file = fopen(DOWNLOAD_FILE_PATH.$fileName, 'wb') OR die(json_encode(array("status" => "error", "errMessage" => "Error while downloading file!")));
if ($file) {
$query = "SELECT * FROM database";
$data = $test_db->rawQuery($query);
switch ($platform) {
case 'magento':
$header = array(
#csv titles goes here
);
fputcsv($file, $header);
foreach ($data as $rows) {
$csvData = array(
#csv records from database goes here
);
fputcsv($file, $csvData);
}
break;
case 'catch':
$header = array(
#csv titles goes here but not same as magento
);
fputcsv($file, $header);
foreach ($data as $rows) {
$csvData = array(
#csv records from database goes here but not same as magento
);
fputcsv($file, $csvData);
}
break;
}
}
}
There are many ways to break things up. Here's a starter
function fileOpen {$filename}
{ return fopen(DOWNLOAD_FILE_PATH.$fileName, 'wb') OR
die(json_encode(array("status" => "error", "errMessage" => "Error while downloading file!")));
}
function getData ()
{
$test_db = new MysqliDb (TEST_DB_HOSTNAME, TEST_DB_USERNAME, TEST_DB_PASSWORD, TEST_DB_DATABASE);
$query = "SELECT * FROM database";
return $test_db->rawQuery($query);
}
function doMagento ($file, &$data)
{
$header = array(
#csv titles goes here
);
fputcsv($file, $header);
foreach ($data as $rows) {
$csvData = array(
#csv records from database goes here
);
fputcsv($file, $csvData);
}
}
function doCatch ($file, &$data)
{
$header = array(
#csv titles goes here but not same as magento
);
fputcsv($file, $header);
foreach ($data as $rows) {
$csvData = array(
#csv records from database goes here but not same as magento
);
fputcsv($file, $csvData);
}
}
function exportFile ($fileName, $platform, $option) {
$file = fileOpen ($filename);
if ($file) {
$data = getData ();
switch ($platform) {
case 'magento': doMagento ($file, $data);
break;
case 'catch': doCatch ($file, $data);
break;
}
}
}
One more iteration is required to make a function for csvData. That's for you to do !

Laravel Exception: Both parameters should have an equal number of elements

I'm new to laravel, and I'm making an app that need a .CSV data to be imported into the database, the data has been succesfully imported however i faced this issue :
ErrorException array_combine(): Both parameters should have an equal number of elements
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Accounts;
class AccountController extends Controller
{
public function show(){
return view ('upload');
}
public function store(Request $request){
$file = $request->file('upload-file');
$csvData = file_get_contents($file);
$rows = array_map("str_getcsv", explode("\n", $csvData));
// dd($rows);
$header = array_shift($rows);
// dd($header);
foreach ($rows as $row) {
$row = array_combine($header, $row);
if (count($header) != count($row)) {
continue;
}
set_time_limit(0);
Accounts::create([
'AccountClass' => $row['Classe'],
'AccountNumber' => $row['Compte'],
'AccountDesc' => $row['Desc'],
'active' => 1,
]);
}
return view ('home');
}
}
would you point me to the right direction thank you in advance
This error appears when you try to combine two arrays with unequal length.
$arr1 = ["a", "s", "d"];
$arr2 = [1, 2, 3];
if(count($arr1) == count($arr2)){
$result = array_combine($arr1, $arr2);
} else{
echo "Error array combine";
}

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

Process CSV Into Array With Column Headings For Key

I have a CSV with the first row containing the field names. Example data is...
"Make","Model","Note"
"Chevy","1500","loaded"
"Chevy","2500",""
"Chevy","","loaded"
I need my data formatted in an array of key-value pairs where the key name is the column heading. I guess it would something like this for row 1:
$array = [
"Make" => "Chevy",
"Model" => "1500",
"Note" => "loaded"
];
...row 2...
$array = [
"Make" => "Chevy",
"Model" => "1500",
"Note" => ""
];
...and row 3...
$array = [
"Make" => "Chevy",
"Model" => "",
"Note" => "loaded"
];
I'm not sure how to do this other than statically - problem is the columns with their associated data could change from one file to the next... columns rearranged, dropped, or added.
You ideas are much appreciated.
$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
$all_rows[] = array_combine($header, $row);
}
print_r($all_rows);
PHP offers already 99,9% of what you need within SplFileObject, you add the missing 0,1% by extending from it. In the following example CSVFile extends from it:
$csv = new CSVFile('../data/test.csv');
foreach ($csv as $line)
{
var_dump($line);
}
With your example data:
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(4) "1500"
["Note"]=> string(6) "loaded"
}
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(4) "2500"
["Note"]=> string(0) ""
}
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(0) ""
["Note"]=> string(6) "loaded"
}
CSVFile is defined as the following:
class CSVFile extends SplFileObject
{
private $keys;
public function __construct($file)
{
parent::__construct($file);
$this->setFlags(SplFileObject::READ_CSV);
}
public function rewind()
{
parent::rewind();
$this->keys = parent::current();
parent::next();
}
public function current()
{
return array_combine($this->keys, parent::current());
}
public function getKeys()
{
return $this->keys;
}
}
If you do it this way, the details are nicely encapsulated away. Additionally it's more easy to deal with errors (e.g. count mismatch) inside the current() function so the code which makes use of the data does not need to deal with it.
Edit:
However the example given is short in terms of re-usablity. Instead of extending from SplFileObject it's much better to aggregate it:
class KeyedArrayIterator extends IteratorIterator
{
private $keys;
public function rewind()
{
parent::rewind();
$this->keys = parent::current();
parent::next();
}
public function current()
{
return array_combine($this->keys, parent::current());
}
public function getKeys()
{
return $this->keys;
}
}
The code is identical but the details that were encapsulated in the constructor are left out. This reduction allows to use the type more broadly, e.g. with (but not only with) the said SplFileObject:
$file = new SplFileObject('../data/test.csv');
$file->setFlags($file::READ_CSV);
$csv = new KeyedArrayIterator($file);
foreach ($csv as $line) {
var_dump($line);
}
If that now sounds too verbose, it again can be wrapped to give it again a nicer facade:
class CSVFile extends KeyedArrayIterator
{
/**
* #param string $file
*/
public function __construct($file)
{
parent::__construct(new SplFileObject($file));
$this->setFlags(SplFileObject::READ_CSV);
}
}
Thanks to the standard decorating-ability of TraversableIterator, the original constructor code from the first example of CSVFile could just be copied 100%.
This last addition also allows to keep the original code that uses the CSVFile Iterator intact:
$csv = new CSVFile('../data/test.csv');
foreach ($csv as $line) {
var_dump($line);
}
So just a quick refactoring to allow more code-reuse. You get a KeyedArrayIterator for free.
$csv_data = array_map('str_getcsv', file('Book.csv'));// reads the csv file in php array
$csv_header = $csv_data[0];//creates a copy of csv header array
unset($csv_data[0]);//removes the header from $csv_data since no longer needed
foreach($csv_data as $row){
$row = array_combine($csv_header, $row);// adds header to each row as key
var_dump($row);//do something here with each row
}
function processCsv($absolutePath)
{
$csv = array_map('str_getcsv', file($absolutePath));
$headers = $csv[0];
unset($csv[0]);
$rowsWithKeys = [];
foreach ($csv as $row) {
$newRow = [];
foreach ($headers as $k => $key) {
$newRow[$key] = $row[$k];
}
$rowsWithKeys[] = $newRow;
}
return $rowsWithKeys;
}
At this point I'm assuming you've already solved the issue but thought I'd throw in a suggested way around this, probably not the best/most elegant solution but it does the trick:
$row = 1;
$array = array();
$marray = array();
$handle = fopen('file.csv', 'r');
if ($handle !== FALSE) {
while (($data = fgetcsv($handle, 0, ',')) !== FALSE) {
if ($row === 1) {
$num = count($data);
for ($i = 0; $i < $num; $i++) {
array_push($array, $data[$i]);
}
}
else {
$c = 0;
foreach ($array as $key) {
$marray[$row - 1][$key] = $data[$c];
$c++;
}
}
$row++;
}
echo '<pre>';
print_r($marray);
echo '</pre>';
}
Try this
$csv = array_map("str_getcsv", file('file.csv', FILE_SKIP_EMPTY_LINES));
$header = array_shift($csv); // get header from array
foreach ($csv as $key => $value) {
$csv[$key] = array_combine($header, $value);
var_dump($csv[$key]['Model']);
}
var_dump($csv);
Try with this code:
$query = "SELECT * FROM datashep_AMS.COMPLETE_APPLICATIONS";
$export= mysql_query($query);
$first = true;
$temp = $export[0];
//echo "<pre>"; print_r($first); exit;
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=file.csv');
header('Pragma: no-cache');
header("Expires: 0");
$outstream = fopen("php://output", "w");
foreach($export as $result)
{
if($first){
$titles = array();
foreach($temp as $key=>$val){
$titles[] = $key;
}
//print_r ($titles);exit;
fputcsv($outstream, $titles);
}
$first = false;
fputcsv($outstream, $result);
}
fclose($outstream);
Thanks
The array_combine() function only works if header colums match the data colums otherwise an error is thrown.
In the answer of Tim Cooper above, instead of
$all_rows = array();
$header = null;
while ($row = fgetcsv($file)) {
if ($header === null) {
$header = $row;
continue;
}
$all_rows[] = array_combine($header, $row);
}
I would code, in a more elegant and efficient way:
$rows = null;
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
$rows[] = array_combine($header, $row);
}

CSV to Array PHP

I know there are a lot of resources out there for putting a CSV into an associative array, but I can't find anything that helps a noob like me do exactly what I want to do.
I currently have an associative array defined inside my PHP file:
$users = array(
'v4f25' => 'Stan Parker',
'ntl35' => 'John Smith',
);
I would like to move that array into a CSV file (users.txt) so:
v4f25, Stan Parker
ntl35, John Smith
The next step is to import users.txt so I can use it precisely like I was using the array $users.
Any help here? The last code I tried returned this: (which is not what I want)
array(2) {
["v4f25"]=>
string(5) "ntl35"
["Stan Parker"]=>
string(10) "John Smith"
}
What about the following?
$data = array();
if ($fp = fopen('csvfile.csv', 'r')) {
while (!feof($fp)) {
$row = fgetcsv($fp);
$data[$row[0]] = $row[1];
}
fclose($fp);
}
$users = array(
'v4f25' => 'Stan Parker',
'ntl35' => 'John Smith',
);
$fp = fopen('users.txt', 'w');
if ($fp) {
foreach ($users as $key => $value) {
fputcsv($fp, array($key, $value));
}
fclose($fp);
} else {
exit('Could not open CSV file')
}
See: fputcsv()
UPDATE - in the comments you're interested in how to read the file and get your users back out. Here's the return trip:
$users = array();
if (($handle = fopen("my-csv-file.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$users[$data[0]] = $data[1];
}
fclose($handle);
} else {
exit('Could not open CSV file');
}
if (count($users) == 0) {
exit('CSV file empty: no users found');
}
Here's a solution using fputcsv() which flattens the key/value pairs to an array before writing to disk.
$filehandle = fopen("csvfile.csv", "w");
if ($filehandle) {
foreach ($users as $key => $value) {
fputcsv($filehandle, array($key, $value);
}
fclose($filehandle);
}
else // couldn't open file
Try this (assuming your strings contain no commas):
$users = array(
'v4f25' => 'Stan Parker',
'ntl35' => 'John Smith',
);
foreach ($users as $k => $v) {
print "$k, $v\n";
}
Obviously you could then create the CSV file like so:
php above_script.php > outfile.csv
Now, to get from CSV back into an array you could use something like:
$file = 'outfile.csv';
$arr = array();
if (file_exists($file)) {
foreach (explode("\n", file_get_contents($file)) as $l) {
list($k, $v) = explode(',', $l);
$arr[trim($k)] = trim($l);
}
}
print_r($arr, true);
NOTES:
If your strings do (or might) contain commas, then you'll probably want to use a PHP builtin function to decode them - in which case the answers by harald and artlung are useful.
RFC 4180 describes how commas (and other values) are encoded in CSV, in case you want to roll your own CSV encoding/decoding functions for whatever reason.

Categories