jsl/database

PHP 表达式数据库层 - 基于 Illuminate/Database

2.6.3 2023-01-11 16:44 UTC

README

数据库组件是一个与框架无关的 PHP 数据库抽象层,提供了一种表达式的查询构建器。它目前支持 MySQL、Postgres、SQL Server 和 SQLite。

功能

  • 简单的 CRUD 功能
  • 支持插入忽略/替换
  • 支持插入重复键更新
  • 支持直接 INSERT INTO ... SELECT * FROM 查询
  • 从 Traversable/Iterator 接口缓冲插入
  • 连接
  • 子查询
  • 嵌套查询
  • 批量插入
  • MySQL SELECT * INTO OUTFILE '...'
  • MySQL LOAD DATA INFILE '...'
  • 延迟连接
  • PSR 兼容日志记录
  • 数据库连接解析器

该组件基于 Laravel 的 Illuminate\Database,拥有非常熟悉的语法。核心查询构建器基本上是兼容的。主要的改动是对对象组合,以及连接工厂和连接解析器类中连接的创建和解析。

安装

composer require jsl/database

基本示例

首先,创建一个新的 "ConnectionFactory" 实例。

$factory = new \Database\Connectors\ConnectionFactory();

$connection = $factory->make(array(
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'username'  => 'root',
    'password'  => 'password',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',

    // Don't connect until we execute our first query
    'lazy'      => true,

    // Set PDO attributes after connection
    'options' => array(
        PDO::MYSQL_ATTR_LOCAL_INFILE    => true,
        PDO::ATTR_EMULATE_PREPARES      => true,
    )
));

$connection->query("SELECT id, username FROM customers");

文档

目录

连接

数据库组件支持 MySQL、SQLite、SqlServer 和 PostgreSQL 驱动。您可以在连接期间指定驱动程序,以及创建新连接时的相关配置。您还可以创建多个连接,但一次只能使用一个别名。

$factory = new \Database\Connectors\ConnectionFactory();

MySQL

$connection = $factory->make(array(
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'database', // Optional
    'username'  => 'root',
    'password'  => 'password',
    'charset'   => 'utf8mb4', // Optional: default value if omitted
    'collation' => 'utf8mb4_unicode_ci', // Optional: default value if omitted
));

$connection->fetchAll("SELECT id, username FROM customers"); 

$connection->table('customers')
	   ->find(12);
	   
$connection->table('customers')
	   ->join('products', 'customer.id', '=', 'customer_id')
	   ->where('favourites', '=', 1)
	   ->get();

SQLite

$connection = $factory->make(array(
    'driver'   => 'sqlite',
    'database' => '/path/to/sqlite.db',
    'create'   => true,
));

PDO 总是在数据库不存在时创建数据库。由于您不会收到任何提示,这可能会使调试变得非常困难。使用连接选项 create 您可以决定这种行为。将其设置为 true 以允许 PDO 创建数据库。将其设置为 false(默认值)将在数据库不存在时抛出异常。

默认连接选项

默认情况下,以下 PDO 属性将在连接上设置。您可以在连接配置的 options 数组参数中覆盖这些属性或添加它们。

PDO::ATTR_CASE              => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES  => false,

连接解析器

许多复杂的应用程序可能需要多个数据库连接。您可以在连接解析器内部创建一组命名连接,并在您的应用程序中通过名称引用它们。

$resolver = new Database\ConnectionResolver(array(
    'local' => array(
        'driver'    => 'mysql',
        'host'      => 'localhost',
        'username'  => 'root',
        'password'  => 'password',
        'charset'   => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
    ),
    'archive' => array(
        'driver'    => 'mysql',
        'host'      => '1.2.3.456',
        'username'  => 'root',
        'password'  => 'password',
        'charset'   => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
    ),
));

$dbLocal = $resolver->connection('local');

// Use it
$dbLocal->table('users')->get();


$dbArchive = $resolver->connection('archive');
// Etc...

如果您请求的连接是您之前在应用程序中使用的,连接解析器将返回相同的连接,而不是创建一个新的连接。

在创建解析器后,您可以设置默认连接,这样您就不必在整个应用程序中指定连接名称。

$resolver->setDefaultConnection('local');

// Returns the `local` connection
$resolver->connection();

原始查询

执行查询,带有绑定并返回 PDOStatement 对象

$statement = $connection->query('SELECT * FROM users WHERE name = ?', array('John Smith'));

// PDOStatement
$statement->rowCount();
$statement->fetchAll();

查询快捷键

$firstRow = $connection->fetch('SELECT * FROM users WHERE name = ?', array('John Smith'));

$allRows = $connection->fetchAll('SELECT * FROM users WHERE name = ?', array('John Smith'));

$firstColumnFirstRow = $connection->fetchOne('SELECT COUNT(*) FROM users WHERE name = ?', array('John Smith'));

查询构建器

选择

获取 PDOStatement

如果您打算遍历行,则获取 PDOStatement 可能更高效

$rows = $connection->table('users')->query();

获取所有

$rows = $connection->table('users')->get();

获取第一行

$row = $connection->table('users')->first();

按 ID 查找

$row = $connection->table('users')->find(6);

上述查询假设您的表的主键是 'id' 并且您想检索所有列。您可以指定要获取的列和主键

$connection->table('users')->find(3, array('user_id', 'name', 'email'), 'user_id');

选择列

$rows = $connection->table('users')->select('name')->addSelect('age', 'dob')->get();

限制和偏移

$connection->table('users')->offset(100)->limit(10);

Where

$connection->table('user')
    ->where('username', '=', 'jsmith')
    ->whereNotIn('age', array(10,20,30))
    ->orWhere('type', '=', 'admin')
    ->orWhereNot('name', 'LIKE', '%Smith%')
    ->get();
分组 Where
$connection->table('users')
            ->where('age', '>', 10)
            ->orWhere(function($subWhere)
                {
                    $subWhere
                        ->where('animal', '=', 'dog')
                        ->where('age', '>', 1)
                });

SELECT * FROM `users` WHERE `age` > 10 or (`age` > 1 and `animal` = 'dog')`.

分组 By、排序 By 和 Having

$users = $connection->table('users')
                    ->orderBy('name', 'desc')
                    ->groupBy('count')
                    ->having('count', '>', 100)
                    ->get();

连接

$connection->table('users')
    ->join('products', 'user_id', '=', 'users.id')
    ->get();
/*
    ->leftJoin()
    ->rightJoin()
*/
多个连接标准

如果您需要多个标准来连接一个表,则可以将闭包作为第二个参数传递。

->join('products', function($table)
    {
        $table->on('users.id', '=', 'products.user_id');
        $table->on('products.price', '>', 'users.max_price');
    })

子查询

$query = $connection->table('users')
            ->selectSub(function($subQuery){
            	$subQuery
            	->from('customer')
            	->select('name')
            	->where('id', '=', $subQuery->raw('users.id'));
            }, 'tmp');

这将生成如下查询

SELECT (SELECT `name` FROM `customer` WHERE `id` = users.id) as `tmp` FROM `users`

聚合

计数
$count = $connection->table('users')->count();
最小值
$count = $connection->table('users')->min('age');
最大值
$count = $connection->table('users')->max('age');
平均值
$count = $connection->table('users')->avg('age');
总和
$count = $connection->table('users')->sum('age');

MySQL Outfile

$connection
	->table('users')
	->select('*')
	->where('bar', '=', 'baz')
	->intoOutfile('filename', function(\Database\Query\OutfileClause $out){
		$out
		->enclosedBy(".")
		->escapedBy("\\")
		->linesTerminatedBy("\n\r")
		->fieldsTerminatedBy(',');
	})->query();

插入

$data = array(
    'username' = 'jsmith',
    'name' = 'John Smith'
);
$connection->table('users')->insert($data);
// Returns PDOStatement

`->insertGetId($data)` method returns the insert id instead of a PDOStatement

插入忽略

忽略具有重复唯一键插入的任何行的错误

$data = array(
    'username' = 'jsmith',
    'name' = 'John Smith'
);
$connection->table('users')->insertIgnore($data);

替换

替换匹配唯一键的现有行

$data = array(
    'username' = 'jsmith',
    'name' = 'John Smith'
);
$connection->table('users')->replace($data);

批量插入

查询构建器将智能处理多个插入行

$data = array(
	array(
	    'username' = 'jsmith',
	    'name' = 'John Smith'
	),
	array(
	    'username' = 'jbloggs',
	    'name' = 'Joe Bloggs'
	),
);
$connection->table('users')->insert($data);

您还可以将批量插入传递给 replace() 和 insertIgnore()

重复键更新

$data = array(
    'username' = 'jsmith',
    'name' = 'John Smith'
);

$now = $connection->raw('NOW()');

$connection->table('users')->insertUpdate(
    array('username' => 'jsmith', 'active' => $now), // Insert this data
    array('active' => $now)                          // Or partially update the row if it exists
);

//insertOnDuplicateKeyUpdate() is an alias of insertUpdate

插入选择

$connection->table('users')->insertSelect(function($select){ $select->from('admin') ->select('name', 'email') ->where('status', '=', 1);

}, array('name','email'));

insertIgnoreSelectreplaceSelect 方法支持 MySQL 语法驱动程序。

缓冲迭代器插入

如果您有一个大型数据集,可以以所选大小分批插入(支持插入忽略/替换/重复键更新)。

如果您想从一个服务器选择大量数据集并插入到另一个服务器,这将特别有用。

$pdoStatement = $mainServer->table('users')->query(); // Returns a PDOStatement (which implements the `Traversable` interface)

// Will be inserted in batches of 1000 as it reads from the rowset iterator.
$backupServer->table('users')->buffer(1000)->insertIgnore($pdoStatement);

更新

$data = array(
    'username' = 'jsmith123',
    'name' = 'John Smith'
);

$connection->table('users')->where('id', 123)->update($data);

删除

$connection->table('users')->where('last_active', '>', 12)->delete();

将删除所有 id 大于 5 的行。

原始表达式

使用 $connection->raw() 包装原始查询以绕过查询参数绑定。注意:谨慎使用 - 不会进行清理。

$connection->table('users')
            ->select($connection->raw('DATE(activity_time) as activity_date'))
            ->where('user', '=', 123)
            ->get();

获取 SQL 查询和绑定

$query = $connection->table('users')->find(1)->toSql();
$query->toSql();
// SELECT * FROM users where `id` = ?

$query->getBindings();
// array(1)

原始 PDO 实例

$connection->getPdo();

致谢

这是 mrjgreen/database 的一个分支。