kevinsperrine/idiorm

完全重构,PSR 兼容的 Idiorm 版本:一个轻量级、几乎零配置的对象关系映射器和流畅查询构建器,适用于 PHP5。

v2.1.3 2012-10-24 21:34 UTC

This package is not auto-updated.

Last update: 2024-09-28 15:23:43 UTC


README

http://j4mie.github.com/idiormandparis/

一个轻量级、几乎零配置的对象关系映射器和流畅查询构建器,适用于 PHP5。

在 PHP 5.2.0+ 上进行测试 - 可能适用于更早的版本,只要使用 PDO 和正确的数据库驱动程序。

BSD 许可证 下发布。

另请参阅:基于 Idiorm 构建的 Active Record 实现 Paris

功能

  • 使简单的查询和简单的 CRUD 操作变得完全无痛苦。
  • 在需要更复杂的 SQL 时不再干涉。
  • 基于 PDO 构建。
  • 在整个过程中使用 准备语句 来防止 SQL 注入 攻击。
  • 无需模型类、无需 XML 配置和无需代码生成:只需提供连接字符串即可开箱即用。
  • 仅包含一个名为 ORM 的类。对全局命名空间的污染最小。
  • 数据库无关。目前支持 SQLite 和 MySQL。可能支持其他数据库,请尝试一下!

变更日志

1.0.0 - 发布于 2010-12-01

  • 初始版本

1.1.0 - 发布于 2011-01-24

  • 添加 isDirty 方法
  • 添加基本查询缓存
  • 添加 distinct 方法
  • 添加 groupBy 方法

1.1.1 - 发布于 2011-01-30

2.0.0 - 发布于 2012-09-17

  • 没有添加新功能
  • 大规模重写以匹配 PSR 编码标准和 PHPUnit 测试
  • 由于与向后兼容性断开,因此给出了 2.0 版本号

2.1.0 - 发布于 2012-09-19

2.1.1 - 发布于 2012-09-19

  • 推送到 Packagist

2.1.2 - 发布于 2012-10-22

  • 添加旧的下划线公共静态方法,以允许与原始接口向后兼容。

2.1.3 - 发布于 2012-10-24

  • 增加了更多测试以增加代码覆盖率,并修复了沿途发现的几个小错误。

哲学

帕累托法则表明,大约80%的效果来自于20%的原因。在软件开发术语中,这可以翻译成大约80%的结果来自于20%的复杂性。换句话说,你可以通过非常愚蠢的方式来取得很大的进步。

Idiorm 故意保持简单。其他ORM可能包含数十个具有复杂继承层次结构的类,而Idiorm只有一个类,即ORM,它既充当流畅的SELECT查询API,也充当简单的CRUD模型类。如果我的猜测正确,这应该足够许多实际应用。让我们面对现实:我们中的大多数人都不是在建造Facebook。我们正在从事小到中等规模的项目,其中强调的是简单性和快速开发,而不是无限的灵活性和功能。

你可能会把Idiorm看作是一个微型ORM。它可能,也许,是“与Slim的西装相配的领带”(借用DocumentCloud的一句话)。或者它可能是清理那些你不得不支持的糟糕的、SQL垃圾遍地的旧PHP应用的绝佳方式。

Idiorm也可能为构建更高层次、更复杂的数据库抽象提供了一个良好的基础。例如,Paris是基于Idiorm构建的Active Record模式的实现。

让我们看看代码

关于Idiorm的第一件事是,你不需要定义任何模型类就可以使用它。使用几乎所有的其他ORM,首先要做的是设置你的模型并将它们映射到数据库表(通过配置变量、XML文件等)。使用Idiorm,你可以立即开始使用ORM。

设置

首先,require Idiorm源文件

require_once 'idiorm.php';

然后,将数据源名称连接字符串传递给ORM类的configure方法。这被PDO用于连接到你的数据库。有关更多信息,请参阅PDO文档。ORM::configure('sqlite:./example.db');

你可能还需要通过使用usernamepassword配置选项将用户名和密码传递给你的数据库驱动程序。例如,如果你正在使用MySQL

ORM::configure('mysql:host=localhost;dbname=my_database');
ORM::configure('username', 'database_user');
ORM::configure('password', 'top_secret');

也请参阅下面的“配置”部分。

查询

Idiorm提供了一个流畅接口,以允许构建简单的查询而无需编写任何SQL代码。如果你使用过jQuery,你应该熟悉流畅接口的概念。这仅仅意味着你可以将方法调用连锁在一起,一个接一个。这可以使你的代码更易读,因为按顺序排列的方法调用可以开始看起来像句子。

所有Idiorm查询都从调用ORM类上的forTable静态方法开始。这告诉ORM在查询时要使用哪个表。

请注意,此方法不会转义其查询参数,因此表名不应该直接从用户输入中传递。

然后,将添加过滤器和约束到你的查询的方法调用串联在一起。最后,通过调用findOne()findMany()完成链,这些方法执行查询并返回结果。

让我们从一个简单的例子开始。假设我们有一个名为 person 的表,其中包含以下列:id(记录的主键 - Idiorm 假设主键列名为 id,但这是可配置的,见下文),nameagegender

单个记录

任何以 findOne() 结尾的方法链将返回表示您请求的数据库行的 ORM 类的 单个 实例,如果没有找到匹配的记录,则返回 false

要查找 name 列值为 "Fred Bloggs" 的单个记录

$person = ORM::forTable('person')->where('name', 'Fred Bloggs')->findOne();

这大致对应于以下 SQL:SELECT * FROM person WHERE name = "Fred Bloggs"

要按 ID 查找单个记录,可以将 ID 直接传递给 findOne 方法

$person = ORM::forTable('person')->findOne(5);

多个记录

任何以 findMany() 结尾的方法链将返回 ORM 类实例的 数组,每个实例对应于查询中匹配的每一行。如果没有找到行,则返回空数组。

要查找表中的所有记录

$people = ORM::forTable('person')->findMany();

要查找性别为 female 的所有记录

$females = ORM::forTable('person')->where('gender', 'female')->findMany();

计算结果

要返回查询将返回的行数的计数,请调用 count() 方法。

$number_of_people = ORM::forTable('person')->count();

过滤结果

Idiorm 提供了一组方法,用于提取满足某些条件或多个条件的记录。这些方法可以多次调用以构建查询,Idiorm 的流畅接口允许方法调用 链式 调用,从而创建可读性和简单易懂的查询。

注意事项

使用 Idiorm 时,可用的 SQL 支持的条件子集。此外,在运行查询时,所有 WHERE 子句都将使用 AND 连接。目前不支持 OR 连接 WHERE 子句。

这些限制是故意的:这些是最常用的标准,通过避免对非常复杂的查询的支持,Idiorm 代码库可以保持小而简单。

通过 whereRawrawQuery 方法(见下文)提供对更复杂条件和支持的某些支持。如果您发现自己经常需要比 Idiorm 提供的更多功能,可能需要考虑使用功能更全面的 ORM。

相等:wherewhereEqualwhereNotEqual

默认情况下,使用两个参数(列名和值)调用 where 将使用等于运算符(=)将它们组合起来。例如,调用 where('name', 'Fred') 将产生子句 WHERE name = "Fred"

如果您的编码风格更喜欢清晰度而不是简洁性,您可能更喜欢使用 whereEqual 方法:这与 where 相同。

whereNotEqual 方法将 WHERE column != "value" 子句添加到您的查询中。

快捷方式:whereIdIs

这是一个简单的辅助方法,用于通过主键查询表。尊重配置中指定的 ID 列。

小于/大于:whereLtwhereGtwhereLtewhereGte

有四种方法可用于不等式

  • 小于:$people = ORM::forTable('person')->whereLt('age', 10)->findMany();
  • 大于:$people = ORM::forTable('person')->whereGt('age', 5)->findMany();
  • 小于或等于:$people = ORM::forTable('person')->whereLte('age', 10)->findMany();
  • 大于或等于:$people = ORM::forTable('person')->whereGte('age', 5)->findMany();
字符串比较:whereLikewhereNotLike

要添加 WHERE ... LIKE 子句,使用

$people = ORM::forTable('person')->whereLike('name', '%fred%')->findMany();

类似地,要添加 WHERE ... NOT LIKE 子句,使用

$people = ORM::forTable('person')->whereNotLike('name', '%bob%')->findMany();
集合成员:whereInwhereNotIn

要添加 WHERE ... IN ()WHERE ... NOT IN () 条件,分别使用 whereInwhereNotIn 方法。

这两个方法都接受两个参数。第一个是用于比较的列名,第二个是可能的值 数组

$people = ORM::forTable('person')->whereIn('name', array('Fred', 'Joe', 'John'))->findMany();
处理 NULL 值:whereNullwhereNotNull

要添加 WHERE column IS NULLWHERE column IS NOT NULL 条件,分别使用 whereNullwhereNotNull 方法。这两个方法只接受一个参数:要测试的列名。

原始 WHERE 条件

如果你需要一个更复杂的查询,可以使用 whereRaw 方法来指定 WHERE 条件的 SQL 片段。此方法接受两个参数:要添加到查询中的字符串,以及一个(可选)参数数组,这些参数将被绑定到字符串中。如果提供了参数,则字符串应包含问号字符(?)来表示要绑定的值,参数数组应包含按正确顺序替换字符串中的值的值。

此方法可以与 where* 方法以及其他方法(如 offsetlimitorderBy*)一起在方法链中使用。你提供的字符串内容将与前面的 WHERE 条件用 AND 连接。

$people = ORM::forTable('person')
            ->where('name', 'Fred')
            ->whereRaw('(`age` = ? OR `age` = ?)', array(20, 25))
            ->orderByAsc('name')
            ->findMany();

// Creates SQL:
SELECT * FROM `person` WHERE `name` = "Fred" AND (`age` = 20 OR `age` = 25) ORDER BY `name` ASC;

请注意,此方法仅支持“问号占位符”语法,而不是“命名占位符”语法。这是因为 PDO 不允许包含混合占位符类型的查询。此外,你应该确保字符串中的问号占位符数量与数组中的元素数量完全匹配。

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

限制和偏移

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

limitoffset 方法与它们的 SQL 等效项非常相似。

$people = ORM::forTable('person')->where('gender', 'female')->limit(5)->offset(10)->findMany();
排序

请注意,此方法 转义其查询参数,因此不应直接从用户输入传递。

提供了两种方法来添加 ORDER BY 条件到你的查询。这些是 orderByDescorderByAsc,每个都接受一个用于排序的列名。

$people = ORM::forTable('person')->orderByAsc('gender')->orderByDesc('name')->findMany();

分组

请注意,此方法 转义其查询参数,因此不应直接从用户输入传递。

要添加 GROUP BY 条件到你的查询,请调用 groupBy 方法,传递列名。你可以多次调用此方法来添加更多列。

$poeple = ORM::forTable('person')->where('gender', 'female')->groupBy('name')->findMany();

结果列

默认情况下,查询从你的查询返回 SELECT 语句中的所有列。也就是说,调用

$people = ORM::forTable('person')->findMany();

将会得到以下查询

SELECT * FROM `person`;

select 方法让你控制返回哪些列。多次调用 select 来指定要返回的列。

$people = ORM::forTable('person')->select('name')->select('age')->findMany();

将会得到以下查询

SELECT `name`, `age` FROM `person`;

可选地,你还可以向 select 提供第二个参数来指定列的别名

$people = ORM::forTable('person')->select('name', 'person_name')->findMany();

将会得到以下查询

SELECT `name` AS `person_name` FROM `person`;

传递给 select 的列名会自动引用,即使它们包含 table.column 样式的标识符。

$people = ORM::forTable('person')->select('person.name', 'person_name')->findMany();

将会得到以下查询

SELECT `person`.`name` AS `person_name` FROM `person`;

如果你希望覆盖此行为(例如,提供数据库表达式),则应改用 selectExpr 方法。同样,它也接受别名作为可选的第二个参数。

// NOTE: For illustrative purposes only. To perform a count query, use the count() method.
$people_count = ORM::forTable('person')->select('COUNT(*)', 'count')->findMany();

将会得到以下查询

SELECT COUNT(*) AS `count` FROM `person`;

DISTINCT

要向你的查询中添加结果列列表前面的 DISTINCT 关键字,请向查询链添加对 distinct() 的调用。

$distinct_names = ORM::forTable('person')->distinct()->select('name')->findMany();

这将得到以下查询

SELECT DISTINCT `name` FROM `person`;

连接

Idiorm 提供了一组方法来向构建的查询中添加不同类型的 JOIN

方法:joininnerJoinleftOuterJoinrightOuterJoinfullOuterJoin

这些方法都接受相同的参数集合。以下描述将以基本 join 方法为例,但同样适用于每个方法。

前两个参数是必需的。第一个是要连接的表名,第二个提供连接条件。指定条件的方式是作为一个包含三个组件的 数组:第一列、运算符和第二列。表和列名将被自动引用。例如

$results = ORM::forTable('person')->join('person_profile', array('person.id', '=', 'person_profile.person_id'))->findMany();

也可以将条件指定为字符串,它将被原样插入到查询中。然而,在这种情况下,列名 不会 被转义,因此应谨慎使用此方法。

// Not recommended because the join condition will not be escaped.
$results = ORM::forTable('person')->join('person_profile', 'person.id = person_profile.person_id')->findMany();

join 方法还接受一个可选的第三个参数,它是查询中表的 别名。如果您想将表与其自身连接以创建层次结构,这很有用。在这种情况下,最好与 tableAlias 方法结合使用,该方法将为与 ORM 关联的 表添加别名,并使用 select 方法来控制返回哪些列。

$results = ORM::forTable('person')
    ->tableAlias('p1')
    ->select('p1.*')
    ->select('p2.name', 'parent_name')
    ->join('person', array('p1.parent', '=', 'p2.id'), 'p2')
    ->findMany();

原始查询

如果您需要执行更复杂的查询,可以使用 rawQuery 方法完全指定要执行的查询。此方法接受一个字符串和一个参数数组。字符串应包含占位符,可以是问号或命名占位符语法,这些占位符将用于将参数绑定到查询。

$people = ORM::forTable('person')->rawQuery('SELECT p.* FROM person p JOIN role r ON p.role_id = r.id WHERE r.name = :role', array('role' => 'janitor')->findMany();

ORM 类实例(实例)将包含查询返回的所有列的数据。请注意,您仍然必须调用 forTable 来将实例绑定到特定表,即使查询中可以指定一个完全不同的表也没有关系。这是因为如果您稍后想调用 save,ORM 需要知道要更新哪个表。

请注意,使用 rawQuery 是高级的,可能具有风险,Idiorm 不会尝试在您使用此方法时保护您免受错误的影响。如果您经常调用 rawQuery,您可能没有正确理解使用 ORM 的目的,或者您的应用程序可能过于复杂,不适合 Idiorm。考虑使用功能更全面的数据库抽象系统。

从对象获取数据

一旦从查询中返回了一组记录(对象),您可以通过两种方式访问这些对象上的属性(存储在相应表中列中的值):使用 get 方法,或者直接访问对象的属性

$person = ORM::forTable('person')->findOne(5);

// The following two forms are equivalent
$name = $person->get('name');
$name = $person->name;

您还可以使用 asArray 方法获取由 ORM 实例包装的所有数据。这将返回一个关联数组,将列名(键)映射到其值。

asArray 方法接受列名作为可选参数。如果提供了一个或多个这些参数,则只返回匹配的列名。

$person = ORM::forTable('person')->create();

$person->first_name = 'Fred';
$person->surname = 'Bloggs';
$person->age = 50;

// Returns array('first_name' => 'Fred', 'surname' => 'Bloggs', 'age' => 50)
$data = $person->asArray();

// Returns array('first_name' => 'Fred', 'age' => 50)
$data = $person->asArray('first_name', 'age');

更新记录

要更新数据库,更改对象的一个或多个属性,然后调用 save 方法将更改提交到数据库。同样,您可以通过使用 set 方法或直接设置属性的值来更改对象的属性值

$person = ORM::forTable('person')->findOne(5);

// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;

// Syncronise the object with the database
$person->save();

创建新记录

要添加新记录,您需要首先创建一个“空”对象实例。然后按正常方式设置对象的值,并将其保存。

$person = ORM::forTable('person')->create();

$person->name = 'Joe Bloggs';
$person->age = 40;

$person->save();

对象保存后,您可以调用其 id() 方法以找到数据库为其分配的自定义主键值。

检查属性是否已修改

要检查属性自对象创建(或最后保存)以来是否已更改,请调用 isDirty 方法

$name_has_changed = $person->isDirty('name'); // Returns true or false

删除记录

要从数据库中删除对象,只需调用其 delete 方法。

$person = ORM::forTable('person')->findOne(5);
$person->delete();

事务

Idiorm 不提供处理事务的额外方法,但它使用 PDO 内置方法非常简单。

// Start a transaction
ORM::getDatabase()->beginTransaction();

// Commit a transaction
ORM::getDatabase()->commit();

// Roll back a transaction
ORM::getDatabase()->rollBack();

有关更多详细信息,请参阅PDO 事务文档

配置

除了设置数据库连接的 DSN 字符串(见上文)外,还可以使用 configure 方法设置 ORM 类的一些其他简单选项。修改设置需要传递一个键/值对给 configure 方法,表示要修改的设置和要设置的值。

ORM::configure('setting_name', 'value_for_setting');

数据库认证详情

设置:usernamepassword

某些数据库适配器(如 MySQL)要求将用户名和密码分别提供给 DSN 字符串。这些设置允许您提供这些值。一个典型的 MySQL 连接设置可能如下所示

ORM::configure('mysql:host=localhost;dbname=my_database');
ORM::configure('username', 'database_user');
ORM::configure('password', 'top_secret');

PDO 驱动选项

设置:driver_options

某些数据库适配器需要(或允许)传递一个包含特定配置选项的数组。此设置允许您通过 PDO 构造函数传递这些选项。有关更多信息,请参阅PDO 文档。例如,要强制 MySQL 驱动程序在连接时使用 UTF-8

ORM::configure('driver_options', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

PDO 错误模式

设置:error_mode

这可以用于设置 Idiorm 所使用的数据库连接类上的 PDO::ATTR_ERRMODE 设置。它应该传递 PDO 定义的类常量之一。例如

ORM::configure('error_mode', PDO::ERRMODE_WARNING);

默认设置是 PDO::ERRMODE_EXCEPTION。有关可用的错误模式的完整详情,请参阅PDO 文档

标识符引号字符

设置:identifier_quote_character

设置用于引号标识符(例如表名、列名)的字符。如果没有设置,它将根据 PDO 所使用的数据库驱动程序自动检测。

标识符列

默认情况下,ORM 假设所有表都有一个名为 id 的主键列。有两种方法可以覆盖这一点:对数据库中的所有表,或对每个表单独。

设置:id_column

此设置用于配置所有表的主键列名称。如果您的 ID 列名为 primary_key,则使用

ORM::configure('id_column', 'primary_key');

设置:id_column_overrides

此设置用于指定每个表的主键列名称。它接受一个关联数组,将表名映射到列名。例如,如果您的 ID 列名包括表名,您可以使用以下配置

ORM::configure('id_column_overrides', array(
    'person' => 'person_id',
    'role' => 'role_id',
));

查询日志记录

设置:logging

Idiorm 可以记录它执行的查询。要启用查询日志记录,将 logging 选项设置为 true(默认为 false)。

当查询日志记录启用时,您可以使用两个静态方法来访问日志。 ORM::getLastQuery() 返回最近执行的查询。 ORM::getQueryLog() 返回已执行的所有查询的数组。

查询缓存

设置:caching

Idiorm 可以在请求期间缓存它执行的查询。要启用查询缓存,将 caching 选项设置为 true(默认为 false)。

当查询缓存启用时,Idiorm 将缓存它执行的每个 SELECT 查询的结果。如果 Idiorm 遇到已执行的查询,它将直接从其缓存中获取结果,而不是执行数据库查询。

警告和注意事项
  • 请注意,这是一个内存缓存,仅在单个请求的持续时间内保留数据。这不是持久缓存(如Memcached)的替代品。

  • Idiorm的缓存非常简单,在数据变更时不会尝试使缓存失效。这意味着,如果您运行查询以检索一些数据,修改并保存它,然后再次运行相同的查询,结果将会过时(即,它们不会反映您的修改)。这可能会导致应用程序中产生微妙的错误。如果您已启用缓存并遇到异常行为,请禁用它并再次尝试。如果您需要执行此类操作但仍然希望使用缓存,可以调用ORM::clearCache()来清除所有现有的缓存查询。

  • 启用缓存会增加您应用程序的内存使用量,因为每次请求期间获取的所有数据库行都会保留在内存中。如果您正在处理大量数据,您可能希望禁用缓存。