尽管 PHP 没有任何直接读取特定电子表格格式的能力(如 XLSX、ODS 等),但它有读取(CSV 逗号分隔值)文件的能力,因此,为了处理客户的电子表格,需要要求他们提供 CSV 格式的文件,或者需要自己进行转换。
准备...
当要把电子表格(即CSV文件)上传到数据库中时,有三个主要的考虑因素:
大规模的文件迭代使用前面的示例来处理,我们使用 fgetcsv()
函数将CSV行转换为PHP数组。最后,我们使用(PDO PHP Data Objects )类建立数据库连接并进行插入。
如何做...
1.首先,我们定义一个 Application\Database\Connection
类,该类根据提供给构造函数的一组参数创建一个 PDO 实例。
复制 <? php
namespace Application \ Database ;
use Exception ;
use PDO ;
class Connection
{
const ERROR_UNABLE = 'ERROR: Unable to create database connection' ;
public $pdo;
public function __construct ( array $config)
{
if ( ! isset ( $config[ 'driver' ] ) ) {
$message = __METHOD__ . ' : ' . self:: ERROR_UNABLE . PHP_EOL ;
throw new Exception ($message);
}
$dsn = $config[ 'driver' ]
. ':host=' . $config[ 'host' ]
. ';dbname=' . $config[ 'dbname' ];
try {
$this -> pdo = new PDO ($dsn ,
$config[ 'user' ] ,
$config[ 'password' ] ,
[ PDO :: ATTR_ERRMODE => $config[ 'errmode' ]]);
} catch ( PDOException $e) {
error_log ( $e -> getMessage ()) ;
}
}
}
2.然后,我们加入一个 Application\Iterator\LargeFile
的实例。我们在这个类中添加了一个新的方法,这个方法被设计用来迭代 CSV 文件:
复制 protected function fileIteratorCsv ()
{
$count = 0 ;
while ( ! $this -> file -> eof () ) {
yield $this -> file -> fgetcsv () ;
$count ++ ;
}
return $count;
}
3.我们还需要将 Csv 添加到允许的迭代器方法列表中:
复制 const ERROR_UNABLE = 'ERROR: Unable to open file' ;
const ERROR_TYPE = 'ERROR: Type must be "ByLength", "ByLine" or "Csv"' ;
protected $file;
protected $allowedTypes = [ 'ByLine' , 'ByLength' , 'Csv' ];
如何运行...
首先我们定义一个配置文件, /path/to/source/config/db.config.php
,其中包含数据库连接参数:
复制 <? php
return [
'driver' => 'mysql' ,
'host' => 'localhost' ,
'dbname' => 'php7cookbook' ,
'user' => 'cook' ,
'password' => 'book' ,
'errmode' => PDO :: ERRMODE_EXCEPTION ,
];
接下来,我们利用第一章 《建立基础》 中定义的自动加载类,获得 Application\Database\DatabaseConnection
和 Application\Iterator\LargeFile
的实例,定义一个调用程序,chap_02_uploading_csv_to_database.php
。
复制 define ( 'DB_CONFIG_FILE' , '/../data/config/db.config.php' ) ;
define ( 'CSV_FILE' , '/../data/files/prospects.csv' ) ;
require __DIR__ . '/../../Application/Autoload/Loader.php' ;
Application \ Autoload \ Loader :: init ( __DIR__ . '/..' ) ;
之后,我们设置了一个 try {...} catch () {...}
块,它可以捕获 Throwable
。这样我们就可以同时捕获异常和错误:
复制 try {
// ...
} catch ( Throwable $e) {
echo $e -> getMessage () ;
}
在 try {...} catch () {...}
块里面,我们得到一个连接和大文件迭代器类的实例:
复制 $connection = new Application \ Database \ Connection (
include __DIR__ . DB_CONFIG_FILE);
$iterator = ( new Application \ Iterator \ LargeFile ( __DIR__ . CSV_FILE))
-> getIterator ( 'Csv' ) ;
然后我们利用PDO的 prepare/execute 功能。准备语句 SQL 中的 ?
表示参数标记,参数在SQL执行时会被替换:
复制 $sql = ' INSERT INTO `prospects` '
. '(`id`,`first_name`,`last_name`,`address`,`city`,`state_province`,'
. '`postal_code`,`phone`,`country`,`email`,`status`,`budget`,`last_updated`) '
. ' VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)' ;
$statement = $connection -> pdo -> prepare ( $sql ) ;
然后我们使用 foreach()
来循环执行文件迭代器。每一条 yield
语句都会产生一个代表数据库中一行值的数组。然后,我们可以使用这些值与 PDOStatement::execute()
来执行准备好的语句,将该行的值插入到数据库中。
复制 foreach ($iterator as $row) {
echo implode ( ',' , $row ) . PHP_EOL ;
$statement -> execute ( $row ) ;
}
然后可以检查数据库,以验证数据是否已成功插入。