基于Active Record的Impressive ORM。

安装: 22

依赖项: 0

建议者: 0

安全: 0

类型:

v1.1 2018-07-12 10:02 UTC

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_NDB::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();

字符串比较:whereLikewhereNotLike

要添加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' ));

集合成员:whereInwhereNotIn

要添加WHERE ... IN ()或WHERE ... NOT IN ()子句,分别使用whereIn和whereNotIn方法。

这两种方法都接受两个参数。第一个是要比较的列名。第二个是可能值的数组。像所有where...方法一样,您可以使用关联数组指定多个列,只需将其作为唯一第一个参数传递。

$users = DB::table('users')->whereIn('name', array('A', 'B', 'C'))->find();

处理NULL值:whereNullwhereNotNull

要添加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 不允许包含混合占位符类型的查询。此外,您应确保字符串中的问号占位符数量与数组中的元素数量完全匹配。

如果您需要更多的灵活性,可以手动指定整个查询。请参阅下面的原始查询。

限制和偏移量

请注意,这些方法 **不转义它们的查询参数,因此不应直接从用户输入传递。

limitoffset 方法与它们的 SQL 等价物非常相似。

$users = DB::table('users')->where('role', 'admin')->limit(5)->offset(10)->find();

排序

请注意,这些方法 **不转义它们的查询参数,因此不应直接从用户输入传递。

提供了两种方法来向查询添加 ORDER BY 子句。这些是 orderByDescorderByAscorderByRaworderBy,每个都接受一个要排序的列名。列名将被引用。

$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();

您还可以使用 havingEqualhavingNotEqualhavingIdIshavingLikehavingGthavingLthavingGtehavingLtehavingInhavingNotInhavingNullhavingNotNullhavingRaw

结果列

默认情况下,查询将从您的查询返回 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';
}