neat / database
Neat 数据库组件
Requires
- php: ^7.2 || ^8.0
- psr/log: ^1.0
Requires (Dev)
- ext-sqlite3: *
- phpunit/phpunit: ^8.5 || ^9.5
This package is auto-updated.
Last update: 2024-09-09 19:25:56 UTC
README
Neat 数据库组件提供了干净且表达性强的 API 来访问您的数据库。其核心是使用 PDO 实例的连接。
入门
要安装此软件包,只需在命令行中执行 composer
composer require neat/database
然后创建一个新的数据库连接
<?php // Connecting is easy, just pass a PDO instance $pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password'); $db = new Neat\Database\Connection($pdo);
查询
使用 query
和 select
方法从数据库获取信息很简单。它们都返回一个 Result
对象,允许您通过调用同名方法来获取 row(s)
和 value(s)
。
$db = new Neat\Database\Connection(new PDO('...')); // Get all users as associative arrays in an indexed array $users = $db->query('SELECT * FROM users')->rows(); // Get a single user row as an associative array $user = $db->query('SELECT * FROM users WHERE id = ?', 31)->row(); // Get the id of a user $id = $db->query('SELECT id FROM users WHERE username = ?', 'john')->value(); // Get an array of user names $names = $db->query('SELECT username FROM users')->values();
遍历结果
在大多数情况下,当您获取多行结果时,您会想逐行遍历结果。这看起来可能很简单,但有几种方法可以实现。
$db = new Neat\Database\Connection(new PDO('...')); // Fetching all rows before iterating over them can consume a lot of memory. foreach ($db->query('SELECT * FROM users')->rows() as $row) { var_dump($row); } // By calling the row() method repeatedly until you hit false, you store only // one row at a time in memory $result = $db->query('SELECT * FROM users'); while ($row = $result->row()) { var_dump($row); } // The same can be achieved by looping over the Result directly using foreach foreach ($db->query('SELECT * FROM users') as $row) { var_dump($row); }
获取的结果
要多次使用结果(例如,先计数其行数然后返回所有行),您需要先 fetch
结果。一个活着的 query
结果不允许您这样做
$db = new Neat\Database\Connection(new PDO('...')); // Get the fetched result first $result = $db->fetch('SELECT * FROM users'); $count = $result->count(); $users = $result->rows();
操作
因为非选择查询从不返回结果,所以使用相同的结果 API 也没有意义。相反,您可能会喜欢处理您查询影响的行数...这正是 execute
方法提供的。
$db = new Neat\Database\Connection(new PDO('...')); // Update a user (execute returns the number of rows affected) $rows = $db->execute('UPDATE users SET login_at = ?', new DateTime('now')); // Delete all inactive users $rows = $db->execute('DELETE FROM users WHERE active = 0'); // Insert a user and get the auto_increment id value $rows = $db->execute('INSERT INTO users (username, password) VALUES (?, ?)', 'john', password_hash('secret', PASSWORD_DEFAULT));
请注意,仅包含 2 个字段的插入操作已经变得相当长且难以阅读。因为这些数据操作查询通常填充了来自数组的数据,因此使用专门的 API 来完成此操作是有意义的。
进入 insert
、update
和 delete
方法。像 execute
方法一样,这些方法在未传递所有参数时也返回影响的行数。
$db = new Neat\Database\Connection(new PDO('...')); // Welcome John! We'll now turn you into a database record. if ($db->insert('users', ['username' => 'john', 'password' => '...'])) { $id = $db->insertedId(); } // Update John's last login $time = new DateTime('now'); $rows = $db->update('users', ['login_at' => $time], ['username' => 'john']); // Delete all users that haven't logged in for a year or more $rows = $db->delete('users', 'login_at < DATE_SUB(NOW(), INTERVAL 1 YEAR)');
请注意,插入方法不返回最后一个插入的 ID,而是您必须使用 insertedId
方法。
查询构建
为了在代码中编写查询,您可以使用查询构建器。它允许您逐步组合查询,而无需担心 SQL 语法和连接。
可以通过调用 build
或 select
方法获取查询构建器。通过链式调用方法,您可以交互式地构建查询,并通过结果 API 访问结果或使用类型转换或其 get* 方法将查询转换为字符串表示。
$db = new Neat\Database\Connection(new PDO('...')); // Look mama, without SQL! $users = $db->select()->from('users')->where('active = 1')->query()->rows(); // Or just get the SQL... this prints "SELECT * FROM users" echo $db->select()->from('users'); // Complex select statements are just as easy to build $db->select('g.*, COUNT(1) as active_users') ->from('users', 'u') ->leftJoin('users_groups', 'ug', 'u.id = ug.user_id') ->leftJoin('groups', 'g', 'g.id = ug.group_id') ->where('users.active = ?', 1) ->groupBy('g.id') ->having('COUNT(u.id) > 1') ->orderBy('g.name') ->limit(25) ->query() ->rows(); // Mixing the order of your calls can be useful too $query = $db->select('u.*') ->from('users', 'u') ->where('active = 1'); if (isset($searchGroup)) { $query->join('users_groups', 'ug', 'u.id = ug.user_id') ->join('groups', 'g', 'g.id = ug.group_id') ->where('g.name LIKE ?', "%$searchGroup%"); }
当您不传递所有参数时,insert
、update
和 delete
方法也返回查询构建器实例。
$db = new Neat\Database\Connection(new PDO('...')); // A simple insert query $db->insert('users') ->values(['username' => 'john', 'password' => '...']) ->execute(); // Or an update query $db->update('users') ->set(['login_at' => new DateTime('now')]) ->where(['username' => 'john']) ->execute();
转义和引用
当内置的查询构建器和占位符替换不再足够时,您很可能会最终连接自己的 SQL 查询。 quote
和 quoteIdentifier
方法允许您在您自己的 SQL 语句中安全地嵌入文字值和标识符。
$db = new Neat\Database\Connection(new PDO('...')); // First escape and quote the user input into an SQL safe string literal $quoted = $db->quote('%' . $_GET['search'] . '%'); $sql = "SELECT * FROM users WHERE lastname LIKE $quoted OR firstname LIKE $quoted"; // It also understands DateTime value objects $date = $db->quote(new DateTime('last monday')); $sql = "SELECT * FORM users WHERE login_at > $date"; // And NULL values (be sure to use the appropriate SQL syntax, eg IS instead of =) $null = $db->quote(null); // store 'NULL' into $null // Identifiers can also be quoted $table = $db->quoteIdentifier('users'); // store '`users`' (note the backticks) into $table $sql = "SELECT * FROM $table";
事务和锁定
如果您想在事务中运行一组数据库操作,可以使用事务方法并将操作作为闭包传递。当闭包返回时,事务将自动提交。但是,如果抛出异常,事务将自动回滚。
$db = new Neat\Database\Connection(new PDO('...')); // When the email could not be sent, rollback the transaction $db->transaction(function () use ($db) { $db->execute('UPDATE users SET active = 0 WHERE username = ?', 'john'); if (!mail('john@example.com', 'Confirmation', 'Account terminated')) { throw new \RuntimeException('E-mail failure, please rollback!'); } });