加拿大dubcanada / idiorm
PHP5的轻量级几乎零配置对象关系映射器和流畅查询构建器
Requires
- php: >=5.2.0
This package is auto-updated.
Last update: 2024-09-11 23:51:38 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月1日
- 初始版本
1.1.0 - 发布于2011年1月24日
- 添加
is_dirty
方法 - 添加基本查询缓存
- 添加
distinct
方法 - 添加
group_by
方法
1.1.1 - 发布于2011年1月30日
- 修复引号列通配符的bug。 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_*
等方法一起使用。你提供的字符串内容将与前面的WHERE子句通过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();
分组
请注意,此方法不会转义其查询参数,因此不应该直接从用户输入传递。
要向查询中添加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
方法,要么直接设置属性的值。
$person = ORM::for_table('person')->find_one(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::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();
事务
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 会根据所使用的数据库驱动程序自动检测。
ID 列
默认情况下,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()
来清除所有现有缓存查询。 -
启用缓存会增加您应用程序的内存使用量,因为每个请求期间获取的所有数据库行都保留在内存中。如果您处理大量数据,您可能希望禁用缓存。