zilionis / idiorm
PHP5 的轻量级几乎零配置对象关系映射器和流畅查询构建器
Requires
- php: >=5.2.0
This package is not auto-updated.
Last update: 2024-09-28 13:52:32 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
- 添加
is_dirty方法 - 添加基本查询缓存
- 添加
distinct方法 - 添加
group_by方法
1.1.1 - 发布于 2011-01-30
- 修复在引号列通配符中的错误。 j4mie/paris#12
- 小的文档改进
理念
帕累托原理表明 大约 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');
你可能还需要通过 username 和 password 配置选项向数据库驱动程序传递用户名和密码。例如,如果你正在使用 MySQL
ORM::configure('mysql:host=localhost;dbname=my_database');
ORM::configure('username', 'database_user');
ORM::configure('password', 'top_secret');
也请参阅下面的 "配置" 部分。
查询
Idiorm 提供了一个 流畅接口,以便可以构建简单的查询而无需编写任何 SQL 字符。如果你使用过 jQuery,你将熟悉流畅接口的概念。这意味着你可以 链式 调用方法,一个接一个。这可以使代码更易于阅读,因为按顺序连接的方法调用开始看起来像一句句子。
所有 Idiorm 查询都以调用 ORM 类上的 for_table 静态方法开始。这告诉 ORM 在执行查询时要使用哪个表。
请注意,此方法 不会 转义其查询参数,因此表名不应直接从用户输入中传递。
然后,将添加筛选器和约束到查询的方法调用串联在一起。最后,通过调用 find_one() 或 find_many() 完成链,这将执行查询并返回结果。
让我们从一个简单的例子开始。假设我们有一个名为 person 的表,该表包含列 id(记录的主键 - Idiorm 假设主键列名为 id,但这可配置,请参阅下面),name,age 和 gender。
单条记录
任何以 find_one() 结尾的方法链将返回数据库行的一个 单个 ORM 类实例,或者如果没有找到匹配的记录,则返回 false。
要查找名为 "Fred Bloggs" 的单条记录
$person = ORM::for_table('person')->where('name', 'Fred Bloggs')->find_one();
这大致等同于以下 SQL:SELECT * FROM person WHERE name = "Fred Bloggs"
要按 ID 查找单条记录,可以直接将 ID 传递给 find_one 方法
$person = ORM::for_table('person')->find_one(5);
多条记录
任何以 find_many() 结尾的方法链将返回一个 ORM 类实例的 数组,每个实例对应于查询匹配的每一行。如果没有找到行,则返回空数组。
要查找表中的所有记录
$people = ORM::for_table('person')->find_many();
要查找性别为 female 的所有记录
$females = ORM::for_table('person')->where('gender', 'female')->find_many();
计算结果
要返回查询将返回的行数的计数,请调用 count() 方法。
$number_of_people = ORM::for_table('person')->count();
筛选结果
Idiorm 提供了一系列方法来提取满足某些条件或多个条件的记录。这些方法可以多次调用以构建查询,Idiorm 的流畅接口允许方法调用 链式调用,从而创建易于阅读和理解查询。
注意事项
使用 Idiorm 时,仅支持 SQL 支持的可用条件的一个子集。此外,当查询运行时,所有 WHERE 子句都将通过 AND 连接起来。目前不支持 OR 连接 WHERE 子句。
这些限制是故意的:这些是最常用的标准,通过避免对非常复杂的查询的支持,Idiorm 代码库可以保持小而简单。
通过 where_raw 和 raw_query 方法提供对更复杂条件和查询的一些支持(见下文)。如果您发现自己经常需要比 Idiorm 提供的更多功能,那么可能是时候考虑使用功能更齐全的 ORM 了。
相等性:where、where_equal、where_not_equal
默认情况下,使用两个参数(列名和值)调用 where 会使用等于运算符(=)将它们组合起来。例如,调用 where('name', 'Fred') 将产生子句 WHERE name = "Fred"。
如果您的编码风格更倾向于清晰而不是简洁,您可能更喜欢使用 where_equal 方法:这与 where 相同。
where_not_equal 方法会在您的查询中添加 WHERE column != "value" 子句。
快捷方式:where_id_is
这是一个简单的辅助方法,用于通过主键查询表。尊重配置中指定的 ID 列。
小于 / 大于:where_lt、where_gt、where_lte、where_gte
有四种方法可用于不等式
- 小于:
$people = ORM::for_table('person')->where_lt('age', 10)->find_many(); - 大于:
$people = ORM::for_table('person')->where_gt('age', 5)->find_many(); - 小于或等于:
$people = ORM::for_table('person')->where_lte('age', 10)->find_many(); - 大于或等于:
$people = ORM::for_table('person')->where_gte('age', 5)->find_many();
字符串比较:where_like 和 where_not_like
要添加 WHERE ... LIKE 子句,使用
$people = ORM::for_table('person')->where_like('name', '%fred%')->find_many();
类似地,要添加 WHERE ... NOT LIKE 子句,使用
$people = ORM::for_table('person')->where_not_like('name', '%bob%')->find_many();
集合成员:where_in 和 where_not_in
要添加 WHERE ... IN () 或 WHERE ... NOT IN () 子句,分别使用 where_in 和 where_not_in 方法。
两种方法都接受两个参数。第一个是要比较的列名。第二个是可能的值 数组。
$people = ORM::for_table('person')->where_in('name', array('Fred', 'Joe', 'John'))->find_many();
处理 NULL 值:where_null 和 where_not_null
要添加 WHERE column IS NULL 或 WHERE column IS NOT NULL 子句,分别使用 where_null 和 where_not_null 方法。这两种方法都接受一个参数:要测试的列名。
原始 WHERE 子句
如果您需要更复杂的查询,可以使用 where_raw 方法来指定 WHERE 子句的确切 SQL 片段。此方法接受两个参数:要添加到查询中的字符串,以及一个(可选)包含参数的数组,这些参数将被绑定到字符串上。如果提供了参数,则字符串应包含问号字符(?)以表示要绑定的值,并且参数数组应包含在字符串中正确顺序替换的值。
此方法可以与其他where_*方法一起在方法链中使用,也可以与offset、limit和order_by_*等方法一起使用。您提供的字符串内容将通过AND与前后WHERE子句连接。
$people = ORM::for_table('person')
->where('name', 'Fred')
->where_raw('(`age` = ? OR `age` = ?)', array(20, 25))
->order_by_asc('name')
->find_many();
// Creates SQL:
SELECT * FROM `person` WHERE `name` = "Fred" AND (`age` = 20 OR `age` = 25) ORDER BY `name` ASC;
请注意,此方法仅支持“问号占位符”语法,不支持“命名占位符”语法。这是因为PDO不允许包含不同类型占位符的查询。此外,您应确保字符串中问号占位符的数量与数组中元素的数量完全匹配。
如果您需要更多的灵活性,可以手动指定整个查询。请参阅下文的“原始查询”。
限制和偏移量
请注意,这些方法不会转义它们的查询参数,因此不应直接从用户输入传递。
limit和offset方法与其SQL等价物映射非常紧密。
$people = ORM::for_table('person')->where('gender', 'female')->limit(5)->offset(10)->find_many();
排序
请注意,这些方法不会转义它们的查询参数,因此不应直接从用户输入传递。
提供了两种方法来为您的查询添加ORDER BY子句。这些是order_by_desc和order_by_asc,每个方法都接受一个要排序的列名。列名将被引用。
$people = ORM::for_table('person')->order_by_asc('gender')->order_by_desc('name')->find_many();
如果您想根据除列名之外的内容进行排序,则可以使用order_by_expr方法将未引用的SQL表达式作为ORDER BY子句添加。
$people = ORM::for_table('person')->order_by_expr('SOUNDEX(`name`)')->find_many();
分组
请注意,此方法不会转义其查询参数,因此不应直接从用户输入传递。
要为您的查询添加GROUP BY子句,请调用group_by方法,并传入列名。您可以多次调用此方法来添加更多列。
$poeple = ORM::for_table('person')->where('gender', 'female')->group_by('name')->find_many();
结果列
默认情况下,查询将从您的查询返回SELECT语句中的所有列。也就是说,调用
$people = ORM::for_table('person')->find_many();
将导致以下查询
SELECT * FROM `person`;
select方法让您控制哪些列被返回。多次调用select以指定要返回的列。
$people = ORM::for_table('person')->select('name')->select('age')->find_many();
将导致以下查询
SELECT `name`, `age` FROM `person`;
可选地,您还可以向select提供第二个参数以指定列的别名
$people = ORM::for_table('person')->select('name', 'person_name')->find_many();
将导致以下查询
SELECT `name` AS `person_name` FROM `person`;
传递给select的列名将自动引用,即使它们包含table.column风格的标识符也是如此
$people = ORM::for_table('person')->select('person.name', 'person_name')->find_many();
将导致以下查询
SELECT `person`.`name` AS `person_name` FROM `person`;
如果您想覆盖此行为(例如,提供数据库表达式),则应改用select_expr方法。同样,它也接受别名作为可选的第二个参数。
// NOTE: For illustrative purposes only. To perform a count query, use the count() method.
$people_count = ORM::for_table('person')->select('COUNT(*)', 'count')->find_many();
将导致以下查询
SELECT COUNT(*) AS `count` FROM `person`;
DISTINCT
要将在查询结果列列表之前添加DISTINCT关键字,请向您的查询链添加对distinct()的调用。
$distinct_names = ORM::for_table('person')->distinct()->select('name')->find_many();
这将导致以下查询
SELECT DISTINCT `name` FROM `person`;
连接
Idiorm提供了一系列方法,用于向其构建的查询添加不同类型的JOIN。
方法:join、inner_join、left_outer_join、right_outer_join、full_outer_join。
这些方法接受相同的参数集。以下描述将使用基本的join方法作为示例,但同样适用于每个方法。
前两个参数是必需的。第一个是要连接的表名,第二个提供了连接的条件。指定条件的推荐方式是一个包含三个组件的数组:第一个列、运算符和第二个列。表和列名将被自动引用。例如
$results = ORM::for_table('person')->join('person_profile', array('person.id', '=', 'person_profile.person_id'))->find_many();
也可以将条件指定为字符串,该字符串将被原样插入到查询中。但是,在这种情况下,列名将不会被转义,因此应谨慎使用此方法。
// Not recommended because the join condition will not be escaped.
$results = ORM::for_table('person')->join('person_profile', 'person.id = person_profile.person_id')->find_many();
join方法还接受一个可选的第三个参数,它是查询中表的别名。如果您想将表与自身连接以创建层次结构,这很有用。在这种情况下,最好将其与table_alias方法结合使用,这将向与ORM关联的主表添加别名,并使用select方法来控制哪些列被返回。
$results = ORM::for_table('person')
->table_alias('p1')
->select('p1.*')
->select('p2.name', 'parent_name')
->join('person', array('p1.parent', '=', 'p2.id'), 'p2')
->find_many();
原始查询
如果您需要执行更复杂的查询,可以使用raw_query方法完全指定要执行的查询。此方法接受一个字符串和一个可选的参数数组。字符串可以包含占位符,可以是问号或命名占位符语法,这将用于将参数绑定到查询。
$people = ORM::for_table('person')->raw_query('SELECT p.* FROM person p JOIN role r ON p.role_id = r.id WHERE r.name = :role', array('role' => 'janitor')->find_many();
返回的ORM类实例将包含查询返回的所有列的数据。请注意,您仍然必须调用for_table方法来将实例绑定到特定的表,即使查询中可以指定一个完全不同的表也没有关系。这是因为如果您稍后要调用save方法,ORM需要知道更新哪个表。
请注意,使用raw_query是高级且可能危险的,Idiorm不会尝试在您使用此方法时保护您免受错误的影响。如果您经常调用raw_query,您可能误解了使用ORM的目的,或者您的应用程序可能过于复杂,不适合Idiorm。考虑使用功能更全面的数据库抽象系统。
从对象获取数据
一旦从查询中返回了一组记录(对象),您可以通过两种方式访问这些对象上的属性(存储在其对应表中列的值):使用get方法,或者直接访问对象的属性
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$name = $person->get('name');
$name = $person->name;
您还可以使用as_array方法获取ORM实例包装的所有数据。这将返回一个关联数组,将列名(键)映射到它们的值。
as_array方法接受列名作为可选参数。如果提供了这些参数中的一个或多个,则只返回匹配的列名。
$person = ORM::for_table('person')->create();
$person->first_name = 'Fred';
$person->surname = 'Bloggs';
$person->age = 50;
// Returns array('first_name' => 'Fred', 'surname' => 'Bloggs', 'age' => 50)
$data = $person->as_array();
// Returns array('first_name' => 'Fred', 'age' => 50)
$data = $person->as_array('first_name', 'age');
更新记录
要更新数据库,更改对象的一个或多个属性,然后调用save方法将更改提交到数据库。同样,您可以通过使用set方法或直接设置属性的值来更改对象属性的值。通过使用set方法,还可以通过传递一个关联数组一次性更新多个属性。
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;
// This is equivalent to the above two assignments
$person->set(array(
'name' => 'Bob Smith',
'age' => 20,
));
// Syncronise the object with the database
$person->save();
创建新记录
要添加新记录,您首先需要创建一个“空”对象实例。然后像通常一样设置对象的值,并保存它。
$person = ORM::for_table('person')->create();
$person->name = 'Joe Bloggs';
$person->age = 40;
$person->save();
对象保存后,您可以调用其id()方法以找到数据库为其自动生成的主键值。
检查属性是否已修改
要检查属性自对象创建(或上次保存)以来是否已更改,请调用is_dirty方法
$name_has_changed = $person->is_dirty('name'); // Returns true or false
删除记录
要从数据库中删除对象,只需调用其delete方法。
$person = ORM::for_table('person')->find_one(5);
$person->delete();
要删除数据库中的多个对象,请构建一个查询
$person = ORM::for_table('person')
->where_equal('zipcode', 55555)
->delete_many();
事务
Idiorm不提供任何额外的处理事务的方法,但它很容易使用PDO的内置方法
// Start a transaction
ORM::get_db()->beginTransaction();
// Commit a transaction
ORM::get_db()->commit();
// Roll back a transaction
ORM::get_db()->rollBack();
有关更多详细信息,请参阅PDO事务文档。
配置
除了设置数据库连接的DSN字符串(见上文)之外,还可以使用configure方法在ORM类上设置一些其他简单选项。修改设置涉及向configure方法传递一个键/值对,表示要修改的设置及其要设置的值。
ORM::configure('setting_name', 'value_for_setting');
数据库身份验证详细信息
设置:username和password
一些数据库适配器(例如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::get_last_query()返回最近执行的查询。 ORM::get_query_log()返回所有已执行的查询数组。
查询缓存
设置:caching
Idiorm可以在请求过程中缓存其执行的查询。要启用查询缓存,将caching选项设置为true(默认为false)。
当启用查询缓存时,Idiorm将缓存每个它执行的SELECT查询的结果。如果Idiorm遇到已经运行的查询,它将直接从其缓存中获取结果,而不是执行数据库查询。
警告和注意事项
-
请注意,这是一个内存缓存,仅在单个请求期间持续数据。这不是持久缓存(如Memcached)的替代品。
-
Idiorm的缓存非常简单,不会尝试在数据更改时使缓存失效。这意味着如果您执行一个查询以检索某些数据,修改并保存它,然后再次执行相同的查询,结果将是过时的(即,它们不会反映您的修改)。这可能导致您应用程序中微妙的错误。如果您启用了缓存并且遇到奇怪的行为,请禁用它并再次尝试。如果您需要进行此类操作但仍希望使用缓存,可以调用
ORM::clear_cache()以清除所有现有的缓存查询。 -
启用缓存将增加应用程序的内存使用量,因为每个请求期间检索的所有数据库行都保留在内存中。如果您处理大量数据,您可能希望禁用缓存。