Exporting an array to Excel - php

I'm trying to export an array of arrays to excel. I have it set up to be a header variable, and a data variable that basically builds a giant string to be executed in the export. However, only the header variable is going through. Let me show some code:
This is setting the parameters:
str_replace(" ", "_", $getData['name']);
$filename = $getData['name']."_summary.xls";
header("Content-Type: application/x-msdownload");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Pragma: no-cache");
header("Expires: 0");
Which goes to a function to get the information:
foreach($tempRS as $key=>$value)
{
foreach($value as $iKey=>$iValue)
{
if($count == 6)
{
$iValue = str_replace('"', '""', $iValue);
$iValue = '"'.$iValue.'"'."\n";
$data .= trim($iValue);
$count = 0;
}
else
{
$iValue = str_replace('"', '""', $iValue);
$iValue = '"'.$iValue.'"'."\t";
$data .= trim($iValue);
$count++;
}
}
}
$header = "ROW HEADER 1\tROW HEADER 2\tROW HEADER 3\tROW HEADER 4\tROW HEADER 5\tROW HEADER 6\n";
print "$header\n$data";
I can't seem to figure out why i'm losing the $data variable on the export.

Why not just fputcsv() to generate that CSV data for you? Or better yet, instead of making a .csv masquerade as an Excel file, you can use PHPExcel to output a native .xls/.xlsx and actually use formatting and formulae in the generated spreadsheet.

first of all, use echo instead of print. Print causes loads of overhead as it does both return and echo the data.
Secondly, don't put the variables within quotes, use
echo $header ."\n".$data;
To get to your question, does the foreach loops actually loop? Have you checked the $data if it contains any data?
A better solution might be this:
$header = '';
echo $header;
foreach() loop here {
//echo the data here instead of putting it in a variable
}
Or maybe better, use http://nl.php.net/fputcsv

I tested your code and it seems that your trim() method is trimming your \n and \t.
If you remove it, your code should be fine.
Here's my code to export a array of product in excel.
Only one little problem with this code : Excel doesn't want to open it because of a format problem, but if you click "yes" when it promps an error message, you'll be ok...
No problem with open office though
Working on a fix...
$filename = "products_exports " . date("Y-m-d H_i_s") . ".xls";
/**
* Tell the browser to download an excel file.
*/
header("Content-Type: application/x-msdownload; charset=utf-8");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Pragma: no-cache");
header("Expires: 0");
/**
* The header of the table
* #var string
*/
$header = "";
/**
* All the data of the table in a formatted string
* #var string
*/
$data = "";
//We fill in the header
foreach ($productArray as $i => $product) {
$count = 0;
foreach ($product as $key => $value) {
if($count == count((array)new Product("")) - 1){
$header.= $key;
}
else{
$header.= $key . "\t";
$count++;
}
}
break; /*One loop is enough to get the header !*/
}
//And then the data by the product and all the categories
/*Where $productArray = This can be your custom array of object. eg.
array[0] = new YouCustomObject(your params...);
*/
foreach ($productArray as $i => $product) {
$count = 0;
foreach ($product as $key => $value) {
if($count == count((array)new Product("")) - 1){
$value = str_replace('"', '""', $value);
$value = '"' . $value . '"' . "\n";
$data .= $value;
$count = 0;
}
else{
$value = str_replace('"', '""', $value);
$value = '"' . $value . '"' . "\t";
$data .= $value;
$count++;
}
}
}
echo $header ."\n". $data;

Related

Export WordPress database table to excel file with all meta informations

I try to export an Excel file from a WordPress database users table. It works in general. But I also need to add all user_meta data into this file. How could I combine this?
My code:
<?php
//Include the wp-load.php file
include('../../../../wp-load.php');
//As this is external file, we aren't using the WP theme here. So setting this as false
define('WP_USE_THEMES', false);
global $wpdb;
$table_name = $wpdb->prefix . 'users';
$file = 'email_csv'; // ?? not defined in original code
$results = $wpdb->get_results("SELECT * FROM $table_name", ARRAY_A);
if (empty($results)) {
return;
}
$csv_output = '"'.implode('";"',array_keys($results[0])).'";'."\n";;
foreach ($results as $row) {
$csv_output .= '"'.implode('";"',$row).'";'."\n";
}
$csv_output .= "\n";
$filename = $file."_".date("Y-m-d_H-i",time());
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: csv" . date("Y-m-d") . ".csv");
header( "Content-disposition: filename=".$filename.".csv");
print $csv_output;
exit;
For your custom list of meta_keys (company, firstname, address) you can define an array containing these, and add them to the CSV as trailing columns like so:
global $wpdb;
// Define your custom meta keys you want to add as columns HERE:
$meta_keys = ['company', 'firstname', `address`]; // can contain as many as you want
// This function will load the desired keys as an associative array
// in the form: [$meta_key_1 => $meta_value_1, ..., $meta_key_n => $meta_value_n]
function load_user_meta($user_id, $keys) {
$meta_row = [];
foreach ($keys as $meta_key) {
$value = get_user_meta($user_id, $meta_key, true);
$meta_row[$meta_key] = $value;
}
return $meta_row;
}
$table_name = $wpdb->prefix . 'users';
$results = $wpdb->get_results("SELECT * FROM $table_name", ARRAY_A);
if (empty($results)) {
return;
}
// Merge the result's keys with the list of custom meta keys:
$csv_output = '"'.implode('";"',array_merge(array_keys($results[0]), $meta_keys )).'";'."\n";
foreach ($results as $row) {
// load the custom meta for this user's ID:
$custom_meta_row = load_user_meta($row['ID'], $meta_keys);
// merge the acquired user meta into the row output:
$csv_output .= '"'.implode('";"',array_merge($row, $custom_meta_row)).'";'."\n";
}
$csv_output .= "\n";
If a certain meta_key does not exist for all users, this still works since get_user_meta will produce an empty string for these cases.
Additional note: it's generally not a good idea to produce CSV output the way you do. This works as long as there are no " and ; characters anywhere in your user_meta - those characters would have to be escaped with a certain escape character. Therefore, it is better to use fputcsv on the output stream ( $outstream = fopen("php://output", 'w');).

How to get some values from a 3d array into a new array inside a while loop

I would like to get a CSV export working where the user picks some optional fields to export with the required fields. I'm having trouble just adding the list of fields to export into this csv export method.
The list of fields i want to be part of the export ends up in the $fields array, but I'm not sure how to integrate that into the export loop for each row.
// database conection stuff and $_POST data validation/sanitation above here, not relevant. From a CSV export form.
$exportToCSVQuery = " SELECT $allRequestedFields' FROM my_table ";
// if the (run query) is valid then...
if ($exportToCSVResult = mysqli_query($mysqli, $exportToCSVQuery)) {
// count number of rows in result.
$exportToCSVResultNumRows = mysqli_num_rows($exportToCSVResult);
// if there is at least one result.
if ($exportToCSVResultNumRows > 0 ) {
if ($safeDelimiter == 'Comma') {
$delimiter = ",";
}
elseif ($safeDelimiter == 'Semi-colon') {
$delimiter = ";";
}
else{
$delimiter = ",";
}
//create a file pointer
$f = fopen('php://memory', 'w');
//set column headers
$fields = array('ID', 'Name', 'em', 'type', 'reason', 'result', 'status', 'notes', 'datetime', 'requestedby', 'requestedbycitrixid', 'week', 'period', 'year', 'historylog');
// put required and optional fields together from the form.
$fields = $fields+$safeFieldsArray;
fputcsv($f, $fields, $delimiter);
//create $linedata information, formatted in a php assoc row format.
//flip the $fields array so the values become the keys, needed for the next step.
$fieldsFlipped = array_flip($fields);
//output each row of the data, format line as csv and write to file pointer
while($exporttocsvrow = $exporttocsvresult->fetch_assoc()){
$fieldsarrayforlinedata = array_intersect_key($exporttocsvrow,$fieldsFlipped);
// flip array
$flippedfieldsarrayforlinedata = array_flip($fieldsarrayforlinedata);
fputcsv($f, $flippedfieldsarrayforlinedata, $delimiter);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $safeFilename . '";');
//output all remaining data on a file pointer
fpassthru($f);
}
else {
Echo 'No Data to Export';
}
}
else {
$exportToCSVResultErr = "Error: " .mysqli_error($mysqli) ;
trigger_error("Query Error: ".$exportToCSVQuery." Error:" $exportToCSVResultErr, E_USER_WARNING);
}
Edit: I tried adding something based on Pradeep’s answer below to the loop like so:
while($exporttocsvrow = $exporttocsvresult->fetch_assoc()){
$fieldsarrayforlinedata = array_intersect_key($exportToCSVRow,$fieldsFlipped);
unset($valuesArray);
$valuesArray = array();
foreach ($fieldsFlipped as $key){
foreach ($exportToCSVRow[$key] as $values) {
$valuesArray[] = $values;
}
}
fputcsv($f, $valuesArray, $delimiter);
}
But this just results in the CSV with headings and blank data. I guess I'm probably adding that loop incorrectly.
I hope this will help to get the desired output.
$row=array('fieldA'=>array('data1','data2','data2','data3'),'fieldB'=>array('data1','data2','data3'),'fieldC'=>array('data1','data2','data3'));
$key=array('fieldA','fieldB');
foreach($key as $key){
foreach($row[$key] as $values){
echo $values;
}
}

How to replace certain values pulled from MySQL DB while printing to excel spreadsheet in PHP?

I'm using PHP to create an excel document with the data I'm pulling from a MySQL Database.
While cycling through the rows at the end and printing out my data to each cell, I want to replace each cell that has the string/int equivical of '-1' to '0'.
Here's my code so far,
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=exported-data.csv');
$sql = "SELECT * FROM db_table";
$sth = $db->prepare($sql);
$sth->execute();
$filename = date('d.m.Y').'.csv';
$data = fopen($filename, 'w');
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
$csv = implode(',', $row) . "\n";
fwrite($data, $csv);
print_r($csv);
}
echo "\r\n";
This works efficiently with getting the data from the db and outputting to an excel spreadsheet but how would I check each record pulled and replace it with a 0 if the record is -1?
So visually, if my database looked like this:
id mon tues wed
85 -1 -1 75
36 -1 12 -1
The excel spreadsheet would pop out like so:
85 0 0 75
36 0 12 0
I agree with u_mulder about replacing the -1 results with 0. I would build upon that with some error checking, cache busting, and I would use PHP's fputcsv function to stream the file directly to STDOUT. This will reduce memory usage, increase performance, and ensure that field values containing double quotes or commas are correctly parsed.
<?php
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=exported-data.csv');
header("Pragma: no-cache");
header("Expires: 0");
$sql = "SELECT * FROM db_table";
$sth = $db->prepare($sql);
$sth->execute();
$file = fopen('php://output', 'w');
if(false === $file) {
throw new Exception("Failed to create output stream resource.");
}
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
foreach($row as &$field) {
if("-1" == strval($field)) {
$field = "0";
}
}
if(false === fputcsv($file, $row)) {
throw new Exception("Failed to write row to CSV file.");
}
}
fclose($file);
I suggest you to do this:
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
// check every key of $row
// use & to pass $value by reference
foreach ($row as &$value) {
// use strval to get string representation of a $value
if (strval($value) == '-1') {
$value = '0';
}
}
$csv = implode(',', $row) . "\n";
fwrite($data, $csv);
print_r($csv);
}
Doing this at the SQL level will be much faster then analyzing each row individually with PHP especially as your database gets larger. Use CASE.
header('Content-Type: text/csv');
header('Content-Disposition: attachment;filename=exported-data.csv');
$sql = "
SELECT
id
,CASE WHEN monday = -1 THEN 0 ELSE monday END AS monday
,CASE WHEN tuesday = -1 THEN 0 ELSE tuesday END AS tuesday
,CASE WHEN wednesday = -1 THEN 0 ELSE wednesday END AS wednesday
FROM db_table";
$sth = $db->prepare($sql);
$sth->execute();
$filename = date('d.m.Y').'.csv';
$data = fopen($filename, 'w');
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
$csv = implode(',', $row) . "\n";
fwrite($data, $csv);
print_r($csv);
}
echo "\r\n";
Filter $row to replace negative values by 0:
$row = array_map(function ($val) { return $val > 0 ? $val : 0; }, $row);
Also, consider using fputcsv() to generate valid CSV files instead of trying to do it yourself. Simply separating values by , may work when you are dealing only with numbers but this may create problems with strings.
$output = fopen('php://output', 'w');
while ($row = $sth->fetch(PDO::FETCH_ASSOC))
{
$row = array_map(function ($val) { return $val > 0 ? $val : 0; }, $row);
fputcsv($data, $row);
fputcsv($output, $row);
}
Another problem that you may face when generating CSV files to be opened with Excel is localization. Excel expects the CSV files it opens to be formatted according to the local system localization settings. A record such as 1,2,3,1.23 may work in English, having each number separated in a column and having 1.23 with its decimals interpreted correctly, but will not work on many others languages, specially when , is used as decimal separator instead of ..
With that in mind you may want to implement localization to your CSV aswell, and your best guess is to use the client's browser language settings:
$locale = isset($_COOKIE['locale']) ? $_COOKIE['locale'] : $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$locale = preg_split('/[,;]/', $locale);
setlocale(LC_ALL, $locale[0]);
$locale = localeconv();
$output = fopen('php://output', 'w');
while ($row = $sth->fetch(PDO::FETCH_ASSOC))
{
$row = array_map(function ($val) { return $val > 0 ? $val : 0; }, $row);
fputcsv($data, $row, $locale['decimal_point'] == ',' ? ';' : ',');
fputcsv($output, $row, $locale['decimal_point'] == ',' ? ';' : ',');
}
Now, if you don't want to have this complication, you can instead generate real Excel .XLSX files using a library such as PHPExcel.

optimizing CSV export with codeigniter

i have recently migrated my export CSV code from core php to code igniter. the code works well but its very slow when exporting very large amount of data..
here is my old code:
function exportCSV($qry,$con,$title)
{
$result = mysql_query($qry, $con) or die(mysql_error($con));
header('Content-Type: text/csv; charset=UTF-8');
header("Cache-Control: no-store, no-cache");
header("Content-Disposition: attachment;filename=".$title."-".date('mdY').".csv");
//echo "\xEF\xBB\xBF";
$row = mysql_fetch_assoc($result);
if ($row) {
echocsv(array_keys($row));
}
while ($row) {
echocsv($row);
$row = mysql_fetch_assoc($result);
}
}
function echocsv($fields)
{
$separator = '';
foreach ($fields as $field) {
if (preg_match('/\\r|\\n|,|"/', $field)) {
$field = '"' . str_replace('"', '""', $field) . '"';
}
echo $separator . $field;
$separator = ',';
}
echo "\r\n";
}
and here is my codeigniter code that is very slow... exporting data to CSV with 77000 rows took about 15 minutes excluding the download time..
public function exportCSV()
{
set_time_limit(0);
$delimiter = ",";
$newline = "\r\n";
$curr_date_time = date("l jS \of F Y h:i:s A");
$this->products_model->set_venture($this->selected_venture['abbrev']);
if($get_data = $this->input->get())
{
$data = $this->products_model->export_model($get_data);
$download = $this->dbutil->csv_from_result($data, $delimiter, $newline);
force_download('export - '.$curr_date_time.'.csv', $download);
}
else
{
show_404('page', FALSE);
}
}
public function export_model($params = NULL)
{
if ($params != NULL)
{
if ($params['name_filter'] != '')
{
$this->crawler_db->like('name', $params['name_filter']);
}
if ($params['comp_filter'] != '')
{
$this->crawler_db->where('fk_competitor_website', $params['comp_filter']);
}
}
return $this->crawler_db->get('pcrawler_'.$this->venture.'.products_final');
}
Hi check database util class can work for you its really simple to generate good CSV files here is the code
$this->load->dbutil();
$query = $this->db->query("SELECT * FROM mytable");
echo $this->dbutil->csv_from_result($query);
please read the document here Codeigniter CSV export with DB util
I would look at debugging the function, to see where it's actually slow.
Simple way is to use the Benchmarking Class to see if it's the query that's slow, or the call to csv_from_result().
Is it still slow when you don't pass any params to export_model()? If it's only slow when you run a like or where against the database, then maybe you need to add some indexes?
https://ellislab.com/codeigniter/user-guide/libraries/benchmark.html
Once you find the bottleneck, you can go from there to
code igniter's csv_from_result is really slow for me, i made a different one to suit my needs.. it can be re used for other CSV exports too...
public function array_to_csv($array)
{
if (count($array) == 0) {
return null;
}
ob_start();
$df = fopen("php://output", 'w');
fputcsv($df, array_keys(reset($array)));
foreach ($array as $row) {
fputcsv($df, $row);
}
fclose($df);
return ob_get_clean();
}
usage:
$this->array_to_csv($array);

Export sqlite table using php

Im just getting started with PHP, and I need to export a table from my sqlite database to a CSV or ideally XLS. I have found an example using mysql, but i cant convert it to work with sqlite.
Here is what i have so far:
<?php
$db = new PDO('sqlite:../ccm.sqlite');
$query = $db->query('SELECT * FROM Emails');
$export = sqlite_query ($query);
$fields = sqlite_num_fields ( $export );
for ( $i = 0; $i < $fields; $i++ ){
$header .= sqlite_field_name( $export , $i ) . "\t";
}
while( $row = sqlite_fetch_row( $export ) ){
//sqlite_fetch_row doesnt actually exist...
$line = '';
foreach( $row as $value ){
if ( ( !isset( $value ) ) || ( $value == "" ) ){
$value = "\t";
}else{
$value = str_replace( '"' , '""' , $value );
$value = '"' . $value . '"' . "\t";
}
$line .= $value;
}
$data .= trim( $line ) . "\n";
}
$data = str_replace( "\r" , "" , $data );
if ( $data == "" ){
$data = "\n(0) Records Found!\n";
}
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=emails.xls");
header("Pragma: no-cache");
header("Expires: 0");
print "$header\n$data";
?>
Can anyone help me out with this?
or if there is a simpler way that would be great.
Cheers
You are mixing PDO methods with sqlite_* functions; try using one or the other:
$db = new PDO("sqlite:../ccm.sqlite");
$query = $db->query("select * from emails");
$first_row = true;
while ($row = $query->fetch(PDO::FETCH_ASSOC))
{
if ($first_row)
{
// I'm not sure how to get the field names using a PDO method but
// we can use the first row's (or any row's) key values as these
// are the field names.
$first_row = false;
$number_of_fields = count($row);
$field_names = array_keys($row);
$first_field_name = $field_names[0];
}
// do stuff here with the row
print_r($row);
}
or
$db = sqlite_open("../cmm.sqlite");
$query = sqlite_query($db, "select * from emails");
$number_of_fields = sqlite_num_fields($query);
$first_field_name = sqlite_field_name($query, 0);
while ($row = sqlite_fetch_array($query))
{
// do stuff here with the row.
print_r($row);
}
I'm not 100% sure but I think PDO works with sqlite3 databases and sqlite_* functions works with sqlite2 databases.
If I need to quickly get data out of a sqlite3 database as CSV files, I use the sqlite3 CLI:
$ sqlite3 ccm.sqlite
sqlite> .mode csv
sqlite> .output emails.csv
sqlite> .headers on
sqlite> select * from emails
sqlite> .output stdout
sqlite> .quit
$ cat emails.csv
This starts the sqlite3 CLI opening the ccm.sqlite database, sets the output mode to csv (the format of select statements), sets output to the file named emails.csv, turns select column headers on (optional), selects all the data in the emails table, sets output to standard out (closing the emails.csv file), quits the CLI and checks the output by sending it to standard out.
There are other formats you can output, type .help at the sqlite3 CLI prompt:
.mode MODE ?TABLE? Set output mode where MODE is one of:
csv Comma-separated values
column Left-aligned columns. (See .width)
html HTML <table> code
insert SQL insert statements for TABLE
line One value per line
list Values delimited by .separator string
tabs Tab-separated values
tcl TCL list elements
I've improved upon Stacey's answer, and thought I'll share it here.
I've added headers to make the browser download the output as a CSV file.
<?
// Set headers to make the browser download the results as a csv file
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=filename.csv");
header("Pragma: no-cache");
header("Expires: 0");
// Connect to DB
$conn = new PDO('sqlite:db_name.db');
// Query
$query = $conn->query("SELECT * FROM some_table");
// Fetch the first row
$row = $query->fetch(PDO::FETCH_ASSOC);
// If no results are found, echo a message and stop
if ($row == false){
echo "No results";
exit;
}
// Print the titles using the first line
print_titles($row);
// Iterate over the results and print each one in a line
while ($row != false) {
// Print the line
echo implode(array_values($row), ",") . "\n";
// Fetch the next line
$row = $query->fetch(PDO::FETCH_ASSOC);
}
// Prints the column names
function print_titles($row){
echo implode(array_keys($row), ",") . "\n";
}
If you need to send a CSV file directly to the browser, without writing in an external file, you can open the output and use fputcsv on it.
<?php
$out = fopen('php://output', 'w');
// print column header
fputcsv($out, array_keys($row)));
//or print content directly
fputcsv($out, array_values($row)));
fclose($out);
?>

Categories