299 lines
10 KiB
Markdown
299 lines
10 KiB
Markdown
|
# ORM {#top}
|
|||
|
|
|||
|
Kohana 3.0 包含一个强劲的 ORM 扩展,作用于记录模式,数据库内省来确定模型的列表。
|
|||
|
|
|||
|
ORM 扩展默认包含在 Kohana 3.0 之中,但是如要使用它则需要先开启它。修改 `application/bootstrap.php` 文件中的 [Kohana::modules] 方法并加载 ORM 扩展:
|
|||
|
|
|||
|
Kohana::modules(array(
|
|||
|
...
|
|||
|
'orm' => MODPATH.'orm',
|
|||
|
...
|
|||
|
));
|
|||
|
|
|||
|
## 配置 {#configuration}
|
|||
|
|
|||
|
通过一些配置 ORM 才能工作。通过在模型类继承 ORM:
|
|||
|
|
|||
|
class Model_User extends ORM
|
|||
|
{
|
|||
|
...
|
|||
|
}
|
|||
|
|
|||
|
上面的例子中,模型会寻找默认数据库的 `users` 表。
|
|||
|
|
|||
|
### 模型配置属性
|
|||
|
|
|||
|
下面的属性是用于配置每个模型的:
|
|||
|
|
|||
|
类型 | 属性 | 描述 | 默认值
|
|||
|
----------|---------------------|----------------------------------| -------------------------
|
|||
|
`string` | _table_name | 表名 | `singular model name`
|
|||
|
`string` | _db | 数据库配置名 | `default`
|
|||
|
`string` | _primary_key | 主键 | `id`
|
|||
|
`string` | _primary_val | 主键值 | `name`
|
|||
|
`bool` | _table_names_plural | 表名是否是复数形式 | `TRUE`
|
|||
|
`array` | _sorting | 列名 => 排序方向的数组 | `primary key => ASC`
|
|||
|
`string` | _foreign_key_suffix | 外键的后缀 | `_id`
|
|||
|
|
|||
|
## 使用 ORM
|
|||
|
|
|||
|
### 加载一条记录
|
|||
|
|
|||
|
通过调用 [ORM::factory] 或 [ORM::__construct] 方法创建模型的实例化:
|
|||
|
|
|||
|
$user = ORM::factory('user');
|
|||
|
// 或者
|
|||
|
$user = new Model_User();
|
|||
|
|
|||
|
构造函数和 factory 方法也接受一个主键值来加载模型数据:
|
|||
|
|
|||
|
// 加载 ID 为 5 的用户
|
|||
|
$user = ORM::factory('user', 5);
|
|||
|
|
|||
|
// 检查用户是否加载成功
|
|||
|
if ($user->loaded()) { ... }
|
|||
|
|
|||
|
你同样可以使用传递键-值型数组的数据对象去加载记录:
|
|||
|
|
|||
|
// 加载 email 为 oe@example.com 的用
|
|||
|
$user = ORM::factory('user', array('email' => 'joe@example.com'));
|
|||
|
|
|||
|
### 搜索记录
|
|||
|
|
|||
|
ORM 支持大多数的 [Database] 方法来强劲驱动搜索模型中的数据。ORM 类的 `_db_methods` 属性列出了所有支持调用的方法列表。记录的搜索可以通过 [ORM::find] 和 [ORM::find_all] 方法调用获得。
|
|||
|
|
|||
|
// 搜索活跃用户中名为 Bob 的第一条记录
|
|||
|
$user = ORM::factory('user')
|
|||
|
->where('active', '=', TRUE)
|
|||
|
->where('name', '=', 'Bob')
|
|||
|
->find();
|
|||
|
|
|||
|
// 搜索名为 Bob 的所有用户
|
|||
|
$users = ORM::factory('user')
|
|||
|
...
|
|||
|
->find_all();
|
|||
|
|
|||
|
当你使用 [ORM::find_all] 搜索一批记录模型,你可以使用迭代从数据库结果中获取每条记录模型:
|
|||
|
|
|||
|
foreach ($users as $user)
|
|||
|
{
|
|||
|
...
|
|||
|
}
|
|||
|
|
|||
|
ORM 一个强大的特性是 [ORM::as_array] 方法,它把返回的记录集转为为一个数组。如果使用了 [ORM::find_all] 所有的记录都会以数组的形式返回。
|
|||
|
对于选择列的时候是非常好用的:
|
|||
|
|
|||
|
// 显示选择列的用户名 (使用 id 作为其值)
|
|||
|
form::select('user', ORM::factory('user')->find_all()->as_array('id', 'username') ...
|
|||
|
|
|||
|
### 记录数
|
|||
|
|
|||
|
使用 [ORM::count_all] 方法返回查询返回记录集的记录数。
|
|||
|
|
|||
|
// 用户的记录数
|
|||
|
$count = ORM::factory('user')->where('active', '=', TRUE)->count_all();
|
|||
|
|
|||
|
如果你想在特定子集的查询语句中统计所有用户的记录数,在调用 `count_all` 方法之前先调用 [ORM::reset] 方法并赋值 `FALSE`:
|
|||
|
|
|||
|
$user = ORM::factory('user');
|
|||
|
|
|||
|
// 用户的总数 (reset FALSE prevents the query object from being cleared)
|
|||
|
$count = $user->where('active', '=', TRUE)->reset(FALSE)->count_all();
|
|||
|
|
|||
|
// 仅返回前 10 条记录的记录数
|
|||
|
$users = $user->limit(10)->find_all();
|
|||
|
|
|||
|
### 取出模型属性
|
|||
|
|
|||
|
所有的模型属性都可以通过 PHP 的魔法方法 `__get` 和 `__set` 得到读写权。
|
|||
|
|
|||
|
$user = ORM::factory('user', 5);
|
|||
|
|
|||
|
// 输出用户名
|
|||
|
echo $user->name;
|
|||
|
|
|||
|
// 更改用户名
|
|||
|
$user->name = 'Bob';
|
|||
|
|
|||
|
假如保存的信息/属性并不存在于模型表中,使用 `_ignored_columns` 来忽略数据成员。
|
|||
|
|
|||
|
class Model_User extends ORM
|
|||
|
{
|
|||
|
...
|
|||
|
protected $_ignored_columns = array('field1', 'field2', ...)
|
|||
|
...
|
|||
|
}
|
|||
|
|
|||
|
使用 [ORM::values] 方法设置键-值型数组:
|
|||
|
|
|||
|
$user->values(array('username' => 'Joe', 'password' => 'bob'));
|
|||
|
|
|||
|
### 创建并存储记录
|
|||
|
|
|||
|
[ORM::save] 方法既可以用于创建新记录也可作用于更新现有记录。
|
|||
|
|
|||
|
// 创建新记录
|
|||
|
$user = ORM::factory('user');
|
|||
|
$user->name = 'New user';
|
|||
|
$user->save();
|
|||
|
|
|||
|
// 更新现有记录
|
|||
|
$user = ORM::factory('user', 5);
|
|||
|
$user->name = 'User 2';
|
|||
|
$user->save();
|
|||
|
|
|||
|
// 检查记录是否保存成功
|
|||
|
if ($user->saved()) { ... }
|
|||
|
|
|||
|
你也可以使用 [ORM::save_all] 方法来更新多条记录:
|
|||
|
|
|||
|
$user = ORM::factory('user');
|
|||
|
$user->name = 'Bob';
|
|||
|
|
|||
|
// 更新所有结果记录的名字为 'Bob'
|
|||
|
$user->where('active', '=', TRUE)->save_all();
|
|||
|
|
|||
|
#### 使用 `Updated` 和 `Created` 列
|
|||
|
|
|||
|
`_updated_column` 和 `_created_column` 变量是用于当模型更新或插入新纪录的时候自动更新设置的字段值。默认没有使用。如果你想使用:
|
|||
|
|
|||
|
// date_created 列用于储存创建的时间,使用 TRUE 保存的是时间戳(timestamp)
|
|||
|
protected $_created_column = array('date_created' => TRUE);
|
|||
|
|
|||
|
// date_modified 列用于储存最后修改时间。这里的时间设置为使用 date() 格式后的字符串
|
|||
|
protected $_updated_column = array('date_modified' => 'm/d/Y');
|
|||
|
|
|||
|
### 删除记录
|
|||
|
|
|||
|
删除记录可以使用 [ORM::delete] 和 [ORM::delet_all] 方法。这两个方法的使用和上面 存储记录 方法类似,但有一点不同的是 [ORM::delete] 方法带有一个删除记录 'id' 的可选参数。
|
|||
|
|
|||
|
### 关系
|
|||
|
|
|||
|
ORM 提供强大的关系模型。Ruby 有一篇介绍关系模型的文章: [http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)
|
|||
|
|
|||
|
#### Belongs-To 和 Has-Many
|
|||
|
|
|||
|
假设我们在一所学校工作,当然学校有很多学生,而每个学生都只属于一个学校。这样的关系模型可以这样定义:
|
|||
|
|
|||
|
// school 模型文件
|
|||
|
protected $_has_many = array('students' => array());
|
|||
|
|
|||
|
// student 模型文件
|
|||
|
protected $_belongs_to = array('school' => array());
|
|||
|
|
|||
|
获取学生的学校:
|
|||
|
|
|||
|
$school = $student->school;
|
|||
|
|
|||
|
获取学校的学生:
|
|||
|
|
|||
|
// 注意在 studends 后必须调用 find_all 方法
|
|||
|
$students = $school->students->find_all();
|
|||
|
|
|||
|
// 缩小范围查询:
|
|||
|
$students = $school->students->where('active', '=', TRUE)->find_all();
|
|||
|
|
|||
|
默认情况下,在 student 表定义的模型文件中 ORM 会寻找 `school_id` 当作外键。这个可以通过 `foreign_key` 属性更改:
|
|||
|
|
|||
|
protected $_belongs_to = array('school' => array('foreign_key' => 'schoolID'));
|
|||
|
|
|||
|
外键应该同时覆写 student 和 school 模型文件。
|
|||
|
|
|||
|
#### Has-One
|
|||
|
|
|||
|
Has-One 是 Has-Many 的一个特别情况,唯一不同的这是一对一关系。还以上面的例子说明就是,每个学校有且只有一个学生(当然这是一个很牵强呃例子)。
|
|||
|
|
|||
|
// school 模型文件
|
|||
|
protected $_has_one = array('student' => array());
|
|||
|
|
|||
|
类似于 Belongs-To,当你引用 Has-One 关系对象的时候无需调用 `find` 方法 - 它是自动完成的。
|
|||
|
|
|||
|
#### Has-Many "Through"
|
|||
|
|
|||
|
Has-Many "through" 关系(也可以称之为 Has-And-Belongs-To-Many) is used in the case of one object being related to multiple objects of another type, and visa-versa. For instance, a student may have multiple classes and a class may have multiple students. In this case, a third table and model known as a `pivot` is used. In this case, we will call the pivot object/model `enrollment`.
|
|||
|
|
|||
|
// student (学生)模型文件
|
|||
|
protected $_has_many = array('classes' => array('through' => 'enrollment'));
|
|||
|
|
|||
|
// class (班级)模型文件
|
|||
|
protected $_has_many = array('students' => array('through' => 'enrollment'));
|
|||
|
|
|||
|
其中 enrollment 表包含两个外键: `class_id` 和 `student_id`。在定义关系时,使用 `foreign_key` 和 `far_key` 覆写了默认值。例如:
|
|||
|
|
|||
|
// student (学生)模型文件() (the foreign key refers to this model [student], while the far key refers to the other model [class])
|
|||
|
protected $_has_many = array('classes' => array('through' => 'enrollment', 'foreign_key' => 'studentID', 'far_key' => 'classID'));
|
|||
|
|
|||
|
// class (班级)模型文件
|
|||
|
protected $_has_many = array('students' => array('through' => 'enrollment', 'foreign_key' => 'classID', 'far_key' => 'studentID'));
|
|||
|
|
|||
|
enrollment 模型文件应该这样定义:
|
|||
|
|
|||
|
// Enrollment 模型同时属于一个 student 和 class
|
|||
|
protected $_belongs_to = array('student' => array(), 'class' => array());
|
|||
|
|
|||
|
获取相关对象:
|
|||
|
|
|||
|
// 从 student 中获取 classes
|
|||
|
$student->classes->find_all();
|
|||
|
|
|||
|
// 从 class 中获取 students
|
|||
|
$class->students->find_all();
|
|||
|
|
|||
|
### 校验
|
|||
|
|
|||
|
ORM 和 [Validate] 类是紧密结合使用的。ORM 提供以下几种校验方式:
|
|||
|
|
|||
|
* _rules
|
|||
|
* _callbacks
|
|||
|
* _filters
|
|||
|
* _labels
|
|||
|
|
|||
|
#### `_rules`
|
|||
|
|
|||
|
protected $_rules = array
|
|||
|
(
|
|||
|
'username' => array('not_empty' => array()),
|
|||
|
'email' => array('not_empty' => array(), 'email' => array()),
|
|||
|
);
|
|||
|
|
|||
|
检测并确保 `username` 字段不为空。检测 `email` 字段不为空且是有效的 Email 地址格式。那些传递空值数组用于提供可选的额外参数到校验方法中使用。
|
|||
|
|
|||
|
#### `_callbacks`
|
|||
|
|
|||
|
protected $_callbacks = array
|
|||
|
(
|
|||
|
'username' => array('username_unique'),
|
|||
|
);
|
|||
|
|
|||
|
`username` 字段被传递到了 `username_unique` 回调函数。如果方法存在于当前模型它就会被调用,否则调用全局函数。下面有个小例子:
|
|||
|
|
|||
|
public function username_unique(Validate $data, $field)
|
|||
|
{
|
|||
|
// 确保 username 是唯一的
|
|||
|
...
|
|||
|
}
|
|||
|
|
|||
|
#### `_filters`
|
|||
|
|
|||
|
protected $_filters = array
|
|||
|
(
|
|||
|
TRUE => array('trim' => array()),
|
|||
|
'username' => array('stripslashes' => array()),
|
|||
|
);
|
|||
|
|
|||
|
`TRUE` 值代表 `trim` 过滤器应用到所有字段。而 `username` 字段则在校验前使用 `stripslashes` 过滤。那些传递空值数组用于提供可选的额外参数到校验方法中使用。
|
|||
|
|
|||
|
#### 检测对象是否通过校验
|
|||
|
|
|||
|
使用 [ORM::check] 检测当前对象是否通过校验:
|
|||
|
|
|||
|
// 设置完对象的值,接下来检测是否通过校验
|
|||
|
if ($user->values($_POST)->check())
|
|||
|
{
|
|||
|
$user->save();
|
|||
|
}
|
|||
|
|
|||
|
你也可是使用 `validate()` 方法直接访问模型的校验对象:
|
|||
|
|
|||
|
// 手动添加额外的过滤器
|
|||
|
$user->validate()->filter('username', 'trim');
|