CodeIgniter 3 ORM Base Model supported Read & Write Database Connections
1. Elegant patterns as Laravel Eloquent & Yii2 Active Record (ORM is not yet)
2. Codeigniter Query Builder integration
3. Timestamps Behavior & Soft Deleting & Query Scopes support
4. Read & Write Splitting for Replications
This package provide Base Model which extended CI_Model
and provided full CRUD methods to make developing database interactions easier and quicker for your CodeIgniter applications.
$this->load->model('post_model', 'PostModel');
$post = $this->PostModel->findOne(123);
$posts = $this->PostModel->find()
->where('is_public', '1')
->limit(0,25)
->order_by('id')
->get()
->result_array();
$result = $this->PostModel->insert(['title' => 'Codeigniter Model']);
// Find out the record which just be inserted
$post = $this->PostModel->find()
->order_by('id', 'DESC')
->get()
->row_array();
// Update the record
$result = $this->PostModel->update(['title' => 'CI3 Model'], $post['id']);
// Delete the record
$result = $this->PostModel->delete($post['id']);
Run Composer in your Codeigniter project under the folder \application
:
composer require yidas/codeigniter-model
Check Codeigniter application/config/config.php
:
$config['composer_autoload'] = TRUE;
You could customize the vendor path into
$config['composer_autoload']
After installation, yidas\Model
class is ready to use. Simply, you could create a model to extend the yidas\Model
directly:
class Post_model extends yidas\Model {}
After that, this model is ready to use for example: $this->PostModel->findOne(123);
However, the schema of tables such as primary key in your applicaiton may not same as default, and it's annoying to defind repeated schema for each model. We recommend you to make My_model
to extend yidas\Model
instead.
You could use My_model
to extend yidas\Model
, then make each model to extend My_model
in Codeigniter application.
1. Create My_model
extended yidas\Model
with configuration for fitting your common table schema:
class My_model extends yidas\Model
{
protected $primaryKey = 'sn';
const CREATED_AT = 'created_time';
const UPDATED_AT = 'updated_time';
// Customized Configurations for your app...
}
2. Create each Model extended My_model
in application with its own table configuration:
class Post_model extends My_model
{
protected $table = "post_table";
}
3. Use each extended Model with library usages:
$this->load->model('post_model', 'PostModel');
$post = $this->PostModel->findOne(123);
My_model Example with Document
To get started, let's create an model extends yidas\Model
or through My_model
, then define each model suitably.
By convention, the "snake case" with lowercase excluded _model
postfix of the class name will be used as the table name unless another name is explicitly specified. So, in this case, Model will assume the Post_model
model stores records in the post
table. You may specify a custom table by defining a table property on your model:
// class My_model extends yidas\Model
class Post_model extends My_model
{
protected $table = "post_table";
}
In our pattern, The naming between model class and table is the same, with supporting no matter singular or plural names:
Model Class Name | Table Name |
---|---|
Post_model | post |
Posts_model | posts |
User_info_model | user_info |
You may define a protected $primaryKey
property to override this convention:
class My_model extends yidas\Model
{
protected $primaryKey = "sn";
}
By default, Model expects created_at
and updated_at
columns to exist on your tables. If you do not wish to have these columns automatically managed by base Model, set the $timestamps
property on your model as false
:
class My_model extends yidas\Model
{
protected $timestamps = false;
}
If you need to customize the format of your timestamps, set the $dateFormat
property on your model. This property determines how date attributes are stored in the database:
class My_model extends yidas\Model
{
/**
* Date format for timestamps.
*
* @var string unixtime(946684800)|datetime(2000-01-01 00:00:00)
*/
protected $dateFormat = 'datetime';
}
If you need to customize the names of the columns used to store the timestamps, you may set the CREATED_AT
and UPDATED_AT
constants in your model:
class My_model extends yidas\Model
{
const CREATED_AT = 'created_time';
const UPDATED_AT = 'updated_time';
}
Also, you could customized turn timestamps behavior off for specified column by assigning as empty:
class My_model extends yidas\Model
{
const CREATED_AT = 'created_time';
const UPDATED_AT = NULL;
}
Create an CI Query Builder instance with Model Filters for query purpose.
$posts = $this->PostModel->find()
->where('is_public', '1')
->limit(0,25)
->order_by('id')
->get()
->result_array();
// Without any scopes & conditions for this query
$posts = $this->PostModel->find(true)
->where('is_deleted', '1')
->get()
->result_array();
// This is equal to find(true) method
$this->PostModel->withAll()->find();
Return a single record array by a primary key or an array of column values with Model Filters.
$post = $this->PostModel->findOne(123);
Return a list of records that match the specified primary key value(s) or a set of column values with Model Filters.
$post = $this->PostModel->findAll([3,21,135]);
Insert a row with Timestamps feature into the associated database table using the attribute values of this record.
$result = $this->Model->insert([
'name' => 'Nick Tsai',
'email' => 'myintaer@gmail.com',
]);
Insert a batch of rows with Timestamps feature into the associated database table using the attribute values of this record.
$result = $this->Model->batchInsert([
['name' => 'Nick Tsai', 'email' => 'myintaer@gmail.com'],
['name' => 'Yidas', 'email' => 'service@yidas.com']
]);
Replace a row with Timestamps feature into the associated database table using the attribute values of this record.
$result = $this->Model->replace([
'id' => 1,
'name' => 'Nick Tsai',
'email' => 'myintaer@gmail.com',
]);
Save the changes with Timestamps feature to the selected record(s) into the associated database table.
$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']);
Notice: You need to call
update
from Model but not from CI-DB builder chain, the wrong sample code:
$this->Model->find()->where('id', 123)->update('table', ['status'=>'off']);
Delete the selected record(s) with Timestamps feature into the associated database table.
$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);
In addition to actually removing records from your database, This Model can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a deleted_at
attribute could be set on the model and inserted into the database.
You could enable SOFT DELETED feature by giving field name to SOFT_DELETED
:
class My_model extends yidas\Model
{
const SOFT_DELETED = 'is_deleted';
}
While SOFT_DELETED
is enabled, you could set $softDeletedFalseValue
and $softDeletedTrueValue
for fitting table schema. Futher, you may set DELETED_AT
with column name for Timestapes feature, or disabled by setting to NULL
by default:
class My_model extends yidas\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';
}
If you need to disabled SOFT DELETED feature for specified model, you may set SOFT_DELETED
to false
, which would disable any SOFT DELETED functions including DELETED_AT
feature:
// class My_model extends yidas\Model
class Log_model extends My_model
{
const SOFT_DELETED = false;
}
Force Delete the selected record(s) with Timestamps feature into the associated database table.
$result = $this->Model->forceDelete(123)
// Query builder ORM usage
$this->Model->find()->where('id', 123);
$result = $this->Model->forceDelete();
Restore SOFT_DELETED field value to the selected record(s) into the associated database table..
$result = $this->Model->restore(123)
// Query builder ORM usage
$this->Model->withTrashed()->find()->where('id', 123);
$this->Model->restore();
Without SOFT DELETED query conditions for next find()
$this->Model->withTrashed()->find();
Query scopes allow you to add constraints to all queries for a given model. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints. The SOFT DELETED scope is a own scope which is not includes in global scope.
You could override _globalScopes
method to define your constraints:
class My_model extends yidas\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();
}
After overriding that, the My_model
will constrain that scope in every query from find()
, unless you remove the query scope before a find query likes withoutGlobalScopes()
.
Without Global Scopes query conditions for next find()
$this->Model->withoutGlobalScopes()->find();
Without all query conditions (SOFT DELETED & QUERY SCOPES) for next find()
That is, with all data set of Models for next find()
$this->Model->withAll()->find();
Sometimes you may wish to use one database connection for SELECT
statements, and another for INSERT
, UPDATE
, and DELETE
statements. This Model implements Replication and Read-Write Splitting, makes database connections will always be used while using Model usages.
Read & Write Connections could be set in the model which extends yidas\Model
, you could defind the read & write databases in extended My_model
for every models.
There are three types to set read & write databases:
You could set the database key refered from \application\config\database.php
into model attributes of database
& databaseRead
, the setting connections would be created automatically:
class My_model extends yidas\Model
{
protected $database = 'default';
protected $databaseRead = 'slave';
}
This method supports cache mechanism for DB connections, each model could define its own connections but share the same connection by key.
If you already have prepared CI DB connections, you could assign to attributes directly in construct section before parent's constrcut:
class My_model extends yidas\Model
{
function __construct()
{
$this->database = $this->db;
$this->databaseRead = $this->dbr;
parent::__construct();
}
}
This way is used for the specified model related to the one time connected database in a request cycle, which would create a new connection per each model:
class My_model extends yidas\Model
{
protected $databaseRead = [
'dsn' => '',
'hostname' => 'specified_db_host',
// Database Configuration...
];
}
In above case, you could set multiple databases and implement random selected connection for Read or Write Databases.
For example, configuring read databases in application/config/database
:
$slaveHosts = ['192.168.1.2', '192.168.1.3'];
$db['slave']['hostname'] = $slaveHosts[mt_rand(0, count($slaveHosts) - 1)];
After that, you could use database key slave
to load or assign it to attribute:
class My_model extends yidas\Model
{
protected $databaseRead = 'slave';
}