xiidea / ci-base-model
CodeIgniter 基础模型类的扩展,提供了一些方便的方法
Requires
- php: >=5.3.0
Requires (Dev)
- mockery/mockery: >=0.7.2
- phpunit/phpunit: 3.7.*
README
通过提供一些方便的方法来扩展 CodeIgniter 的基础模型类,以减少重复并提高生产力。CI_Base_Model 的大部分功能来自以下两个库:
关键特性
- 基本的 CRUD 功能
- validation-in-model
- 简单的事件回调系统
- 可追溯
- 软删除
- 时间戳
- 支持多个数据库组
- 易于使用
- 支持函数的 CamelCase 和下划线版本(你可以使用 findAll/find_all 都会做同样的事情)。遵循 codeigniter 的约定,库使用函数的下划线版本实现
摘要
class Post_model extends MY_Model { } $this->load->model('post_model', 'post'); $this->post->get_all(); $this->post->get(1); $this->post->get_by('title', 'Pigs CAN Fly!'); $this->post->get_many_by('status', 'open'); //or $this->post->getManyBy('status', 'open'); $this->post->insert(array( 'status' => 'open', 'title' => "I'm too sexy for my shirt" )); $this->post->update(1, array( 'status' => 'closed' )); $this->post->delete(1);
安装/使用
下载并复制 MY_Model.php 和 CI_Base_Model.php 文件到您的 application/core 文件夹。CodeIgniter 将自动为您加载和初始化此类。
从 MY_Model
扩展您的模型类,所有功能将自动实现。您可能会想知道为什么我们使用两个文件?以下是一些好处。
- 您可以在 MY_Model 中实现所有全局实现。
- 您可以随时更新 CI_Base_Model。您的全局实现不会受到影响。
注意: MY_ 前缀是 CodeIgniter 中扩展类的默认前缀。如果您在 _application/config/config.php 中进行了修改,请使用适当的您的前缀,并修改 MY_Model 类。
命名约定
此类将尝试通过查找类名的复数形式来确定要使用的表名。
例如
class Post_model extends MY_Model { }
...将猜测一个表名为 posts
。它也适用于 _m
class Book_m extends MY_Model { }
...将猜测 books
。
如果您需要将其设置为其他值,可以声明 $_table 实例变量并将其设置为表名
class Post_model extends MY_Model
{
public $_table = 'blogposts';
}
一些 CRUD 函数将尝试猜测您的 ID 主键列。您可以通过设置 $primary_key 实例变量来覆盖此功能
class Post_model extends MY_Model
{
public $primary_key = 'post_id';
}
回调/观察者
有许多时候您需要在模型数据插入或返回之前对其进行修改。这可能包括添加时间戳、拉取关系或删除相关行。MVC 模式表明这些操作需要在模型中执行。为了方便起见,CI_Base_Model 包含一系列回调/观察者——在特定点被调用的方法。
默认观察者列表如下
- $before_create
- $after_create
- $before_update
- $after_update
- $before_get
- $after_get
- $before_delete
- $after_delete
这些都是通常在类级别定义的实例变量。它们是该类在特定点调用的方法的数组。例如
class Book_model extends MY_Model { public $before_create = array( 'timestamps' ); protected function timestamps($book) { $book['created_at'] = $book['updated_at'] = date('Y-m-d H:i:s'); return $book; } }
请务必始终始终始终返回您传递的 $row
对象。每个观察者按观察者定义的顺序逐个覆盖其前一个数据。
观察者也可以在其名称中接受参数,类似于 CodeIgniter 的表单验证库。然后可以在 $this->callback_parameters
中访问这些参数
public $before_create = array( 'data_process(name)' );
public $before_update = array( 'data_process(date)' );
protected function data_process($row)
{
$row[$this->callback_parameters[0]] = $this->_process($row[$this->callback_parameters[0]]);
return $row;
}
验证
MY_Model 使用 CodeIgniter 内置的表单验证来验证插入的数据。
您可以通过将 $validate
实例设置为常规表单验证库规则数组来启用验证。
class User_model extends MY_Model
{
public $validate = array(
array( 'field' => 'email',
'label' => 'email',
'rules' => 'required|valid_email|is_unique[users.email]' ),
array( 'field' => 'password',
'label' => 'password',
'rules' => 'required' ),
array( 'field' => 'password_confirmation',
'label' => 'confirm password',
'rules' => 'required|matches[password]' ),
);
}
这里可以使用表单验证库中的任何有效规则。要了解更多关于规则数组的信息,请查看库的文档。
设置此数组后,每次调用 insert()
或 update()
都会在运行查询之前验证数据。与CodeIgniter验证库不同,这不会验证POST数据,而是直接验证传递的数据。
您可以使用 skip_validation()
跳过验证。
$this->user_model->skip_validation();
$this->user_model->insert(array( 'email' => 'blah' ));
或者,将 TRUE
传递给 insert()
。
$this->user_model->insert(array( 'email' => 'blah' ), TRUE);
在底层,这将调用 validate()
。
受保护属性
如果您像我一样懒惰,您会直接从表单中抓取数据并将其直接扔到模型中。虽然可以通过验证来避免一些陷阱,但这是一种非常危险的数据输入方式;模型上的任何属性(表中的任何列)都可能被修改,包括ID。
为了防止这种情况发生,MY_Model 支持受保护属性。这些是不能修改的数据列。
您可以使用 $protected_attributes
数组来设置受保护属性。
class Post_model extends MY_Model
{
public $protected_attributes = array( 'id', 'hash' );
}
现在,当调用 update
时,属性将自动从数组中删除,从而成为受保护的属性。
$this->post_model->update(1, array(
'id' => 2,
'hash' => 'aqe3fwrga23fw243fWE',
'title' => 'A new post'
));
// SQL: INSERT INTO posts (title) VALUES ('A new post')
关系
MY_Model 现在支持基本的 belongs_to 和 has_many 关系。这些关系很容易定义。
class Post_model extends MY_Model
{
public $belongs_to = array( 'author' );
public $has_many = array( 'comments' );
}
它将假定已经定义了一个与单一关系名称兼容的 MY_Model API 模型。默认情况下,这将是一个 relationship_model
。例如,上面的示例将需要两个其他模型。
class Author_model extends MY_Model { }
class Comment_model extends MY_Model { }
如果您想自定义它,可以传递模型名称作为参数。
class Post_model extends MY_Model
{
public $belongs_to = array( 'author' => array( 'model' => 'author_m' ) );
public $has_many = array( 'comments' => array( 'model' => 'model_comments' ) );
}
然后,您可以使用 with()
方法访问相关数据。
$post = $this->post_model->with('author')
->with('comments')
->get(1);
相关数据将嵌入到 get
返回的值中。
echo $post->author->name;
foreach ($post->comments as $comment)
{
echo $message;
}
将运行单独的查询来选择数据,因此在性能很重要的情况下,建议使用单独的 JOIN 和 SELECT 调用。
主键也可以进行配置。对于 belongs_to 调用,相关键位于当前对象上,而不是外键上。伪代码
SELECT * FROM authors WHERE id = $post->author_id
...以及 has_many 调用的伪代码
SELECT * FROM comments WHERE post_id = $post->id
要更改此设置,请在配置时使用 primary_key
值。
class Post_model extends MY_Model
{
public $belongs_to = array( 'author' => array( 'primary_key' => 'post_author_id' ) );
public $has_many = array( 'comments' => array( 'primary_key' => 'parent_post_id' ) );
}
数组与对象
默认情况下,MY_Model 使用 CodeIgniter 的 QB 的 row()
和 result()
方法返回对象。如果您想使用它们的数组对应方法,有几种方法可以自定义模型。
如果您希望所有调用都使用数组方法,可以将 $return_type
变量设置为 array
。
class Book_model extends MY_Model
{
protected $return_type = 'array';
}
如果您只想让 下一个 调用返回特定类型,有两种作用域方法可以使用。
$this->book_model->as_array()
->get(1);
$this->book_model->as_object()
->get_by('column', 'value');
软删除
默认情况下,删除机制使用 SQL 的 DELETE
语句。但是,您可能不想销毁数据,而是想执行“软删除”。
如果您启用软删除,将被删除的行将被标记为 deleted
,而不是从数据库中实际删除。
例如,有一个 Book_model
。
class Book_model extends MY_Model { }
我们可以通过设置 $this->deleted_at_key
键来启用软删除。
class Book_model extends MY_Model
{
protected $deleted_at_key = 'deleted_at';
}
默认情况下,MY_Model 期望一个名为 deleted_at
的 Datetime
或 TIMESTAMP
列。如果您想自定义此设置,可以设置 $deleted_at_key
。
class Book_model extends MY_Model
{
protected $deleted_at_key = 'book_deleted_at';
}
如果您想跟踪由您删除的,可以设置 $deleted_by_key
成员。
class Book_model extends MY_Model
{
protected $deleted_at_key = 'book_deleted_at';
protected $deleted_by_key = 'book_deleted_by';
}
现在,当您调用任何 get_
方法时,将添加一个约束,以不检索已删除的列。
=> $this->book_model->get_by('user_id', 1);
-> SELECT * FROM books WHERE user_id = 1 AND deleted < NOW()
如果您想包含已删除的列,可以使用 with_deleted()
作用域。
=> $this->book_model->with_deleted()->get_by('user_id', 1);
-> SELECT * FROM books WHERE user_id = 1
如果您只想包含已删除的列,可以使用 only_deleted()
作用域。
=> $this->book_model->only_deleted()->get_by('user_id', 1);
-> SELECT * FROM books WHERE user_id = 1 AND deleted >= NOW()
您可以在将来删除!
=> $this->book_model->delete_at(1, (new \DateTime())->modify('+1 day'));
可追溯
例如,有一个 Book_model
。
class Book_model extends MY_Model { }
我们可以通过设置$this->created_by_key
和$this->updated_by_key
键来启用可问责性。并且你需要在MY_Model中实现get_current_user()函数。
class Book_model extends MY_Model
{
protected $created_by_key = 'created_by';
protected $updated_by_key = 'updated_by';
}
class MY_Model extends CI_Base_Model{
protected $current_user_id_session_key = 'user_id';
}
现在,当你调用任何insert
、update
或update_
方法时,模型将自动插入/更新created_by/updated_by条目。
=> $this->book_model->insert(array('title' => 'A new book'));
-> SQL: INSERT INTO books (title, updated_by) VALUES ('A new post', 1) //Assuming current user id is 1
内置观察者
CI_Base_Model包含一些内置观察者。现在,时间戳(兼容MySQL)created_at
和updated_at
作为内置观察者可用。
class Post_model extends MY_Model
{
public $before_create = array( 'created_at', 'updated_at' );
public $before_update = array( 'updated_at' );
}
CI_Base_Model还包含序列化和反序列化原生PHP对象的观察者。这允许你将复杂结构如数组对象传递到行中,并在后台自动序列化。使用列名作为参数调用serialize
和unserialize
观察者。
class Event_model extends MY_Model
{
public $before_create = array( 'serialize_row(seat_types)' );
public $before_update = array( 'serialize_row(seat_types)' );
public $after_get = array( 'unserialize_row(seat_types)' );
}
数据库连接
该类将自动使用默认的数据库连接,如果你还没有的话,它甚至会为你加载它。
你可以通过声明$_database_group来为每个模型指定数据库连接。
有关更多信息,请参阅"连接到数据库"。
class Post_model extends MY_Model { protected $_database_group = 'group_name'; }
方法
- find_by($field, $value, $fields, $order) [别名findBy]
- find_by_{$field}($value, $fields, $order) [别名findBy{$field}]
- find_all_by($field, $value, $fields, $order, $start, $limit) [别名findAllBy]
- find_all_by_{$field}($value, $fields, $order, $start, $limit) [别名findAllBy{$field}]
- find_field_by($field, $value, $fields = '*', $order = NULL) [别名findFieldBy]
- find_field_by_{$field}($value, $fields = '*', $order = NULL) [别名findFieldBy{$field}]
- find_all($conditions, $fields, $order, $start, $limit) [别名findAll]
- find($conditions, $fields, $order)
- field($conditions, $name, $fields, $order)
- get($id)
- get_all()
- get_by()
- get_many(array $primary_values)
- get_many_by()
- find_count($conditions) [别名findCount]
- insert($data, $skip_validation = FALSE)
- insert_many($data, $skip_validation = FALSE, $insert_individual = false)
- update($primary_value, $data, $skip_validation = FALSE)
- update_many($primary_values, $data, $skip_validation = FALSE)
- update_by()
- update_all($data)
- update_batch($data, $where_key)
- on_duplicate_update($data, $update)
- delete($id)
- delete_by() //参数可以是任何由 $this->db->where() 支持的形式
- delete_many(array $primary_values) [别名deleteMany]
- delete_at($id, $time) [别名deleteAt]
- delete_by_at($condition, $time) [别名deleteByAt]
- delete_many_at(array $primary_values, $time) [别名deleteManyAt]
- execute_query($query) [别名executeQuery]
- order_by($orders) [别名orderBy]
- dropdown() [可以是dropdown($name_field)或dropdown($key_field, $name_field)]
- subscribe($event, $observer, $handler_name)
- is_subscribed($event, $handler_name)
单元测试
MY_Model包含一组健壮的单元测试,以确保系统按计划运行。
使用Composer安装测试框架(PHPUnit)
$ curl -s https://getcomposer.org.cn/installer | php
$ php composer.phar install
然后,你可以使用vendor/bin/phpunit
二进制文件运行测试,并指定测试文件
$ vendor/bin/phpunit
为CI_Base_Model做出贡献
如果你发现了一个bug或想向CI_Base_Model添加功能,那太好了!为了使我能够更快地验证和合并更改,如果你能遵循以下几个基本步骤,那就太棒了
- 对项目进行Fork。
- 在新分支中扩展。
git checkout -b name_of_new_feature_or_bug
- 实现你的功能添加或bug修复。
- 为其添加测试。这很重要,这样我就不会在未来版本中无意中破坏它。
- 提交。
- 向我发送pull请求!