dg / dibi
Requires
- php: 8.0 - 8.4
Requires (Dev)
- jetbrains/phpstorm-attributes: ^1.0
- nette/di: ^3.1
- nette/tester: ^2.5
- phpstan/phpstan: ^1.0
- tracy/tracy: ^2.9
Replaces
- dg/dibi: *
- dev-master / 6.0.x-dev
- v5.0.x-dev
- v5.0.2
- v5.0.1
- v5.0.0
- v4.2.x-dev
- v4.2.8
- v4.2.7
- v4.2.6
- v4.2.5
- v4.2.3
- v4.2.2
- v4.2.1
- v4.2.0
- v4.1.x-dev
- v4.1.5
- v4.1.4
- v4.1.3
- v4.1.2
- v4.1.1
- v4.1.0
- v4.0.x-dev
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.2.x-dev
- v3.2.4
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.x-dev
- v3.1.1
- v3.1.0
- v3.0.x-dev
- v3.0.9
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.3.x-dev
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.x-dev
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- 2.1.x-dev
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- 2.0.x-dev
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- dev-translator-alt
- dev-revert-363-phpstan
- dev-fetchsingle
This package is auto-updated.
Last update: 2024-09-03 16:54:16 UTC
README
简介
PHP 的数据库访问函数没有标准化。此库隐藏了它们之间的差异,最重要的是,它为您提供了一个非常便捷的接口。
支持我
你喜欢 Dibi 吗?你在期待新功能吗?
谢谢!
安装
通过 Composer 安装 Dibi
composer require dibi/dibi
Dibi 5.0 需要 PHP 版本 8.2,并支持 PHP 8.4。
用法
请参阅 examples
目录中的示例。Dibi 文档可在 主页 上找到。
连接到数据库
数据库连接由对象 Dibi\Connection
表示
$database = new Dibi\Connection([ 'driver' => 'mysqli', 'host' => 'localhost', 'username' => 'root', 'password' => '***', 'database' => 'table', ]); $result = $database->query('SELECT * FROM users');
或者,您可以使用 dibi
静态注册,它在全局可用的存储中维护一个连接对象,并调用所有上面的函数
dibi::connect([ 'driver' => 'mysqli', 'host' => 'localhost', 'username' => 'root', 'password' => '***', 'database' => 'test', 'charset' => 'utf8', ]); $result = dibi::query('SELECT * FROM users');
如果发生连接错误,它将抛出 Dibi\Exception
。
查询
我们通过方法 query()
查询数据库,它返回 Dibi\Result
。行是对象 Dibi\Row
。
您可以在 在线游乐场 中尝试所有示例。
$result = $database->query('SELECT * FROM users'); foreach ($result as $row) { echo $row->id; echo $row->name; } // array of all rows $all = $result->fetchAll(); // array of all rows, key is 'id' $all = $result->fetchAssoc('id'); // associative pairs id => name $pairs = $result->fetchPairs('id', 'name'); // the number of rows of the result, if known, or number of affected rows $count = $result->getRowCount();
方法 fetchAssoc() 可以返回更复杂的关联数组。
您可以将参数轻松添加到查询中,注意问号
$result = $database->query('SELECT * FROM users WHERE name = ? AND active = ?', $name, $active); // or $result = $database->query('SELECT * FROM users WHERE name = ?', $name, 'AND active = ?', $active);); $ids = [10, 20, 30]; $result = $database->query('SELECT * FROM users WHERE id IN (?)', $ids);
警告:永远不要将参数连接到 SQL。这将创建一个 SQL 注入 漏洞。
$result = $database->query('SELECT * FROM users WHERE id = ' . $id); // BAD!!!
可以使用所谓的修饰符代替问号
$result = $database->query('SELECT * FROM users WHERE name = %s', $name);
如果失败,query()
抛出 Dibi\Exception
或其子类
ConstraintViolationException
- 违反表约束ForeignKeyConstraintViolationException
- 无效的外键NotNullConstraintViolationException
- 违反 NOT NULL 条件UniqueConstraintViolationException
- 碰撞唯一索引
您也可以使用快捷方式
// returns associative pairs id => name, shortcut for query(...)->fetchPairs() $pairs = $database->fetchPairs('SELECT id, name FROM users'); // returns array of all rows, shortcut for query(...)->fetchAll() $rows = $database->fetchAll('SELECT * FROM users'); // returns row, shortcut for query(...)->fetch() $row = $database->fetch('SELECT * FROM users WHERE id = ?', $id); // returns field, shortcut for query(...)->fetchSingle() $name = $database->fetchSingle('SELECT name FROM users WHERE id = ?', $id);
修饰符
除了 ?
通配符字符外,我们还可以使用修饰符
示例
$result = $database->query('SELECT * FROM users WHERE name = %s', $name);
如果 $name 为 null,则 NULL 将插入到 SQL 语句中。
如果变量是数组,修饰符将应用于其所有元素,并将它们以逗号分隔的形式插入 SQL 中
$ids = [10, '20', 30]; $result = $database->query('SELECT * FROM users WHERE id IN (%i)', $ids); // SELECT * FROM users WHERE id IN (10, 20, 30)
当表名或列名是变量时,使用修饰符 %n
。 (注意,不允许用户操作此类变量的内容)
$table = 'blog.users'; $column = 'name'; $result = $database->query('SELECT * FROM %n WHERE %n = ?', $table, $column, $value); // SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
对于 LIKE,有三种特殊修饰符可用
搜索以字符串开头的名称
$result = $database->query('SELECT * FROM table WHERE name LIKE %like~', $query);
数组修饰符
SQL 查询中输入的参数也可以是数组。这些修饰符确定如何编译 SQL 语句
示例
$arr = [ 'a' => 'hello', 'b' => true, ]; $database->query('INSERT INTO table %v', $arr); // INSERT INTO `table` (`a`, `b`) VALUES ('hello', 1) $database->query('UPDATE `table` SET %a', $arr); // UPDATE `table` SET `a`='hello', `b`=1
在 WHERE 子句修饰符中,可以使用 %and
或 %or
$result = $database->query('SELECT * FROM users WHERE %and', [ 'name' => $name, 'year' => $year, ]); // SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
修饰符 %by
用于排序,键显示列,布尔值将确定是否按升序排序
$result = $database->query('SELECT id FROM author ORDER BY %by', [ 'id' => true, // ascending 'name' => false, // descending ]); // SELECT id FROM author ORDER BY `id`, `name` DESC
插入、更新 & 删除
我们将数据作为关联数组插入到 SQL 查询中。在这些情况下不需要修饰符和通配符 ?
。
$database->query('INSERT INTO users', [ 'name' => $name, 'year' => $year, ]); // INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978) $id = $database->getInsertId(); // returns the auto-increment of the inserted record $id = $database->getInsertId($sequence); // or sequence value
多重插入
$database->query('INSERT INTO users', [ 'name' => 'Jim', 'year' => 1978, ], [ 'name' => 'Jack', 'year' => 1987, ]); // INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
删除
$database->query('DELETE FROM users WHERE id = ?', $id); // returns the number of deleted rows $affectedRows = $database->getAffectedRows();
更新
$database->query('UPDATE users SET', [ 'name' => $name, 'year' => $year, ], 'WHERE id = ?', $id); // UPDATE users SET `name` = 'Jim', `year` = 1978 WHERE id = 123 // returns the number of updated rows $affectedRows = $database->getAffectedRows();
插入条目或如果已存在则更新
$database->query('INSERT INTO users', [ 'id' => $id, 'name' => $name, 'year' => $year, ], 'ON DUPLICATE KEY UPDATE %a', [ // here the modifier %a must be used 'name' => $name, 'year' => $year, ]); // INSERT INTO users (`id`, `name`, `year`) VALUES (123, 'Jim', 1978) // ON DUPLICATE KEY UPDATE `name` = 'Jim', `year` = 1978
事务
处理事务有三个方法
$database->begin(); $database->commit(); $database->rollback();
测试
为了和Dibi玩耍一下,有一个test()
方法,你可以传递像query()
这样的参数,但它不是执行SQL语句,而是将它们显示在屏幕上。
可以使用$result->dump()
将查询结果以表格的形式输出。
这些变量也可以使用
dibi::$sql; // the latest SQL query dibi::$elapsedTime; // its duration in sec dibi::$numOfQueries; dibi::$totalTime;
复杂查询
参数也可以是一个DateTime
对象。
$result = $database->query('SELECT * FROM users WHERE created < ?', new DateTime); $database->query('INSERT INTO users', [ 'created' => new DateTime, ]);
或者SQL文。
$database->query('UPDATE table SET', [ 'date' => $database->literal('NOW()'), ]); // UPDATE table SET `date` = NOW()
或者一个表达式,你可以使用?
或修饰符。
$database->query('UPDATE `table` SET', [ 'title' => $database::expression('SHA1(?)', 'secret'), ]); // UPDATE `table` SET `title` = SHA1('secret')
在更新时,修饰符可以直接放在键中。
$database->query('UPDATE table SET', [ 'date%SQL' => 'NOW()', // %SQL means SQL ;) ]); // UPDATE table SET `date` = NOW()
在条件(例如,对于%and
和%or
修饰符)中,没有必要指定键。
$result = $database->query('SELECT * FROM `table` WHERE %and', [ 'number > 10', 'number < 100', ]); // SELECT * FROM `table` WHERE (number > 10) AND (number < 100)
修饰符或通配符也可以用在表达式中。
$result = $database->query('SELECT * FROM `table` WHERE %and', [ ['number > ?', 10], // or $database::expression('number > ?', 10) ['number < ?', 100], ['%or', [ 'left' => 1, 'top' => 2, ]], ]); // SELECT * FROM `table` WHERE (number > 10) AND (number < 100) AND (`left` = 1 OR `top` = 2)
%ex
修饰符将数组的所有项插入SQL。
$result = $database->query('SELECT * FROM `table` WHERE %ex', [ $database::expression('left = ?', 1), 'AND', 'top IS NULL', ]); // SELECT * FROM `table` WHERE left = 1 AND top IS NULL
SQL中的条件
条件SQL命令由三个修饰符控制%if
、%else
和%end
。%if
必须位于表示SQL的字符串的末尾,并跟随着变量。
$user = ??? $result = $database->query(' SELECT * FROM table %if', isset($user), 'WHERE user=%s', $user, '%end ORDER BY name ');
可以使用%else
部分补充条件。
$result = $database->query(' SELECT * FROM %if', $cond, 'one_table %else second_table ');
条件可以嵌套。
SQL中的标识符和字符串
SQL本身需要经过处理以符合数据库的约定。标识符(表和列的名称)可以输入到方括号或反引号中,字符串用单引号或双引号引用,但服务器总是发送数据库所请求的内容。例如
$database->query("UPDATE `table` SET [status]='I''m fine'"); // MySQL: UPDATE `table` SET `status`='I\'m fine' // ODBC: UPDATE [table] SET [status]='I''m fine'
SQL字符串中的引号内部需要重复。
结果作为关联数组
示例:以关联字段返回结果,其中键将是id
字段的值。
$assoc = $result->fetchAssoc('id');
fetchAssoc()
的最大功能体现在执行多个表的SQL查询,这些表具有不同类型的连接。数据库将创建一个扁平表,fetchAssoc将返回其形状。
示例:以客户和订单表(N:M关联)为例进行查询
$result = $database->query(' SELECT customer_id, customers.name, order_id, orders.number, ... FROM customers INNER JOIN orders USING (customer_id) WHERE ... ');
我们希望通过客户ID和订单ID获取嵌套的关联数组
$all = $result->fetchAssoc('customer_id|order_id'); // we will iterate like this: foreach ($all as $customerId => $orders) { foreach ($orders as $orderId => $order) { ... } }
关联描述符的语法与通过将数组赋值给PHP时的语法类似。因此,'customer_id|order_id'
表示为所有行依次执行赋值序列$all[$customerId][$orderId] = $row;
。
有时可能会根据客户的名称而不是ID进行关联很有用。
$all = $result->fetchAssoc('name|order_id'); // the elements then proceeds like this: $order = $all['Arnold Rimmer'][$orderId];
但如果有多位客户有相同的名称怎么办?表应该是这样的形式
$row = $all['Arnold Rimmer'][0][$orderId]; $row = $all['Arnold Rimmer'][1][$orderId]; ...
因此,我们可以使用数组区分多个可能的Rimmers。关联描述符的格式与赋值类似,其中的序列数组表示[]
。
$all = $result->fetchAssoc('name[]order_id'); // we get all the Arnolds in the results foreach ($all['Arnold Rimmer'] as $arnoldOrders) { foreach ($arnoldOrders as $orderId => $order) { ... } }
回到customer_id|order_id
描述符的示例,我们将尝试列出每位客户的订单。
$all = $result->fetchAssoc('customer_id|order_id'); foreach ($all as $customerId => $orders) { echo "Customer $customerId": foreach ($orders as $orderId => $order) { echo "ID number: $order->number"; // customer name is in $order->name } }
同时输出客户名称也会很方便。但我们需要在$orders
数组中查找它。所以,让我们调整结果形状
$all[$customerId]->name = 'John Doe'; $all[$customerId]->order_id[$orderId] = $row; $all[$customerId]->order_id[$orderId2] = $row2;
因此,在$clientId
和$orderId
之间,我们将插入一个中间项。这次不是我们用来区分单个Rimmers的编号索引,而是一个数据库行。解决方案非常相似,只需记住行符号表示箭头
$all = $result->fetchAssoc('customer_id->order_id'); foreach ($all as $customerId => $row) { echo "Customer $row->name": foreach ($row->order_id as $orderId => $order) { echo "ID number: $order->number"; } }
前缀和替换
表和列名可以包含可变部分。你首先定义
// create new substitution :blog: ==> wp_ $database->substitute('blog', 'wp_');
然后在SQL中使用它。注意,在SQL中它们用冒号引用。
$database->query("UPDATE [:blog:items] SET [text]='Hello World'"); // UPDATE `wp_items` SET `text`='Hello World'
字段数据类型
Dibi自动检测查询列的类型,并将字段转换为原生类型。您也可以手动指定类型,您可以在Dibi\Type
类中找到可能的数据类型。
$result->setType('id', Dibi\Type::Integer); // id will be integer $row = $result->fetch(); is_int($row->id) // true
日志记录器
Dibi内置了一个日志记录器,可以跟踪所有执行的SQL语句并测量它们的持续时间。激活日志记录器
$database->connect([ 'driver' => 'sqlite', 'database' => 'sample.sdb', 'profiler' => [ 'file' => 'file.log', ], ]);
一个更通用的分析器是当连接到Nette时激活的Tracy面板。
连接到Nette
在配置文件中,我们将注册DI扩展,并为创建所需的对象以及Tracy调试器栏中的数据库面板添加dibi
部分。
extensions: dibi: Dibi\Bridges\Nette\DibiExtension3 dibi: host: localhost username: root password: *** database: foo lazy: true
然后,连接对象可以从容器DI中以服务的形式获得,例如:
class Model { private $database; public function __construct(Dibi\Connection $database) { $this->database = $database; } }