1

So I need my SQL table to be downloadable. Right now my php file creates an excel file and forces a download but the excel file just has a large array with the table's data inside it.

Here is my code

$sql = mysql_query("SELECT * FROM table");

while ($row_user = mysql_fetch_assoc($sql))
{   $userinfo[] = $row_user;

print_r($row_user);

header("Content-Disposition: attachment; filename=\"papi.xls\"");
header("Content-Type: application/vnd.ms-excel;");
header("Pragma: no-cache");
header("Expires: 0");
$file = fopen("php://output","w");

foreach($userinfo as $data)
   {
  fputcsv($file,explode(',',$data),"\t");
 }

  fclose($file);
 }

Any help would be greatly appreciated, thank you in advance.

John Conde
  • 217,595
  • 99
  • 455
  • 496
Ray Mayo
  • 41
  • 6
  • 1
    FYI, [you shouldn't use `mysql_*` functions in new code](http://stackoverflow.com/questions/12859942/). They are no longer maintained [and are officially deprecated](https://wiki.php.net/rfc/mysql_deprecation). See the [red box](http://php.net/manual/en/function.mysql-connect.php)? Learn about [*prepared statements*](https://en.wikipedia.org/wiki/Prepared_statement) instead, and use [PDO](http://php.net/pdo) or [MySQLi](http://php.net/mysqli) - [this article](http://php.net/manual/en/mysqlinfo.api.choosing.php) will help you decide which one is best for you. – John Conde May 03 '17 at 14:23
  • Thanks for the quick answer. The server I'm working with is using an earlier version of PHP so it isn't depreciated in my case. – Ray Mayo May 03 '17 at 14:26
  • 1
    It *is* deprecated, you just can use it because it hasn't be removed from your server yet. But once you upgrade PHP your application will die. Basically, you're writing very temporary code. – John Conde May 03 '17 at 14:28
  • Does it need to specifically be an excel file, as CSV would be a much faster export and doesn't need any external libraries in PHP, just a foreach loop. CSV is pretty easy to open in excel. – Gary Mathis May 03 '17 at 14:40
  • No it doesn't need to be .xls it just has to be in a format compatible with excel – Ray Mayo May 03 '17 at 14:43
  • @GaryMathis - This ___is___ just a csv file – Mark Baker May 03 '17 at 14:43
  • John Conde, depreciated values aren't my problem right now and can be easily fixed later, but thanks for the heads up. – Ray Mayo May 03 '17 at 14:45
  • @MarkBaker I was thinking that, but the way he had his headers set were confusing me a bit because these are not CSV headers (or anywhere close). – Gary Mathis May 03 '17 at 14:46
  • Common issue, and a pet peeve of mine; creating a csv file and naming it xls or setting xls headers – Mark Baker May 03 '17 at 14:51
  • So how do you want the data to be shown if not as a large array? – Mark Baker May 03 '17 at 14:59
  • It needs to appear as a normal excel file. With each column and row displayed accordingly – Ray Mayo May 03 '17 at 15:08

3 Answers3

2

There are several PHP libraries which could help you format your output excel file before forcing the download. I've had good experiences using PHPExcel.

Using PHPExcel would not change accessing the data from your table, but it would change how you process the MySQL result:

//Collect result set
$data = array();
while ($row = mysql_fetch_assoc($sql)){ $data[] = $row; }

//initialize PHPExcel object
$objPHPExcel = new PHPExcel();
$objPHPExcel->getProperties()->setCreator("AuthorName")->setTitle("DocumentTitle");

//Write Column Titles into first Row
$col = 'A';
foreach($data[0] as $key => $val){ 
    $objPHPExcel->setActiveSheetIndex(0)->setCellValue($col.1, $key);
    $col++;
}

//Write Data
for($i = 0; $i < count($data); $i++){
    $col = 'A';
    $row = 2 + $i;
    foreach($data[$i] as $val){
        $objPHPExcel->setActiveSheetIndex(0)->setCellValue($col.$row, $val);
        $col++;
    }
}

//Set Header
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="DataExport.xlsx"');
header('Cache-Control: max-age=0');

//Output Results        
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
ob_end_clean();
$objWriter->save('php://output');

This is by no means the only option, however, I have found this library useful to export data into an XLSX file and have additional options to format it or add equations as needed for generating reports.

M31
  • 1,348
  • 12
  • 16
  • This does not answer the question – John Conde May 03 '17 at 14:28
  • @JohnConde, Fair enough I will update with an example. I was implying that formatting the output would help to get the desired result – M31 May 03 '17 at 14:30
  • Thanks I'll give this a try – Ray Mayo May 03 '17 at 15:48
  • I'm getting an internal server error when I try this. Do you know why this could be happening. I have PHPExcel installed. – Ray Mayo May 04 '17 at 14:26
  • @RayMayo, I would need to see the specific php error leading to the http 500 error to answer that. It is most likely due to a syntax error in the code, but I can't say for sure without more information. – M31 May 04 '17 at 14:31
0

So, This is a class that I wrote to export to CSV and keep everything lined up perfectly.

<?php
class exporter{
    private $map = array();
    private $data = array();
    private $nr = "\n";
    private $dl = ",";
    private $containerChar = "\"";
    private $exportData = "";


    function extractFullMapData($data){
        $map = array();
        foreach($data as $row){
            foreach($row as $colName => $col){
                if(!in_array($colName, $map)){
                    $map[] = $colName;
                }
            }
        }
        $this->setMap($map);
    }

    function addData($row, $multipleRows){
        if($multipleRows){
            foreach($row as $curRow){
                $this->addData($row);
            }
        }else{
            if(empty($this->map)){
                $this->extractMapData($row);
            }
            $newRow = array();
            foreach($this->map as $varName){
                if(isset($row[$varName])){
                    $newRow[$varName] = str_replace(, "\\".$this->containerChar, $row[$varName]);
                }else{
                    $newRow[$varName] = "";
                }
            }
            $this->data[] = $newRow;
        }
    }

    function export(){
        header('Content-Type: application/csv');
        header('Content-Disposition: attachment; filename="export.csv"');
        header('Pragma: no-cache');
        $this->buildExport();
        echo $this->exportData;
    }

    private function extractMapData($row){
        $this->setMap(array_keys($row));
    }

    private function setMap($map){
        $this->map = $map;
    }

    private function buildExport(){
        $exportData = "";
        foreach($this->map as $varName){
            $exportData .= ($exportData == "") ? "" : $this->dl;
            $exportData .= $this->containerChar.$varName.$this->containerChar;
        }
        foreach($this->data as $data){
            $row = "";
            $looped = false;
            foreach($data as $item){
                $row .= (!$looped) ? "" : $this->dl;
                $row .= $this->containerChar.$item.$this->containerChar;
                $looped = true;
            }
            $exportData .= $this->nr.$row;
        }
        $this->exportData = $exportData;
    }
}
?>

This code is derived from a class I wrote within my framework that handles exports for a ton of things. I've also written a class that can read back this data. The way this class is to be used is like this.

<?php
    $exporter = new exporter();
    $exporter->extractFullMapData($fullDataArray);
    $exporter->addData($fullDataArray, true);
    $exporter->export();
?>
Gary Mathis
  • 171
  • 1
  • 4
  • Thanks a ton, I'll give this a try. But what are the 'private' attributes you're putting on those variables at the top? I assume they're for security. – Ray Mayo May 03 '17 at 15:44
  • The private attributes are there so they cannot be changed during runtime, you can make them public if you would like. – Gary Mathis May 03 '17 at 19:26
  • Where exactly does this code pull the data from the table? – Ray Mayo May 04 '17 at 14:27
  • @RayMayo This simply takes an array of associate rows and turns it into a CSV file. You will need to do your database calls separately before the second code block above. – Gary Mathis May 04 '17 at 15:23
  • how do I add the mySQL table to this? – Ray Mayo May 10 '17 at 14:19
0

Something like this should work. I am using PDO instead of the deprecated mysql. I have also included the prepare statement that you may want to use in most cases along with bindParam to prevent SQL injection...even though it is not needed in this example. You will notice that I also changed your headers to indicate a CSV file rather than an Excel file.

<?php

$sql = "select * from table";

$dbh = new PDO("mysql:host=localhost; dbname=MYDB", "DBUSER", "DBPASSWORD");
$stmt = $dbh->prepare($sql);
$stmt->execute();

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');

$out = fopen('php://output', 'w');

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
    fputcsv($out, $row);
}

fclose($out);

I think the main thing throwing you off may have been your explode statement, which is typically not needed with fputcsv. However, I am not familiar with your data.

This follows your example of writing a CSV file. If you'd rather have an Excel .xls or .xlsx file you can use PHPExcel. It does take a bit longer to set up, and the documentation isn't the greatest, but it does a good job.

kojow7
  • 10,308
  • 17
  • 80
  • 135