将整数转换为Excel列(字母或字母组合) - 完整示例PHP,单元测试和解释

时间:2015-10-01 15:38:14

标签: php excel auto-generate letter pdostatement

假设我们有一个像这样的函数,它接受PDOStatement(任何查询)并自动生成一个excel文件(使用PHPExcel库):

    /**
* Create an Excel file from an opened PDOStatement. Return the path to access the Excel file
*   or an empty string if we were not able to create the Excel File
*
*
* @param $errorMessage String to return the error message.
* @param $source PDOStatement containing the data to export to Excel. 
* @param $rows Int Use to return the number of rows exported (the header row isn't counted).
* @param $name String name to give to the Excel file (no need to specify the extension).
* @param $columnName (optional) String Array used for the name of the row in the Excel file.
*
* @return String
*/
public static function createExcelFromRS(&$errorMessage, PDOStatement &$source, &$rows , $name, array $columnName = array()){

    $errorMessage = "";

    $name = self::validateFileExtention($name, "xlsx");

    $path = realpath(dirname(__FILE__)) . '/Archive/' .  $name;

    $rows = 0;

    $totalCols = 0;


    $excel = new PHPExcel();

    $writer = PHPExcel_IOFactory::createWriter($excel, "Excel2007");

    $sheet = $excel->getActiveSheet();

    $sheet->setTitle($name);


    while ($row = $source->fetch(PDO::FETCH_ASSOC)){

        if ($rows === 0){

            $columnName = self::validateColumnNameArray($columnName, $row);

            $totalCols = count($row);

            $sheet->getStyle('A1:' . self::convertNumberToExcelCol($totalCols) . '1')->getFont()->setBold(true)->setSize(12);

            for ($column = 1; $column <= $totalCols; $column++){

                $sheet->getCell(self::convertNumberToExcelCol($column) . '1')->setValue($columnName[$column - 1]);

                $sheet->getColumnDimension(self::convertNumberToExcelCol($column))->setAutoSize(true);
            }

            $rows = 1;
        }

        $rows++;

        $column = 1;

        foreach ($row as $field){

            $sheet->getCell(self::convertNumberToExcelCol($column) . $rows)->setValue($field);

            $column++;
        }
    }

    $writer->save($path);

    unset($sheet, $writer, $excel);


    if ($rows < 1){

        if (is_file($path)){

            unlink($path);  
        }

        $errorMessage =str_replace("[TYPE]", "EXCEL", GeneralDbManager::getInstance()->getErrorMessage('NO_DATA_TO_EXPORT_FILE_ERR', 'There is no data to export to the [TYPE] file.'));    
    }
    elseif(!is_file($path)){

        $errorMessage = str_replace(array("[TYPE]", "[NAME]"), array("EXCEL", $name), GeneralDbManager::getInstance()->getErrorMessage('EXPORT_NO_CREATED_FILE_ERR', 'We were not able to create the [TYPE] file: [NAME].'));
    }
    else{

        $rows --;
    }

    return (empty($errorMessage) ? $path : "");
}

我们希望使用convertNumberToExcelCol函数将整数值转换为excel列。

让我们首先解释这个方法来构建excel文件,下一篇文章将解释获取列的算法。

这些方法作为参数:

  • ErrorMessage :用于返回错误消息
  • 来源:包含要推送到Excel文件的数据
  • :用于返回导出的数据行数
  • 名称:要提供给Excel文件的名称
  • columnName :与人类可读列名称(或翻译)一起使用的可选数组。如果省略,则该方法使用查询中的字段名称。

第一行用于初始化参数(PHP使用松散类型,因此我们必须小心参数)。

此功能确保名称具有有效的名称/扩展名:

/**
* Validate that the file $name has the proper file $extension
* and return the fixed name with the proper extension
*
* Note: No modification will be made if the extension is not a string or is empty
*
* @param $name String file name with or without extension
* @param $extension String example: csv, xls
*
* @return String
*/
public static function validateFileExtention($name, $extension){

    if (is_string($extension)){

        $extension = "." . str_replace(".", "", $extension);

        if (strlen($extension) > 1){

            if (!is_string($name) or empty($name) or strpos($name, ".") === 0){

                $name = "my_file" . $extension; 
            }
            elseif(strpos(strtolower($name), $extension) === false){

                if (strrpos($name, ".") === false){

                    $name .= $extension;    
                }
                else{

                    if (substr_count($name, ".") > 1){

                        $name = str_replace(".", "", $name) . $extension;
                    }
                    else{

                        $name = str_replace(substr($name, strrpos($name, ".")), $extension, $name);
                    }
                }
            }
        }
    }

    return $name;
}

然后我们打开与excel文件的连接:

$excel = new PHPExcel();

$writer = PHPExcel_IOFactory::createWriter($excel, "Excel2007");

$sheet = $excel->getActiveSheet();

$sheet->setTitle($name);

此函数确保列名数组的长度与行数组中的字段数相同。

/**
* Take the array containing the $columnName for data export (CSV, Excel) and make sure
* that it is the number of entry as there are fields in $row.
*
* If column name are missing, we will use the column name used in the query.
*
* Return the merged array
*
* @param $columnName Array containing the column names
* @param $row Array produce by fetch(PDO::FETCH_ASSOC).
*
* @return Array ($columnName)
*/
private static function validateColumnNameArray(array &$columnName, array &$row){

    $buffer = array();

    $colPDO = count($row);

    $count = count($columnName);

    if ($count < $colPDO){

        foreach ($row as $key => $value){

            $buffer[] = $key;   
        }

        for($index = $count; $index < $colPDO; $index++){

            $columnName[] = $buffer[$index];
        }
    }

    unset($buffer);

    return $columnName;
}

validateFileExtentionvalidateColumnNameArray都是针对具有CSV创建功能的共享代码:

    /**
* Create a CSV file from an opened PDOStatement. Return the path to access the CSV file
*   or an empty string if we were not able to create the CSV File
*
*
* @param $errorMessage String to return the error message.
* @param $source PDOStatement containing the data to export to CSV
* @param $rows Int Use to return the number of rows exported (the header row isn't counted).
* @param $name String name to give to the CSV file (no need to specify the extension).
* @param $columnName (optional) String Array used for the name of the row in the CSV file.
*
* @return String
*/
public static function createCSVFromRS(&$errorMessage, PDOStatement &$source, &$rows , $name, array $columnName = array()){

    $errorMessage = "";

    $name = self::validateFileExtention($name, "csv");

    $path = realpath(dirname(__FILE__)) . '/Archive/' . $name;

    $rows = 0;


    $file = fopen($path, "w");

    while ($row = $source->fetch(PDO::FETCH_ASSOC)){

        if ($rows === 0){

            fputcsv($file, array_map('utf8_decode',self::validateColumnNameArray($columnName, $row)));
        }

        fputcsv($file, array_map('utf8_decode',array_values($row)));

        $rows++;
    }

    fclose($file);


    if ($rows < 1){

        if (is_file($path)){

            unlink($path);  
        }

        $errorMessage =str_replace("[TYPE]", "CSV", GeneralDbManager::getInstance()->getErrorMessage('NO_DATA_TO_EXPORT_FILE_ERR', 'There is no data to export to the [TYPE] file.'));  
    }
    elseif(!is_file($path)){

        $errorMessage = str_replace(array("[TYPE]", "[NAME]"), array("CSV", $name), GeneralDbManager::getInstance()->getErrorMessage('EXPORT_NO_CREATED_FILE_ERR', 'We were not able to create the [TYPE] file: [NAME].'));
    }


    return (empty($errorMessage) ? $path : "");
}

如果它是我们添加到excel文件的第一行,那么我们设置基本格式:

if ($rows === 0){

            $columnName = self::validateColumnNameArray($columnName, $row);

            $totalCols = count($row);

            $sheet->getStyle('A1:' . self::convertNumberToExcelCol($totalCols) . '1')->getFont()->setBold(true)->setSize(12);

            for ($column = 1; $column <= $totalCols; $column++){

                $sheet->getCell(self::convertNumberToExcelCol($column) . '1')->setValue($columnName[$column - 1]);

                $sheet->getColumnDimension(self::convertNumberToExcelCol($column))->setAutoSize(true);
            }

            $rows = 1;
        }

使用getStyle方法,我们将标题行设置为粗体和12尺寸。

getCOlumnDimension方法用于设置自动调整大小,因此用户在打开文件时不必自己调整列的大小。

循环的其余部分是将数据从行数组传输到excel文件。

在循环之后,我们关闭连接并取消设置变量use来管理Excel。

然后是错误管理:

if ($rows < 1){

        if (is_file($path)){

            unlink($path);  
        }

        $errorMessage =str_replace("[TYPE]", "EXCEL", GeneralDbManager::getInstance()->getErrorMessage('NO_DATA_TO_EXPORT_FILE_ERR', 'There is no data to export to the [TYPE] file.'));    
    }
    elseif(!is_file($path)){

        $errorMessage = str_replace(array("[TYPE]", "[NAME]"), array("EXCEL", $name), GeneralDbManager::getInstance()->getErrorMessage('EXPORT_NO_CREATED_FILE_ERR', 'We were not able to create the [TYPE] file: [NAME].'));
    }
    else{

        $rows --;
    }

消息存储在数据库中,因此我们可以向用户提供已翻译的消息。我在我的消息中使用泛型[TYPE]和[NAME]标记,我用适当的文件类型和文件名替换。

这允许我在我正在生成的excel和CSV文件(或任何类型的文件)中重用此通用消息。

如果创建的文件为空,我将其删除。此操作是可选的,但我想在完成后立即从磁盘中清除未使用的文件。

另一种方法是使用函数清除存储目录:

/**
* Clear all the archives (zip) files in the archive folder.
*/
public static function emptyArchiveFolder(){

    $handle = NULL;
    $path = realpath(dirname(__FILE__)) . '/Archive/';

    if (is_dir($path) and $handle = opendir($path)) {

        while (false !== ($entry = readdir($handle))) {

            $file = $path . $entry;

            if (is_file($file)){

                unlink($file);  
            }
        }

        unset($handle);
    }

}

当我在午夜进行源文件和数据库的自动备份过程时,我个人只使用这种方法。在白天运行它会增加删除其他用户使用文件的机会。

这就是为什么我正在考虑最佳做法,一旦通过浏览器将文件发送给用户,就删除这些文件,并将清洁方法留作维护用途。

如果没有错误,我将行数减1,因为我不想计算标题行。如果您将标题行视为数据行,则可以删除该行。

最后,这些方法返回访问新创建文件的路径:

return (empty($errorMessage) ? $path : "");

但仅在没有错误的情况下。因此,如果函数返回一个空字符串,则表示发生了错误。

PHP类型松散,您可以返回包括布尔值甚至错误消息在内的任何内容,但我更喜欢返回始终使用相同数据类型以实现常量目的。我个人最喜欢的方法是布尔返回值和引用传递的错误消息变量。所以我可以使用这样的代码:

$errorMessage = "";

            if ($_SESSION["adminAccount"]->updateAccountInfo($errorMessage,
                                                        (isset($_POST['FIRST_NAME_TEXT']) ? $_POST['FIRST_NAME_TEXT'] : $_SESSION["adminAccount"]->getFirstName()),
                                                        (isset($_POST['LAST_NAME_TEXT']) ? $_POST['LAST_NAME_TEXT'] : $_SESSION["adminAccount"]->getLastName()),
                                                        (isset($_POST['EMAIL_TEXT']) ? $_POST['EMAIL_TEXT'] : $_SESSION["adminAccount"]->getEmail()))){

                PageManager::displaySuccessMessage("Your account information were saved with success.", "USER_ACCOUNT_INFORMATION_SAVED");

            }
            else{

                PageManager::displayErrorMessage($errorMessage);    
            }

这样,错误由类方法在内部管理,成功消息可以根据视图上下文进行调整。布尔返回值用于确定是否必须显示错误或成功消息。

注意:单位测试将包含在我的答案中。

来自蒙特利尔的Jonathan Parent-Lévesque

0 个答案:

没有答案