nueip/codeigniter-model

基于My_model示例的CodeIgniter 3 ORM基础模型模式

3.0.2.2 2020-05-15 10:24 UTC

This package is auto-updated.

Last update: 2024-09-15 20:48:56 UTC


README

CodeIgniter模型


支持读写连接的CodeIgniter 3 Active Record (ORM)标准模型

Latest Stable Version License

此ORM模型扩展已收集到nueip/codeigniter-pack,这是一个针对Codeigniter框架的完整解决方案。

特性

此包提供基础模型,扩展了CI_Model并提供完整的CRUD方法,使CodeIgniter应用程序的数据库交互开发更加容易和快捷。

概述

演示

ActiveRecord (ORM)

$this->load->model('Posts_model');

// Create an Active Record
$post = new Posts_model;
$post->title = 'CI3'; // Equivalent to `$post['title'] = 'CI3';`
$post->save();

// Update the Active Record found by primary key
$post = $this->Posts_model->findOne(1);
if ($post) {
    $oldTitle = $post->title; // Equivalent to `$oldTitle = $post['title'];`
    $post->title = 'New CI3';
    $post->save();
}

使用查询构建器查找

模型将定义数据库连接和表本身。

$records = $this->Posts_model->find()
    ->select('*')
    ->where('is_public', '1')
    ->limit(25)
    ->order_by('id')
    ->get()
    ->result_array();

CRUD

$result = $this->Posts_model->insert(['title' => 'Codeigniter Model']);

// Find out the record which just be inserted
$record = $this->Posts_model->find()
  ->order_by('id', 'DESC')
  ->get()
  ->row_array();
  
// Update the record
$result = $this->Posts_model->update(['title' => 'CI3 Model'], $record['id']);

// Delete the record
$result = $this->Posts_model->delete($record['id']);

要求

此库需要以下内容

  • PHP 5.4.0+
  • CodeIgniter 3.0.0+

安装

在Codeigniter项目的\application文件夹下运行Composer

composer require nueip/codeigniter-model

检查Codeigniter application/config/config.php

$config['composer_autoload'] = TRUE;

您可以将供应商路径自定义到$config['composer_autoload']

配置

安装后,nueip\Model类即可使用。直接创建一个模型来扩展nueip\Model

class Post_model extends nueip\Model {}

之后,此模型即可使用,例如:$this->PostModel->findOne(123);

然而,您应用中的表结构,如主键,可能不同于默认值,为每个模型定义重复的表结构很麻烦。我们建议您将 My_model 继承自 nueip\Model

使用My_model扩展基础模型以用于每个模型

您可以使用 My_model 继承 nueip\Model,然后在 Codeigniter 应用中让每个模型继承 My_model

1. 创建一个继承自 nueip\ModelMy_model,并配置以适应您的常用表结构

class My_model extends nueip\Model
{
    protected $primaryKey = 'sn';
    const CREATED_AT = 'created_time';
    const UPDATED_AT = 'updated_time';
    // Customized Configurations for your app...
}

2. 在应用中为每个模型创建一个继承自 My_model 的模型,并为其配置自己的表结构

class Post_model extends My_model
{
    protected $table = "post_table";
}

3. 使用每个扩展模型进行库操作

$this->load->model('post_model', 'PostModel');

$post = $this->PostModel->findOne(123);

带有文档的 My_model 示例

定义模型

要开始,让我们创建一个继承自 nueip\Model 或通过 My_model 的模型,然后为每个模型适当地定义。

表名

按照惯例,类的“snake case”名称(不包含 _model 后缀)将用作表名,除非显式指定了另一个名称。因此,在这种情况下,模型将假设 Post_model 模型将记录存储在 post 表中。您可以在模型上定义一个表属性来指定自定义表

// class My_model extends nueip\Model
class Post_model extends My_model
{
    protected $table = "post_table";
}

您可以为模型设置表别名,通过定义 protected $alias = 'A1';

表名猜测规则

在我们的模式中,模型类和表之间的命名是一致的,无论单数还是复数名称都支持

获取表名

您可以从每个模型获取表名

$tableName = $this->PostModel->getTable();

主键

您可以定义一个受保护的 $primaryKey 属性来覆盖此约定

class My_model extends nueip\Model
{
    protected $primaryKey = "sn";
}

正确设置模型的主键对于 Active Record (ORM) 是必要的。

时间戳

默认情况下,模型期望您的表上存在 created_atupdated_at 列。如果您不希望这些列由基础模型自动管理,请将模型上的 $timestamps 属性设置为 false

class My_model extends nueip\Model
{
    protected $timestamps = false;
}

如果您需要自定义时间戳的格式,请将模型上的 $dateFormat 属性设置。此属性确定日期属性在数据库中的存储方式

class My_model extends nueip\Model
{
    /**
     * Date format for timestamps.
     *
     * @var string unixtime(946684800)|datetime(2000-01-01 00:00:00)
     */
    protected $dateFormat = 'datetime';
}

如果您需要自定义用于存储时间戳的列的名称,您可以在模型中设置 CREATED_ATUPDATED_AT 常量

class My_model extends nueip\Model
{
    const CREATED_AT = 'created_time';
    const UPDATED_AT = 'updated_time';
}

此外,您可以通过将其赋值为空来自定义关闭特定列的时间戳行为

class My_model extends nueip\Model
{
    const CREATED_AT = 'created_time';
    const UPDATED_AT = NULL;
}

数据库连接

默认情况下,所有模型都将使用为您的应用程序配置的默认数据库连接 $this->db。如果您希望为模型指定不同的连接,请使用 $database 属性

class My_model extends nueip\Model
{
    protected $database = 'database2';
}

更多数据库连接设置:读取 & 写入连接

基本用法

上述使用示例是在模型外部调用模型,例如在控制器中

$this->load->model('post_model', 'Model');

如果您在模型本身中调用方法,只需将 $this 作为模型即可。例如,使用 $this->find()... 调用 find() 方法;

方法

find()

为查询目的创建一个具有模型功能的现有 CI Query Builder 实例。

public CI_DB_query_builder find(boolean $withAll=false)

示例

$records = $this->Model->find()
    ->select('*')
    ->where('is_public', '1')
    ->limit(25)
    ->order_by('id')
    ->get()
    ->result_array();
// Without any scopes & conditions for this query
$records = $this->Model->find(true)
    ->where('is_deleted', '1')
    ->get()
    ->result_array();
    
// This is equal to find(true) method
$this->Model->withAll()->find();

从模型启动 find() 后,它返回原始的 CI_DB_query_builder 以进行链式操作。查询构建器可以参考 CodeIgniter 查询构建器类文档

查询构建器实现

您可以将查询构建器分配给一个变量来处理附加条件,而不是使用 $this->Model->getBuilder()

$queryBuilder = $this->Model->find();
if ($filter) {
    $queryBuilder->where('filter', $filter);
}
$records = $queryBuilder->get()->result_array();

reset()

使用模型重置 CI Query Builder 实例。

public self reset()

示例

$this->Model->reset()->find();

insert()

使用时间戳功能将一行插入到相关数据库表的属性值中。

public boolean insert(array $attributes, $runValidation=true)

示例

$result = $this->Model->insert([
    'name' => 'Nick Tsai',
    'email' => 'myintaer@gmail.com',
]);

batchInsert()

使用时间戳功能将一批行插入到相关数据库表的属性值中。

public integer batchInsert(array $data, $runValidation=true)

示例

$result = $this->Model->batchInsert([
     ['name' => 'Nick Tsai', 'email' => 'myintaer@gmail.com'],
     ['name' => 'Yidas', 'email' => 'service@yidas.com']
]);

replace()

使用时间戳功能将一行替换到相关数据库表的属性值中。

public boolean replace(array $attributes, $runValidation=true)

示例

$result = $this->Model->replace([
    'id' => 1,
    'name' => 'Nick Tsai',
    'email' => 'myintaer@gmail.com',
]);

update()

使用时间戳功能将更改保存到所选记录(复数)到相关数据库表中。

public boolean update(array $attributes, array|string $condition=NULL, $runValidation=true)

示例

$result = $this->Model->update(['status'=>'off'], 123)
// Find conditions first then call again
$this->Model->find()->where('id', 123);
$result = $this->Model->update(['status'=>'off']);

注意:您需要从模型中调用 update,而不是从 CI-DB 构建链中调用,错误的示例代码

$this->Model->find()->where('id', 123)->update('table', ['status'=>'off']);

batchUpdate()

将多个更新查询合并为查询字符串。

public integer batchUpdate(array $data, $index, integer $maxSize=100, $runValidation=true)

示例

$result = $this->Model->batchUpdate([
    ['id'=>1, 'title'=>'A1', 'modified'=>'1'],
    ['id'=>2, 'title'=>'A2', 'modified'=>'1'],
], 'id');

batchUpdateMixed()

将多个更新查询合并为查询字符串。

public integer batchUpdateMixed(array $dataSet, boolean $withAll=false, interger $maxLength=4*1024*1024, $runValidation=true)

示例

$result = $this->Model->batchUpdateMixed([
    [['title'=>'A1', 'modified'=>'1'], ['id'=>1]],
    [['title'=>'A2', 'modified'=>'1'], ['id'=>2]],
]);

delete()

使用时间戳功能删除选定的记录(条)到相关数据库表。

public boolean delete(array|string $condition=NULL, boolean $forceDelete=false, array $attributes=[])

示例

$result = $this->Model->delete(123)
// Find conditions first then call again
$this->Model->find()->where('id', 123);
$result = $this->Model->delete();
// Force delete for SOFT_DELETED mode 
$this->Model->delete(123, true);

getLastInsertID()

执行数据库插入时获取插入 ID 号。

示例

$result = $this->Model->insert(['name' => 'Nick Tsai']);
$lastInsertID = $this->Model->getLastInsertID();

getAffectedRows()

执行“写入”类型查询(插入、更新等)时获取受影响行数。

public integer|string getLastInsertID()

示例

$result = $this->Model->update(['name' => 'Nick Tsai'], 32);
$affectedRows = $this->Model->getAffectedRows();

count()

从查询中获取计数。

public integer count(boolean $resetQuery=true)

示例

$result = $this->Model->find()->where("age <", 20);
$totalCount = $this->Model->count();

setAlias()

设置表别名。

public self setAlias(string $alias)

示例

$query = $this->Model->setAlias("A1")
    ->find()
    ->join('table2 AS A2', 'A1.id = A2.id');

ACTIVE RECORD (ORM)

活动记录为访问和操作存储在数据库中的数据提供了一个面向对象的接口。活动记录模型类与数据库表相关联,活动记录实例对应于该表的行,活动记录实例的属性代表该行中特定列的值。

活动记录(ORM)支持如插入和更新时的时间戳事件。

插入

要创建数据库中的新记录,创建一个新的模型实例,设置模型上的属性,然后调用 save 方法

$this->load->model('Posts_model');

$post = new Posts_model;
$post->title = 'CI3';
$result = $post->save();

更新

save 方法还可以用于更新数据库中已存在的模型。要更新模型,您应检索它,设置您希望更新的任何属性,然后调用 save 方法

$this->load->model('Posts_model');

$post = $this->Posts_model->findOne(1);
if ($post) {
    $post->title = 'New CI3';
    $result = $post->save();
}

删除

要删除活动记录,请在模型实例上调用 delete 方法

$this->load->model('Posts_model');

$post = $this->Posts_model->findOne(1);
$result = $post->delete();

delete() 支持软删除,如果是活动记录,则指向自身。

访问数据

您可以通过访问活动记录实例的属性 $activeRecord->attribute 或通过数组键 $activeRecord['attribute'] 获取列值。

$this->load->model('Posts_model');

// Set attributes
$post = new Posts_model;
$post->title = 'CI3';
$post['subtitle'] = 'PHP';
$post->save();

// Get attributes
$post = $this->Posts_model->findOne(1);
$title = $post->title;
$subtitle = $post['subtitle'];

方法

findOne()

通过主键或列值数组返回单个活动记录模型实例。

public object findOne(array $condition=[])

示例

// Find a single active record whose primary key value is 10
$activeRecord = $this->Model->findOne(10);

// Find the first active record whose type is 'A' and whose status is 1
$activeRecord = $this->Model->findOne(['type' => 'A', 'status' => 1]);

// Query builder ORM usage
$this->Model->find()->where('id', 10);
$this->Model->findOne();

findAll()

返回与指定的主键值或列值匹配的活动记录模型列表。

public array findAll(array $condition=[])

示例

// Find the active records whose primary key value is 10, 11 or 12.
$activeRecords = $this->Model->findAll([10, 11, 12]);

// Find the active recordd whose type is 'A' and whose status is 1
$activeRecord = $this->Model->findAll(['type' => 'A', 'status' => 1]);

// Query builder ORM usage
$this->Model->find()->where_in('id', [10, 11, 12]);
$this->Model->findAll();

save()

活动记录(ORM)保存插入或更新

public boolean save($runValidation=true)

toArray()

活动记录转换为数组记录

public array toArray()

示例

if ($activeRecord)
    $record = $activeRecord->toArray();

建议使用 find() 与 CI 构建器一起使用,而不是使用 ORM 并将其转换为数组。

软删除

除了实际上从您的数据库中删除记录之外,此模型还可以“软删除”模型。当模型被软删除时,它们实际上并未从您的数据库中删除。相反,可以在模型上设置一个 deleted_at 属性并将其插入数据库。

配置

您可以通过将字段名传递给 SOFT_DELETED 来启用 SOFT DELETED 功能。

class My_model extends nueip\Model
{
    const SOFT_DELETED = 'is_deleted';
}

启用 SOFT_DELETED 时,您可以为适合表架构设置 $softDeletedFalseValue$softDeletedTrueValue。此外,您可以将 DELETED_AT 设置为列名以用于时间戳功能,或将其默认设置为 NULL

class My_model extends nueip\Model
{
    const SOFT_DELETED = 'is_deleted';
    
    // The actived value for SOFT_DELETED
    protected $softDeletedFalseValue = '0';
    
    // The deleted value for SOFT_DELETED
    protected $softDeletedTrueValue = '1';

    const DELETED_AT = 'deleted_at';
}

如果您需要为特定模型禁用 SOFT DELETED 功能,可以将 SOFT_DELETED 设置为 false,这将禁用包括 DELETED_AT 功能在内的所有 SOFT DELETED 功能。

// class My_model extends nueip\Model
class Log_model extends My_model
{
    const SOFT_DELETED = false;
}

方法

forceDelete()

使用时间戳功能强制删除选定的记录(条)到相关数据库表。

public boolean forceDelete($condition=null)

示例

$result = $this->Model->forceDelete(123)
// Query builder ORM usage
$this->Model->find()->where('id', 123);
$result = $this->Model->forceDelete();

restore()

将 SOFT_DELETED 字段值恢复为选定的记录(条)到相关数据库表。

public boolean restore($condition=null)

示例

$result = $this->Model->restore(123)
// Query builder ORM usage
$this->Model->withTrashed()->find()->where('id', 123);
$this->Model->restore();

withTrashed()

对于下一个 find(),没有 SOFT DELETED 查询条件

public self withTrashed()

示例

$this->Model->withTrashed()->find();

查询作用域

查询范围允许您为给定模型的查询添加约束。编写自己的全局范围可以提供一种方便、简单的方法,以确保给定模型的每个查询都接收某些约束。SOFT DELETED 范围是一个不包含在全局范围中的自定义范围。

配置

您可以通过重写 _globalScopes 方法来定义您的约束

class My_model extends nueip\Model
{
    protected $userAttribute = 'uid';
    
    /**
     * Override _globalScopes with User validation
     */
    protected function _globalScopes()
    {
        $this->db->where(
            $this->_field($this->userAttribute), 
            $this->config->item('user_id')
            );
        return parent::_globalScopes();
    }

重写后,My_model 将在 find() 的每个查询中约束该范围,除非您在 find 查询之前删除查询范围,如 withoutGlobalScopes()

方法

withoutGlobalScopes()

对于下一个 find(),没有全局范围查询条件

public self withoutGlobalScopes()

示例

$this->Model->withoutGlobalScopes()->find();

withAll()

在进行下一次 find() 查询时,不使用所有查询条件(软删除 & 查询范围)。

也就是说,使用所有模型的数据集进行下一次 find() 查询。

public self withAll()

示例

$this->Model->withAll()->find();

验证

一般来说,你不应该信任来自最终用户的数据,在将其用于实际应用之前,你应该始终对其进行验证。

ORM 模型验证集成了 CodeIgniter 表单验证,它提供了一种一致且顺畅的方式来处理模型数据验证。

验证输入

给定一个包含用户输入的模型,你可以通过调用 validate() 方法来验证输入。该方法将返回一个布尔值,指示验证是否成功。如果不成功,你可以从 getErrors() 方法中获取错误信息。

validate()

执行带有过滤器的数据验证

ORM 只对指定的属性进行验证。

public boolean validate($data=[], $returnData=false)

示例

$this->load->model('PostsModel');

if ($this->PostsModel->validate($inputData)) {
    // all inputs are valid
} else {
    // validation failed: $errors is an array containing error messages
    $errors = $this->PostsModel->getErrors();
}

对于如 insert()update() 之类的修改方法,nueip\Model 的方法也会执行验证。如果你确保输入数据已经过验证,你可以关闭方法中的 $runValidation 参数。

ORM 模型示例

$this->load->model('PostsModel');
$post = new PostsModel;
$post->title = '';
// ORM assigned or modified attributes will be validated by calling `validate()` without parameters
if ($post->validate()) {
    // Already performing `validate()` so that turn false for $runValidation
    $result = $post->save(false);
} else {
    // validation failed: $errors is an array containing error messages
    $errors = post->getErrors();
}

ORM 模型的属性将在验证后通过过滤器进行更改。如果你之前已经调用了 validate(),你可以关闭 save() 中的 $runValidation 以实现不重复验证的保存。

getErrors()

验证 - 获取最后失败验证引用的错误数据

public array getErrors()

声明规则

为了使 validate() 真正起作用,你应该为计划验证的属性声明验证规则。这应该通过覆盖 rules() 方法来完成。

rules()

返回属性的验证规则。

public array rules()

示例

class PostsModel extends nueip\Model
{
    protected $table = "posts";
    
    /**
     * Override rules function with validation rules setting
     */
    public function rules()
    {
        return [
            [
                'field' => 'title',
                'rules' => 'required|min_length[3]',
            ],
        ];
    }
}

验证规则和模式可以参考 CodeIgniter 规则参考

带语言的消息错误

当你处理验证错误消息的 i18n 问题,你可以将 CodeIgniter 语言类 集成到规则中。以下示例代码可供你实现

public function rules()
{
    /**
     * Set CodeIgniter language
     * @see https://www.codeigniter.com/userguide3/libraries/language.html
     */
    $this->lang->load('error_messages', 'en-US');

    return [
        [
            'field' => 'title',
            'rules' => 'required|min_length[3]',
            'errors' => [
                'required' => $this->lang->line('required'),
                'min_length' => $this->lang->line('min_length'),
            ],
        ],
    ];
}

在上面的情况下,语言文件可能是 application/language/en-US/error_messages_lang.php

$lang['required'] = '`%s` is required';
$lang['min_length'] = '`%s` requires at least %d letters';

之后,getErrors() 可以返回当前语言下的字段错误信息。

过滤器

用户输入通常需要过滤或预处理。例如,你可能想要修剪用户名输入周围的空格。你可以通过在 filter() 方法中声明过滤规则来实现这一目标。

在模型的 validate() 过程中,filters 将在 rules() 之前执行,这意味着 rules() 验证的输入数据已经过过滤。

为了使 validate() 中的过滤器生效,你应该为你计划执行的属性声明过滤器。这应该通过覆盖 filters() 方法来完成。

filters()

返回验证的过滤器规则。

public array filters()

示例

public function filters()
{
    return [
        [['title', 'name'], 'trim'],    // Perform `trim()` for title & name input data
        [['title'], 'static::method'],  // Perform `public static function method($value)` in this model
        [['name'], function($value) {   // Perform defined anonymous function. 'value' => '[Filtered]value'
            return "[Filtered]" . $value;
        }],
    ];
}

过滤器的格式:[[['attr1','attr2'], callable],]

读写连接

有时你可能希望为 SELECT 语句使用一个数据库连接,而为 INSERTUPDATEDELETE 语句使用另一个连接。此模型实现了复制和读写分离,确保在使用模型时始终使用数据库连接。

配置

在扩展 nueip\Model 的模型中,可以设置读写连接。你可以在扩展的 My_model 中为每个模型定义读写数据库。

设置读写数据库有三种类型

Codeigniter数据库连接

建议预先准备 CI 数据库连接,你可以在父类构造函数之前在构造函数部分直接将它们分配给属性。

class My_model extends nueip\Model
{
    function __construct()
    {
        $this->database = $this->db;
        
        $this->databaseRead = $this->dbr;
        
        parent::__construct();
    }
}

如果你已经有了 $this->db,它将是两个连接的默认设置。

此设置方式支持重新连接

Codeigniter数据库键

您可以将来自\application\config\database.php的数据库键设置到databasedatabaseRead的模型属性中,设置连接将自动创建。

class My_model extends nueip\Model
{
    protected $database = 'default';
    
    protected $databaseRead = 'slave';
}

此方法支持DB连接的缓存机制,每个模型可以定义自己的连接,但通过键共享相同的连接。

Codeigniter数据库配置数组

这种方式用于在请求周期中与特定模型相关的一次连接数据库,每个模型都会创建一个新的连接。

class My_model extends nueip\Model
{
    protected $databaseRead = [
        'dsn'   => '',
        'hostname' => 'specified_db_host',
        // Database Configuration...
        ];
}

数据库负载均衡

在上面的例子中,您可以设置多个数据库,并实现随机选择读或写数据库的连接。

例如,在application/config/database中配置读数据库

$slaveHosts = ['192.168.1.2', '192.168.1.3'];

$db['slave']['hostname'] = $slaveHosts[mt_rand(0, count($slaveHosts) - 1)];

之后,您可以使用数据库键slave来加载或将其分配给属性

class My_model extends nueip\Model
{
    protected $databaseRead = 'slave';
}

重新连接

如果您想在Codeigniter 3中重新连接数据库以重新建立连接,例如$this->db

$this->db->close();
$this->db->initialize();

在重置引用的数据库连接后,可以重置具有Codeigniter DB Connection设置的模型连接。

不要使用reconnect(),这是一个无用的方法。

悲观锁

模型还包括一些函数,可以帮助您在select语句上执行“悲观锁定”。要使用“共享锁”运行语句,可以使用sharedLock方法来获取查询。共享锁阻止所选行在事务提交之前被修改

$this->Model->find()->where('id', 123);
$result = $this->Model->sharedLock()->row_array();

或者,您可以使用lockForUpdate方法。一个“for update”锁阻止行被修改或被另一个共享锁选择

$this->Model->find()->where('id', 123);
$result = $this->Model->lockForUpdate()->row_array();

示例代码

此事务块将为下一次相同的查询使用FOR UPDATE锁锁定所选行

$this->Model->getDB()->trans_start();
$this->Model->find()->where('id', 123)
$result = $this->Model->lockForUpdate()->row_array();
$this->Model->getDB()->trans_complete(); 

辅助函数

模型提供了一些辅助方法

indexBy()

按键索引

public array indexBy(array & $array, Integer $key=null, Boolean $obj2Array=false)

示例

$records = $this->Model->findAll();
$this->Model->indexBy($records, 'sn');

// Result example of $records:
[
    7 => ['sn'=>7, title=>'Foo'],
    13 => ['sn'=>13, title=>'Bar']
]