impressive / orm
基于Active Record的Impressive ORM。
Requires
- php: >=5.6.0
This package is not auto-updated.
Last update: 2024-09-23 07:07:30 UTC
README
与数据库交互所需的基本功能
基于Active Record的Impressive ORM。
理念
在软件开发中,我遵循的原则是:一切都很简单,并始终寻找解决任务的愚蠢选项。Impressive ORM也不例外。它极其简单。在其他ORM由数十个具有复杂继承层次的类组成时,Impressive ORM只有几个可以数得过来的类。在我看来,这已经足够用于许多实际程序。要现实:我们中的大多数人都不会创建Google、Instagram或Facebook。我们在小型、中型项目中工作,注重简单性和快速开发,而不是无尽的灵活性和功能。
安装
目前,此库可通过gitlab.otakoyi.com访问。将来它将被移动到github并在Packagist上托管。
设置和配置
首先,要求vendor/autoload.php
然后创建Connection实例并设置它。这是PDO连接到您的数据库所用的。有关更多信息,请参阅PDO文档。
<?php
$con = new \Impressive\ORM\Connection('mysql:host=localhost;dbname=orm');
$con->set('username', 'root');
$con->set('password', '****');
$con->set('driver_options', [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"]);
// return instance of Collection
$con->set('return_collections', true);
// enable log for debugging
$con->set('logging', true);
// add connection
\Impressive\ORM\DB::addConnection($con);
您可以在配置中设置多个PDO选项。
例如:$con->set('some_key', 'some value');
您可以添加多个连接。
示例
<?php
$con = new \Impressive\ORM\Connection('mysql:host=localhost;dbname=master');
$con->set('username', 'user');
$con->set('password', '****');
$con->set('driver_options', [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"]);
// return instance of Collection
$con->set('return_collections', true);
// enable log for debugging
$con->set('logging', true);
// add connection
\Impressive\ORM\DB::addConnection($con);
$con = new \Impressive\ORM\Connection('mysql:host=localhost;dbname=slave');
$con->set('username', 'user');
$con->set('password', '****');
$con->set('driver_options', [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"]);
// return instance of Collection
$con->set('return_collections', true);
// enable log for debugging
$con->set('logging', true);
// add connection
\Impressive\ORM\DB::addConnection($con, 'slave');
集合
$con->set('return_collections', true);
结果集合可以作为数组(默认)或集合返回。
建议您将项目配置为使用集合,因为它们更具灵活性。
PDO驱动程序选项
如您所见,您可以设置驱动程序选项。因为某些数据库允许设置选项。有关更多信息,请参阅PDO文档。
例如,强制MySQL驱动程序使用UTF-8进行连接
$con->set('driver_options', [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"]);
PDO错误模式
选项:error_mode
这可以用于设置Impressive ORM使用的数据库连接类上的PDO::ATTR_ERRMODE设置。它应传递PDO定义的一个类常量。
例如
$con->set('error_mode', PDO::ERRMODE_WARNING);
默认设置是PDO::ERRMODE_EXCEPTION
。有关可用的错误模式详情,请参阅PDO设置属性文档。
PDO对象访问
任何方式执行语句后,如::save()
或::rawExecute()
,可访问使用的PDOStatement实例通过DB::getLastStatement()
。这可能非常有用,以便访问PDOStatement::errorCode()
,如果关闭了PDO异常,或访问返回基于底层数据库的不同结果的PDOStatement::rowCount()
方法。有关更多信息,请参阅PDOStatement文档。
标识符引号字符
选项:identifier_quote_character
设置用于引用标识符(例如表名、列名)的字符。如果没有设置,它将根据PDO使用的数据库驱动程序自动检测。
列ID
默认情况下,ORM假定您的所有表都有一个名为id的主键列。有两种方法可以覆盖此设置:针对数据库中的所有表,或针对每个表。
选项:pk
此设置用于配置所有表的主键列名称。如果您的列id名为primary_key,则使用
$con->set('pk', 'primary_key');
选项:column_overrides
此设置用于指定每个表的主键列名称。它接受一个关联数组,将表名映射到列名。例如,如果您的列名包含表名,则可以使用以下配置
`<?php $con->set('column_overrides', [
'user' => 'user_id',
'group' => 'group_id',
]); `
限制子句样式
选项:limit_clause_style
您可以在配置中指定限制子句的样式。这是为了便于使用 MS SQL 风格的限制子句,该子句使用 TOP 语法。
可接受的值有 DB::LIMIT_STYLE_TOP_N
和 DB::LIMIT_STYLE_LIMIT
。
如果您正在使用的 PDO 驱动程序是 sqlsrv、dblib 或 mssql 之一,则 ORM 将自动为您选择 DB::LIMIT_STYLE_TOP_N,除非您覆盖了此设置。
查询日志
选项:logging
ORM 可以记录它执行的查询。要启用查询日志,将日志选项设置为 true(默认为 false)。
当启用查询日志时,您可以使用两个静态方法来访问日志。DB::getLastQuery() 返回最近执行的查询。DB::getQueryLog() 返回所有已执行的查询数组。这对于调试非常有用。
查询记录器
选项:logger
> 您必须启用 logging
才能对此设置产生影响。
您可以为此配置设置提供一个可调用的函数,该函数将在 ORM 执行的每个查询上执行。在 PHP 中,可调用的函数可以是任何可以像函数一样执行的东西。最常见的形式是匿名函数。
此设置非常有用,如果您希望使用外部库记录查询,因为它允许您在回调函数内部执行任何您想要的操作。
`$con->set('logger', function($log_string, $query_time) {
d($log_string . ' in ' . $query_time);
}); `
查询缓存
选项:caching
ORM 可以在请求期间缓存它执行的查询。要启用查询缓存,将缓存选项设置为 true(默认为 false)。
$con->set('caching', true);
查询构建器
Impressive ORM 提供了一个 流畅的接口,以便在不编写任何 SQL 代码的情况下构建简单的查询。
所有 Impressive ORM 查询都是从 DB 类的表静态方法调用开始的。这告诉 DB 在执行查询时要使用哪个表。
请注意,此方法 **不会对其查询参数进行转义,因此表名不应直接从用户输入中传递**。
然后,将添加过滤器和约束到您的查询的方法调用被连接起来。最后,通过调用 findOne() 或 find() 来结束链,这执行查询并返回结果。
让我们从简单的示例开始。
假设我们有一个名为 users 的表。这里是一个表结构
CREATE TABLE users ( id int(10) UNSIGNED NOT NULL, name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, surname varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, ses_id varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, email varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, password varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, status enum('active','blocked','deleted','hold') COLLATE utf8mb4_unicode_ci NOT NULL, created_at timestamp NULL DEFAULT NULL, updated_at timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
以及一些用于调试的 PHP 辅助函数
function d(... $args)
{
echo '<pre>'; var_dump(... $args); echo '</pre>';
}
function dd(... $args)
{
d(... $args); die;
}
单个用户
$person = \Impressive\ORM\DB::table('users')->where('name', 'Oleh')->orderByDesc('name')->findOne(); d($person);
获取所有用户
$users = \Impressive\ORM\DB::table('users')->whereEqual('name' , 'Oleh')->orderByDesc('name')->select('id')->selectAs("CONCAT(name ,' ', surname ) as username")->find();
d(\Impressive\ORM\DB::getLastQuery());
echo "<ul>";
foreach ($users as $user) {
// d($user);
echo "<li># $user->id $user->name</li>";
}
echo "</ul>";
通过 ID 查找用户
$person = \Impressive\ORM\DB::table('users')->findOne(11);
d(\Impressive\ORM\DB::getLastQuery());
d($person);
通过参数搜索用户并更新
$users = \Impressive\ORM\DB::table('users')->limit(10)->offset(11)->where(['role'=>'developer', 'status' => 'active'])->find();
d(\Impressive\ORM\DB::getLastQuery());
foreach ($users as $user) {
$user->ses_id = '999';
$user->save();
}
以数组形式获取结果
$users = \Impressive\ORM\DB::table('users')->limit(10)->findArray();
dd($users);
获取集合
$users = \Impressive\ORM\DB::table('users')->limit(10)->findCollection();
dd($users);
使用限制
$users = \Impressive\ORM\DB::table('users')->whereNotLike('name', 'Maks')->limit(10)->findCollection();
dd($users);
$users = \Impressive\ORM\DB::table('users')->whereIdIs(15)->limit(10)->find();
dd($users);
$users = \Impressive\ORM\DB::table('users')->whereIn('id',[1,2,3,6])->limit(10)->find();
dd($users);
$users = \Impressive\ORM\DB::table('users')->select('id','name')->distinct()->find();
dd($users);
$users = \Impressive\ORM\DB::table('users')
->join('user_assoc', "users.id=user_assoc.user_id")
->select('users.id','users.name', 'user_assoc.network')
->distinct()
->limit(3)
->find();
dd($users);
计数结果
$users_count = DB::table('person')->count();
过滤结果
Impressive ORM 提供了一系列方法,以提取满足某些条件或条件的记录。这些方法可以多次调用以构建您的查询,而 DB 流畅的接口允许将方法调用链接起来,以创建可读性和简单性强的查询。
注意事项
当使用 Impressive ORM 时,仅支持 SQL 支持条件的一个子集。此外,在执行查询时,所有 WHERE 子句都将进行 AND 操作。目前不支持 OR WHERE 子句。
这些限制是故意的:这些是目前使用最广泛的准则,通过避免对非常复杂的查询的支持,Impressive ORM代码库可以保持小而简单。
通过whereRaw和rawQuery方法提供了一些对更复杂条件和查询的支持(见下文)。如果你发现自己经常需要比ORM提供的功能更多的功能,可能考虑使用功能更全面的ORM。
### 等于:where, whereEqual, whereNotEqual
默认情况下,调用where使用两个参数(列名和值)将它们使用等于运算符(=)组合起来。例如,调用where('name', 'Taras')将生成WHERE name = "Taras"子句。
如果你的编码风格更注重清晰度而非简洁性,你可能更喜欢使用whereEqual方法:它与where相同。
whereNotEqual方法在查询中添加WHERE column != "value"子句。
您可以在同一调用中指定多个列及其值。在这种情况下,您应该将关联数组作为第一个参数传递。数组表示法使用键作为列名。
$users = DB::table('users')
->where(array(
'name' => 'Taras',
'email' => 'some@emai.com'
))
->find();
快捷方式:whereIdIs
这是一个简单的辅助方法,用于通过主键查询表。尊重在配置中指定的ID列。如果你正在使用复合主键,你必须传递一个数组,其中键是列名。不属于键的列将被忽略。
快捷方式:whereIdIn
这个辅助方法与whereIdIs
类似,但它期望选择一个主键数组。它是复合主键感知的。
小于/大于:whereLt, whereGt, whereLte, whereGte 有四个方法可用于不等式
- 小于:
$users = DB::table('users')->whereLt('age', 10)->find_many();
- 大于:
$users = DB::table('users')->whereGt('age', 5)->find_many();
- 小于或等于:
$users = DB::table('users')->whereLte('age', 10)->find_many();
- 大于或等于:
$users = DB::table('users')->whereGte('age', 5)->find_many();
字符串比较:whereLike
和whereNotLike
要添加WHERE ... LIKE子句,使用
$users = DB::table('users')->whereLike('name', '%Tar%')->find();
同样,要添加WHERE ... NOT LIKE子句,使用
$users = DB::table('users')->whereNotLike('name', '%Tar%')->find();
多个OR条件
您可以使用whereAnyIs向同一WHERE子句中添加简单的OR条件。您应该使用项目数组指定多个条件。每个项目都将是一个包含多个条件的关联数组。
$users = DB::table('users')
->whereAnyIs(
[
['name' => 'Taras', 'age' => 10],
['name' => 'Vitalik', 'age' => 20],
['name' => 'Vlad', 'age' => 12]
]
)
->find();
将构建
SELECT * FROM
users WHERE ((
name = 'Taras' AND
age = '10' ) OR (
name = 'Vitalik' AND
age > '20' ) OR (
name = 'Vlad' AND
age > '12' ));
集合成员:whereIn
和whereNotIn
要添加WHERE ... IN ()或WHERE ... NOT IN ()子句,分别使用whereIn和whereNotIn方法。
这两种方法都接受两个参数。第一个是要比较的列名。第二个是可能值的数组。像所有where...方法一样,您可以使用关联数组指定多个列,只需将其作为唯一第一个参数传递。
$users = DB::table('users')->whereIn('name', array('A', 'B', 'C'))->find();
处理NULL值:whereNull
和whereNotNull
要添加WHERE column IS NULL或WHERE column IS NOT NULL子句,分别使用whereNull和whereNotNull方法。这两种方法都接受单个参数:要测试的列名。
原始WHERE子句
如果您需要一个更复杂的查询,可以使用 whereRaw
方法来精确指定 WHERE 子句的 SQL 片段。此方法接受两个参数:要添加到查询中的字符串,以及一个(可选的)参数数组,这些参数将被绑定到字符串上。如果提供了参数,字符串应包含问号字符 (?) 来表示要绑定的值,并且参数数组应包含按正确顺序替换到字符串中的值。
此方法可以在方法链中使用,与其他 where 方法以及 offset、limit 和 orderBy 等方法一起使用。您提供的字符串内容将与前面和后面的 WHERE 子句通过 AND 连接。
`$users = DB::table('users')
->where('name', 'Alex')
->where_raw('(age = ? OR age = ?)', array(20, 25))
->orderByAsc('name')
->find();`
// 将创建如下 SQL: SELECT * FROM users WHERE name = "Alex" AND (age = 20 OR age = 25) ORDER BY name ASC;
当使用 ALL、ANY、BETWEEN、IN、LIKE、OR 和 SOME 中的任何一个时,必须将您的表达式括在括号中。否则,AND 的优先级将更强,在上面的示例中,您实际上会得到
WHERE (name = "Alex" AND age = 20) OR age = 25
请注意,此方法仅支持“问号占位符”语法,而不是“命名占位符”语法。这是因为 PDO 不允许包含混合占位符类型的查询。此外,您应确保字符串中的问号占位符数量与数组中的元素数量完全匹配。
如果您需要更多的灵活性,可以手动指定整个查询。请参阅下面的原始查询。
限制和偏移量
请注意,这些方法 **不转义它们的查询参数,因此不应直接从用户输入传递。
limit
和 offset
方法与它们的 SQL 等价物非常相似。
$users = DB::table('users')->where('role', 'admin')->limit(5)->offset(10)->find();
排序
请注意,这些方法 **不转义它们的查询参数,因此不应直接从用户输入传递。
提供了两种方法来向查询添加 ORDER BY 子句。这些是 orderByDesc
、orderByAsc
、orderByRaw
和 orderBy
,每个都接受一个要排序的列名。列名将被引用。
$users = DB::table('users')->where('role', 'admin')->orderByAsc('name')->limit(5)->offset(10)->find();
分组
请注意,此方法 **不转义它的查询参数,因此不应直接从用户输入传递。
要添加 GROUP BY 子句到您的查询,调用 groupBy 方法,传入列名。您可以多次调用此方法以添加更多列。
$users = DB::table('users')->where('role', 'admin')->groupBy('name')->limit(5)->offset(10)->find();
也可以按数据库表达式 GROUP BY
$users = DB::table('users')->where('role', 'admin')->groupByRaw('raw expression')->limit(5)->offset(10)->find();
有
当与 GROUP BY 结合使用聚合函数时,可以使用 HAVING 来基于这些值进行过滤。
HAVING 与 DB 中的所有 where 函数完全相同。将 where 替换为 having* 以使用这些函数。
$users = DB::table('users')->where('role', 'admin')->havingNotLike('name', 'Petro')->limit(5)->offset(10)->find();
您还可以使用 havingEqual
、havingNotEqual
、havingIdIs
、havingLike
、havingGt
、havingLt
、havingGte
、havingLte
、havingIn
、havingNotIn
、havingNull
、havingNotNull
、havingRaw
结果列
默认情况下,查询将从您的查询返回 SELECT 语句中的所有列。也就是说,调用
$users = DB::table('users')->find();
将返回 SELECT * FROM users
;
要选择指定列,请使用 select() 方法。您可以在 select 中放入多个参数。例如
将用户表中的id和name字段选出来并查找,代码如下:
返回结果:SELECT id, name FROM users;
如果您想为列添加别名,可以使用以下方法:
将计数作为别名,代码如下:
返回结果:SELECT COUNT(*) AS count FROM users;
DISTINCT
要在查询结果列之前添加DISTINCT关键字,请将distinct()调用添加到查询链中。
查询具有特定角色的用户,代码如下:
连接
ORM支持多种类型的JOIN,可以在查询中添加不同类型的JOIN。
方法:join, innerJoin, leftOuterJoin, rightOuterJoin, fullOuterJoin。
这些方法接受相同的参数集。以下描述将使用基本的join方法作为示例,但同样适用于每个方法。
前两个参数是必需的。第一个参数是要连接的表名,第二个参数是连接条件。推荐的方法是将条件指定为一个包含三个元素的数组:第一个列,操作符,第二个列。表和列名将被自动引号包围。
例如
查询示例:
也可以将条件指定为字符串,它将被原样插入到查询中。但是,在这种情况下,列名不会被转义,因此应谨慎使用此方法。
连接方法还接受一个可选的第三个参数,它是查询中表的别名。如果希望将表连接到自身以创建层次结构,则此选项很有用。在这种情况下,最好与table_alias方法结合使用,该方法将为与ORM关联的表添加别名,并使用select方法来控制返回哪些列。
`$results = DB::table('users')
->tableAlias('u1')
->select('u1.*')
->select('u2.name', 'parent_name')
->join('users', array('u1.parent', '=', 'u2.id'), 'u2')
->find();`
原始JOIN子句
如果需要构建更复杂的查询,可以使用rawJoin方法来指定JOIN子句的SQL片段。此方法需要四个必需的参数:要添加到查询中的字符串,条件数组(包含三个元素:第一列,操作符,第二列),表别名以及可选的参数数组。如果提供了参数,则字符串中应包含代表要绑定值的问号字符(?),参数数组应包含应按正确顺序替换到字符串中的值。
如果您需要更多的灵活性,可以手动指定整个查询。请参阅下面的原始查询。
聚合函数
除了前面记录的COUNT之外,还支持MIN、AVG、MAX和SUM。
要返回列的最小值,请调用min()方法。
查询示例:
其他函数(AVG、MAX和SUM)的工作方式完全相同。提供要执行聚合函数的列名,它将返回一个整数。
原始查询
如果需要执行更复杂的查询,可以使用rawQuery方法完全指定要执行的查询。此方法需要一个字符串,可选地是一个参数数组。字符串可以包含占位符,无论是问号还是命名占位符语法,它将被用于将参数绑定到查询。
查询示例:
返回的DB类实例将包含查询返回的所有列的数据。请注意,您仍然必须调用for_table将实例绑定到特定表,即使查询中可以指定不同的表。这是因为如果您稍后要调用save,数据库需要知道更新哪个表。
使用原查询(rawQuery)是高级的,也可能很危险,数据库(DB)在您使用此方法时不会尝试保护您免犯错误。
使用PDO执行原始SQL
使用此函数,您将直接进入PHP的PDO。强大的ORM在您使用此方法时不会尝试保护您免犯错误。
在实现rawExecute()时,您实际上只是在管理数据库的连接和配置。
在某些情况下,使用ORM底层的PDO实例进行高级查询可能很有用。这些可能包括从数据库中删除ORM不支持且未来也不会支持的表。
此方法直接映射到PDOStatement::execute(),请熟悉其文档。
删除表
可以使用rawExecute()非常简单地完成此操作。
DB::rawExecute('DROP TABLE users')
获取PDO实例
>使用此函数,您将直接进入PHP的PDO。数据库在您使用此方法时不会尝试保护您免犯错误。在实现getDb()
时,您实际上只是在管理数据库的连接和配置。
`$pdo = DB::getDb(); foreach($pdo->query('SHOW TABLES') as $row) {
var_dump($row);
}`
对象和CRUD
从对象获取数据
一旦从查询中返回了一组记录(对象),您可以通过两种方式访问这些对象的属性(在对应表中的列值):使用get方法,或者直接访问对象上的属性
$user = DB::table('person')->find(5);
// 以下两种形式等效
$name = $user->get('name'); $name = $user->name;
您还可以使用as_array方法通过ORM实例获取所有数据。这将返回一个关联数组,将列名(键)映射到它们的值。
toArray方法接受列名作为可选参数。如果提供了一个或多个这些参数,则仅返回匹配的列名。
$user = DB::table('person')->create();
$user->name = 'Robin';
$user->surname = 'Good';
$user->age = 50;
// Returns array('name' => 'Robin', 'surname' => 'Good', 'age' => 50)
`$data = $user->toArray();`
`// Returns ['name' => 'Robin', 'age' => 50]`
`$data = $user->toArray('name', 'age');`
更新记录
要更新数据库,更改对象的一个或多个属性,然后调用save方法将更改提交到数据库。同样,您可以通过使用set方法或直接设置属性值来更改对象的属性值。通过使用set方法,您还可以通过传递关联数组一次性更新多个属性。
$user = DB::table('users')->find(5);
// The following two forms are equivalent
$user->set('name', 'Tom');
$user->age = 20;
// This is equivalent to the above two assignments
$user->set['name' => 'Tom', 'age' => 20]);
// Synchronise the object with the database
$user->save();
包含表达式的属性
$user = DB::table('users')->find(5);
$user->setExpr('updated_at', 'NOW()');
$user->save();
创建新记录
要添加新记录,首先需要创建一个“空”对象实例。然后像通常一样设置对象上的值,并将其保存。
$user = DB::table('users')->create();
$user->name = 'Alex';
$user->age = 40;
$user->save();
对象保存后,您可以调用其id()方法以找到数据库为其分配的自增主键值。
包含表达式的属性
您可以使用setExpr方法在模型上设置包含数据库表达式的属性。
$user = DB::table('users')->create();
$user->name = 'Alex';
$user->age = 40;
$user->setExpr('created_at', 'NOW()');
$user->save();
添加的列的值将以原始形式插入到查询中,因此允许数据库执行任何引用的功能 - 例如,在本例中的NOW()。
检查属性是否已更改
要检查属性自对象创建(或上次保存)以来是否已更改,请调用isDirty()方法
$name_has_changed = $user->isDirty('name'); // 返回true或false
删除记录
要从数据库中删除对象,只需调用其delete方法。
$user = DB::table('users')->find(5);
$user->delete();
要删除数据库中的多个对象,构建一个查询
DB::table('users')->where('status', 'ban')->deleteMany();
事务
数据库不提供任何额外的处理事务的方法,但使用PDO内置的方法非常简单
$task = \Impressive\ORM\DB::table('tasks')->first(50);
if($task){
\Impressive\ORM\DB::beginTransaction();
$task->project_id=22;
$status = $task->save();
if($status){
\Impressive\ORM\DB::commit();
} else {
\Impressive\ORM\DB::rollback();
}
}
// OR
$task = \Impressive\ORM\DB::table('tasks')->first(50);
if($task){
$res = \Impressive\ORM\DB::transaction(function() use ($task) {
$task->project_id=22;
return $task->save();
});
}
有关更多详细信息,请参阅PDO事务文档。
多个连接
ORM现在支持多个连接。大多数静态函数都可以通过可选的连接名称作为额外参数来使用。
$con = new \Impressive\ORM\Connection('mysql:host=localhost;dbname=master');
$con->set('username', 'root');
$con->set('password', '****');
...
\Impressive\ORM\DB::addConnection($con);
$con = new \Impressive\ORM\Connection('mysql:host=localhost;dbname=slave');
$con->set('username', 'root');
$con->set('password', '****');
...
\Impressive\ORM\DB::addConnection($con, 'slave');
$user = \Impressive\ORM\DB::table('users')->first('1');
d($user->name);
$user = \Impressive\ORM\DB::table('users', 'slave')->first('1');
// $user->name .= time();
// $user->save();
d($user->name);
对于\Impressive\ORM\DB::addConnection($con);方法,这意味着在传递新的连接的连接字符串时,通常省略的第二个参数应该是null。在所有情况下,如果没有提供连接名称,则默认为DB::DEFAULT_CONNECTION。
支持的方法
在这些情况下,$connection_name参数是可选的,它是一个任意键,用于标识命名连接。
- DB::table($table_name, $connection_name)
- DB::setDb($pdo, $connection_name)
- DB::getDb($connection_name)
- DB::rawExecute($query, $parameters, $connection_name)
- DB::getLast_query($connection_name)
- DB::getQueryLog($connection_name)
在这些方法中,只有DB::getLastQuery($connection_name)在未传递连接名称时不回退到默认连接。相反,不传递连接名称(或null)将返回任何连接上的最新查询。
活动记录实现
模型类
您应该为应用程序中的每个实体创建一个模型类。例如,如果您正在构建一个需要用户的程序,则应创建一个User类。您的模型类应扩展基本Model类
class User extends Model {}
Impressive ORM负责创建您模型类的实例,并用来自数据库的数据填充它们。然后,您可以通过实现应用程序逻辑的公共方法来向此类添加行为。这种数据和行为的组合是活动记录模式的精髓。
让我们看看例子
class User extends \Impressive\ORM\Model
{
public function tasks()
{
return $this->hasMany(Task::class, 'user_id');
}
public function projects()
{
return $this->hasMany(Project::class, 'user_id');
}
public function comments()
{
return $this->hasMany(Comments::class, 'user_id');
}
}
class Project extends \Impressive\ORM\Model
{
protected $table = 'project';
public function owner()
{
return $this->belongsTo(User::class, 'user_id');
}
public function team()
{
return $this->hasManyThrough(User::class, ProjectTeam::class, 'project_id', 'user_id');
}
}
class ProjectTeam extends \Impressive\ORM\Model
{
protected $table = 'project_teams';
}
class Comments extends \Impressive\ORM\Model
{
protected $table = 'task_comments';
}
class Task extends \Impressive\ORM\Model
{
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function manager()
{
return $this->belongsTo(User::class, 'manager_id');
}
public function project()
{
return $this->belongsTo(Project::class, 'project_id');
}
public function comments()
{
return $this->hasMany(Comments::class, 'task_id');
}
}
// $project = Project::first(20);
//
// foreach ($project->team as $team) {
// print_r($team);
// }
//
// d(\Impressive\ORM\DB::getQueryLog());
$tasks = Task::where('status', 'new')->limit(10)->find();
// dd($tasks);
?>
<ul>
<?php foreach ($tasks as $task) : ?>
<li>
#<?= $task->id ?> <?= $task->name ?>
<span>Project: <?= $task->project->name ?></span>
<span>Manager: <?= $task->manager->name ?> <?= $task->manager->surname ?></span>
<span>User: <?= $task->user->name ?> <?= $task->user->surname ?></span>
<span>Comments: <?= $task->comments->count() ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php
d(\Impressive\ORM\DB::getQueryLog());
延迟加载
$users = User::find();
foreach($users as $result){
echo $result->profile->img;
}
请注意,如果在上述示例中没有找到profile结果,它将抛出Notice: Trying to get property of non-object... 注意:也许值得为这种情况和其他情况创建一个NULL对象。
IDE 自动完成
由于Impressive ORM不需要您为每个数据库列指定一个方法/函数,因此很难知道特定模型上可用的属性。由于PHP的__get()
方法的魔法特性,IDE无法提供自动完成提示。
/**
* @param int $id
* @param string $name
* @param string $surname
* @param string $email
* ...
*/
class User extends Model {}
数据库表
您的User类应该在您的数据库中有一个相应的user表来存储其数据。
默认情况下,Impressive ORM假设您的类名采用CapWords样式,您的表名采用lowercase_with_underscores样式。它将自动在两者之间进行转换。例如,如果您的类名为UsersGroups,Impressive ORM将查找名为users_groups的表。
如果您使用命名空间,则将按类似方式转换为表名。例如\Users\Meta将转换为users_meta。注意,在本段之前讨论的CapWords替换之外,这里还将反斜杠替换为下划线。
要覆盖默认的命名行为并直接指定表名,请向您的类中添加一个名为$table的公共属性
class User extends Model
{
public $table = 'custom_users_table';
}
主键
Impressive ORM要求您的数据库表有一个唯一的键列。默认情况下,Impressive ORM将使用名为id的列。要覆盖此默认行为,请向您的类中添加一个名为$pk的公共属性
class User extends Model
{
public $pk = 'my_primary_key'
}
关联
Impressive ORM提供了一种简单的API,用于在模型之间实现一对一、一对多和多对多关系(关联)。它与许多其他ORM采用的方法不同,这些ORM使用关联数组在模型类中添加关于关系的配置元数据。这些数组通常非常嵌套和复杂,因此容易出错。
相反,Impressive ORM 将查询关系的操作视为一种行为,并提供一系列辅助方法来帮助生成此类查询。这些辅助方法应该从您的模型类的方法中调用,这些方法的名称用于描述关系。这些方法返回 ORM 实例(而不是实际的模型实例),因此,如果需要,可以在运行之前修改和添加关系查询。
摘要
以下列表总结了 Impressive ORM 提供的关联,并解释了哪种辅助方法支持每种关联类型
一对一
在基础模型中使用 hasOne
,在关联模型中使用 belongsTo
。
一对多
在基础模型中使用 hasMany
,在关联模型中使用 belongsTo
。
多对多
在基础模型和关联模型中都使用 hasManyThrough
。
以下详细讨论了每个关联辅助方法。
单对一
使用 has_one 方法实现一对一关系。例如,假设我们有一个 User 模型。每个用户都有一个 Profile,因此用户表应与 profile 表关联。为了能够找到特定用户的 profile,我们应该在 User 类中添加一个名为 profile 的方法(注意,这里的方法名是任意的,但应描述关系)。此方法调用 Impressive ORM 提供的受保护的 hasOne 方法,传入相关对象的类名。profile 方法应返回一个准备就绪的 ORM 实例,以便进行(可选)进一步过滤。
class Profile extends Model {}
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
此方法的 API 工作如下
// Select a particular user from the database
$user = DB::factory('User')->findOne($user_id);
// Find the profile associated with the user
$profile = $user->profile()->findOne();
默认情况下,Impressive ORM 假设相关表上的外键列的名称与当前(基础)表相同,后缀为 _id。在上面的示例中,Impressive ORM 将在 Profile 类使用的表中查找名为 user_id 的外键列。要覆盖此行为,请在 has_one 调用中添加第二个参数,传入要使用的列的名称。
此外,Impressive ORM 假设当前(基础)表中的外键列是基础表的主键列。在上面的示例中,Impressive ORM 将使用基础表(在这种情况下为用户表)中的名为 user_id 的列(假设 user_id 是用户表的主键)作为基础表中的外键列。要覆盖此行为,请在 hasOne 调用中添加第三个参数,传入要作为基础表外键列使用的列的名称。
多对一
使用 hasMany 方法实现一对多关系。例如,假设我们有一个 User 模型。每个用户有多个 Meta 对象。用户表应与 post 表关联。为了能够找到特定用户的帖子,我们应该在 User 类中添加一个名为 posts 的方法(注意,这里的方法名是任意的,但应描述关系)。此方法调用 Impressive ORM 提供的受保护的 hasMany 方法,传入相关对象的类名。传入模型类名时,请使用实际名称,而不是复数形式。posts 方法应返回一个准备就绪的 ORM 实例,以便进行(可选)进一步过滤。
class UserMeta extends Model {}
class User extends Model
{
public function posts()
{
return $this->hasMany(UserMeta::class);
}
}
此方法的 API 工作如下
// Select a particular user from the database
$user = DB::factory('User')->findOne($user_id);
// Find the posts associated with the user
$posts = $user->posts()->find();
默认情况下,Impressive ORM 假设相关表上的外键列的名称与当前(基础)表相同,后缀为 _id。在上面的示例中,Impressive ORM 将在 UserMeta 类使用的表中查找名为 user_id 的外键列。要覆盖此行为,请在 hasMany 调用中添加第二个参数,传入要使用的列的名称。
此外,Impressive ORM 假设当前(基础)表的外键列是基础表的主键列。在上面的示例中,Impressive ORM 将使用名为 user_id 的列(假设 user_id 是用户表的主键)作为基础表(在这种情况下是用户表)的外键列。要覆盖此行为,请向 has_many 调用添加第三个参数,传入要在基础表中用作外键列的列名。
属于
hasOne 和 hasMany 的“另一边”是 belongsTo。此方法调用与这些方法具有相同的参数,但假设外键位于当前(基础)表中,而不是相关表中。
class Profile extends Model
{
public function user()
{
return $this->belongsTo('User');
}
}
class User extends Model {}
此方法的 API 工作如下
// Select a particular profile from the database
$profile = DB::factory('Profile')->findOne($profile_id);
// Find the user associated with the profile
$user = $profile->user()->findOne();
同样,Impressive ORM 假设当前(基础)表的外键与带 _id 后缀的相关表具有相同的名称。在上面的示例中,Impressive ORM 将寻找名为 user_id 的列。要覆盖此行为,请向 belongsTo 方法传递第二个参数,指定当前(基础)表上要使用的列名。
Impressive ORM 还假设关联(相关)表中的外键是相关表的主键列。在上面的示例中,Impressive ORM 将在用户表(本例中的相关表)中寻找名为 user_id 的列。要覆盖此行为,请向 belongs_to 方法传递第三个参数,指定相关表中用作外键列的列名。
通过...
多对多关系是通过 hasManyThrough 方法实现的。此方法只有一个必需的参数:相关模型的名称。提供更多参数允许我们覆盖方法的默认行为。
例如,假设我们有一个 Book 模型。每本书可能有几个 Author 对象,每个 Author 可能写过几本书。要能够找到特定书籍的作者,我们首先应该创建一个中间模型。此模型的名称应由两个相关类的名称按字母顺序连接而成。在这种情况下,我们的类名为 Author 和 Book,因此中间模型应称为 AuthorBook。
然后,我们应该向 Book 类添加一个名为 authors 的方法(请注意,这里的方法名是任意的,但应该描述关系)。此方法调用 Impressive ORM 提供的受保护 hasManyThrough 方法,传入相关对象的类名。应直接传递模型类名,而不是复数形式。authors 方法应返回一个 ORM 实例,准备进行(可选)进一步筛选。
class Author extends Model
{
public function books()
{
return $this->hasManyThrough('Book');
}
}
class Book extends Model
{
public function authors()
{
return $this->hasManyThrough('Author');
}
}
class AuthorBook extends Model {}
此方法的 API 工作如下
// Select a particular book from the database
$book = DB::factory('Book')->findOne($book_id);
// Find the authors associated with the book
$authors = $book->authors()->find();
// Get the first author
$first_author = $authors[0];
// Find all the books written by this author
$first_author_books = $first_author->books()->find();
覆盖默认设置
hasManyThrough 方法最多接受六个参数,这些参数允许我们逐步覆盖方法做出的默认假设。
第一个参数:关联模型名称 - 这是必需的,应该是我们希望跨关联选择的模型名称。
第二个参数:中间模型名称 - 这是可选的,默认为两个关联模型名称,按字母顺序排序并连接。
第三个参数:在中间表上对基础表的自定义键 - 这是可选的,默认为基础表名称加上 _id。
第四个参数:在中间表上对关联表的自定义键 - 这是可选的,默认为关联表名称加上 _id。
第五个参数:基础表中的外键列 - 这是可选的,默认为基础表中的主键列名称。
第六个参数:关联表中的外键列 - 这是可选的,默认为关联表中的主键列名称。
筛选器
通常,我们希望创建可重用的查询,以便在不重复大量代码的情况下提取特定的数据子集。Impressive ORM通过提供一个名为filter的方法来实现这一点,该方法可以在查询中与现有的DB查询API一起链式调用。filter方法接受当前Model子类上公共静态方法的名字作为参数。在链式调用filter的位置,将调用提供的静态方法,并将ORM对象作为第一个参数传递。该方法应在调用一个或多个查询方法后返回ORM对象。如果需要,可以继续链式调用方法。
以下通过一个例子来说明这一点。假设有一个应用,用户可以被分配一个角色,该角色控制他们对某些功能的访问。在这种情况下,您可能经常需要检索具有“admin”角色的用户列表。为此,您可以在Model类中添加一个名为(例如)admins的静态方法
class User extends Model
{
public static function admins($builder)
{
return $builder->where('role', 'admin');
}
}
然后您可以在查询中使用这个filter
$admin_users = DB::factory('User')->filter('admins')->find();
您也可以像通常一样将其与其它方法链式调用
$admins =
DB::factory('User')
->filter('admins')
->whereLt('age', 18)
->find();
带有参数的filter
您还可以向自定义filter传递参数。传递给filter方法的任何额外参数(在应用filter的名称之后)都将作为额外参数传递给您的自定义filter(在DB实例之后)。
例如,假设您希望将上面的角色filter泛化,以便您可以通过参数检索任何角色的用户。您可以将角色名称作为参数传递给filter
class User extends Model
{
public static function hasRole($builder, $role)
{
return $builder->where('role', $role);
}
}
$admins = DB::factory('User')->filter('has_role', 'admin')->find();
$guests = DB::factory('User')->filter('has_role', 'guest')->find();
多个连接
Impressive ORM支持多个数据库连接(并且必然依赖于支持多个连接的DB的更新版本)。数据库连接通过字符串名称标识,默认为Wrapper::DEFAULT_CONNECTION(实际上是ORM::DEFAULT_CONNECTION)。
class MyClass extends Model
{
public $connection_name = 'custom';
}