{"id":13609574,"url":"https://github.com/yidas/codeigniter-model","last_synced_at":"2025-04-04T20:15:47.010Z","repository":{"id":45198581,"uuid":"100226908","full_name":"yidas/codeigniter-model","owner":"yidas","description":"CodeIgniter 3 Active Record (ORM) Standard Model with Laravel Eloquent \u0026 Yii2 AR like","archived":false,"fork":false,"pushed_at":"2024-04-25T06:22:52.000Z","size":147,"stargazers_count":170,"open_issues_count":5,"forks_count":49,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-28T19:11:22.962Z","etag":null,"topics":["codeigniter3","model","orm","query-builder","query-scopes","read-write-splitting","soft-delete"],"latest_commit_sha":null,"homepage":"https://codeigniter.com/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yidas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-14T04:04:09.000Z","updated_at":"2025-03-23T17:45:45.000Z","dependencies_parsed_at":"2024-01-13T12:52:45.274Z","dependency_job_id":"9f9d6319-7c48-47a4-acf2-a68fdbee8584","html_url":"https://github.com/yidas/codeigniter-model","commit_stats":{"total_commits":99,"total_committers":5,"mean_commits":19.8,"dds":0.0505050505050505,"last_synced_commit":"abda725de9e20ecb451c02208b1da28785f2d18e"},"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yidas%2Fcodeigniter-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yidas%2Fcodeigniter-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yidas%2Fcodeigniter-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yidas%2Fcodeigniter-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yidas","download_url":"https://codeload.github.com/yidas/codeigniter-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242681,"owners_count":20907134,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codeigniter3","model","orm","query-builder","query-scopes","read-write-splitting","soft-delete"],"created_at":"2024-08-01T19:01:36.099Z","updated_at":"2025-04-04T20:15:46.992Z","avatar_url":"https://github.com/yidas.png","language":"PHP","readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://codeigniter.com/userguide3/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://codeigniter.com/assets/icons/ci-logo.png\" height=\"60\"\u003e\n    \u003c/a\u003e\n    \u003ch1 align=\"center\"\u003eCodeIgniter Model\u003c/h1\u003e\n    \u003cbr\u003e\n\u003c/p\u003e\n\nCodeIgniter 3 Active Record (ORM) Standard Model supported Read \u0026 Write Connections\n\n[![Latest Stable Version](https://poser.pugx.org/yidas/codeigniter-model/v/stable?format=flat-square)](https://packagist.org/packages/yidas/codeigniter-model)\n[![License](https://poser.pugx.org/yidas/codeigniter-model/license?format=flat-square)](https://packagist.org/packages/yidas/codeigniter-model)\n\nThis ORM Model extension is collected into [yidas/codeigniter-pack](https://github.com/yidas/codeigniter-pack) which is a complete solution for Codeigniter framework.\n\nFEATURES\n--------\n\n- ***[ORM](#active-record-orm)** Model with **Elegant patterns** as Laravel Eloquent ORM \u0026 Yii2 Active Record*\n\n- ***[CodeIgniter Query Builder](#find)** integration*\n\n- ***[Timestamps Behavior](#timestamps)** \u0026 **[Validation](#validation)** \u0026 **[Soft Deleting](#soft-deleted)** \u0026 **[Query Scopes](#query-scopes)** support*\n\n- ***[Read \u0026 Write Splitting](#read--write-connections)** for Replications*\n\nThis 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.\n\nOUTLINE\n-------\n\n- [Demonstration](#demonstration)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Defining Models](#defining-models)\n  - [Table Names](#table-names)\n  - [Primary Keys](#primary-keys)\n  - [Timestamps](#timestamps)\n  - [Database Connection](#database-connection)\n  - [Other settings](#other-settings)\n- [Basic Usage](#basic-usage)\n  - [Methods](#methods)\n    - [find()](#find)\n      - [Query Builder Implementation](#query-builder-implementation)\n    - [reset()](#reset)\n    - [insert()](#insert)\n    - [batchInsert()](#batchinsert)\n    - [update()](#update)\n    - [batchUpdate()](#batchupdate)\n    - [replace()](#replace)\n    - [delete()](#delete)\n    - [getLastInsertID()](#getlastinsertid)\n    - [getAffectedRows()](#getaffectedrows)\n    - [count()](#count)\n    - [setAlias()](#setalias)\n- [Active Record (ORM)](#active-record-orm)\n  - [Inserts](#inserts)\n  - [Updates](#updates)\n  - [Deletes](#deletes)\n  - [Accessing Data](#accessing-data)\n  - [Relationships](#relationships)\n  - [Methods](#methods-1)\n    - [findone()](#findone)\n    - [findAll()](#findall)\n    - [save()](#save)\n    - [beforeSave()](#beforesave)\n    - [afterSave()](#afterave)\n    - [hasOne()](#hasone)\n    - [hasMany()](#hasmany)\n    - [toArray()](#toarray)\n- [Soft Deleted](#soft-deleted)\n  - [Configuration](#configuration-1)\n  - [Methods](#method-2)\n- [Query Scopes](#query-scopes)\n  - [Configuration](#configuration-2)\n  - [Methods](#method-3)\n- [Validation](#validation)\n  - [Validating Input](#validating-input)\n    - [validate()](#validate)\n    - [getErrors()](#geterrors)\n  - [Declaring Rules](#declaring-rules)\n    - [rules()](#rules)\n    - [Error Message with Language](#error-message-with-language)\n  - [Filters](#filters)\n    - [Filters()](#filters-1)\n- [Read \u0026 Write Connections](#read--write-connections)\n  - [Configuration](#configuration-3)\n  - [Load Balancing for Databases](#load-balancing-for-databases)\n  - [Reconnection](#reconnection)\n- [Pessimistic Locking](#pessimistic-locking)\n- [Helpers](#helpers)\n  - [indexBy()](#indexby)\n  \n---\n\nDEMONSTRATION\n-------------\n\n### ActiveRecord (ORM)\n\n```php\n$this-\u003eload-\u003emodel('Posts_model');\n\n// Create an Active Record\n$post = new Posts_model;\n$post-\u003etitle = 'CI3'; // Equivalent to `$post['title'] = 'CI3';`\n$post-\u003esave();\n\n// Update the Active Record found by primary key\n$post = $this-\u003ePosts_model-\u003efindOne(1);\nif ($post) {\n    $oldTitle = $post-\u003etitle; // Equivalent to `$oldTitle = $post['title'];`\n    $post-\u003etitle = 'New CI3';\n    $post-\u003esave();\n}\n```\n\n\u003e The pattern is similar to [Yii2 Active Record](https://www.yiiframework.com/doc/guide/2.0/en/db-active-record#active-record) and [Laravel Eloquent](https://laravel.com/docs/5.8/eloquent#inserting-and-updating-models)\n\n### Find with Query Builder\n\nStart to use CodeIgniter Query Builder from `find()` method, the Model will automatically load its own database connections and data tables.\n\n```php\n$records = $this-\u003ePosts_model-\u003efind()\n    -\u003eselect('*')\n    -\u003ewhere('is_public', '1')\n    -\u003elimit(25)\n    -\u003eorder_by('id')\n    -\u003eget()\n    -\u003eresult_array();\n```\n\n### CRUD\n\n```php\n$result = $this-\u003ePosts_model-\u003einsert(['title' =\u003e 'Codeigniter Model']);\n\n// Find out the record which just be inserted\n$record = $this-\u003ePosts_model-\u003efind()\n  -\u003eorder_by('id', 'DESC')\n  -\u003eget()\n  -\u003erow_array();\n  \n// Update the record\n$result = $this-\u003ePosts_model-\u003eupdate(['title' =\u003e 'CI3 Model'], $record['id']);\n\n// Delete the record\n$result = $this-\u003ePosts_model-\u003edelete($record['id']);\n```\n\n---\n\nREQUIREMENTS\n------------\n\nThis library requires the following:\n\n- PHP 5.4.0+\n- CodeIgniter 3.0.0+\n\n---\n\nINSTALLATION\n------------\n\nRun Composer in your Codeigniter project under the folder `\\application`:\n\n    composer require yidas/codeigniter-model\n    \nCheck Codeigniter `application/config/config.php`:\n\n```php\n$config['composer_autoload'] = TRUE;\n```\n    \n\u003e You could customize the vendor path into `$config['composer_autoload']`\n\n---\n\nCONFIGURATION\n-------------\n\nAfter installation, `yidas\\Model` class is ready to use. Simply, you could create a model to extend the `yidas\\Model` directly:\n\n```php\nclass Post_model extends yidas\\Model {}\n```\n\nAfter that, this model is ready to use for example: `$this-\u003ePostModel-\u003efindOne(123);`\n\nHowever, 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.\n\n### Use My_model to Extend Base Model for every Models\n\nYou could use `My_model` to extend `yidas\\Model`, then make each model to extend `My_model` in Codeigniter application.\n\n*1. Create `My_model` extended `yidas\\Model` with configuration for fitting your common table schema:*\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $primaryKey = 'sn';\n    const CREATED_AT = 'created_time';\n    const UPDATED_AT = 'updated_time';\n    // Customized Configurations for your app...\n}\n```\n\n*2. Create each Model extended `My_model` in application with its own table configuration:*\n\n```php\nclass Post_model extends My_model\n{\n    protected $table = \"post_table\";\n}\n```\n\n*3. Use each extended Model with library usages:*\n\n```php\n$this-\u003eload-\u003emodel('post_model', 'PostModel');\n\n$post = $this-\u003ePostModel-\u003efindOne(123);\n```\n\n[My_model Example with Document](https://github.com/yidas/codeigniter-model/tree/master/example)\n\n---\n\nDEFINING MODELS\n---------------\n\nTo get started, let's create an model extends `yidas\\Model` or through `My_model`, then define each model suitably.\n\n### Table Names\n\nBy 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:\n\n```php\n// class My_model extends yidas\\Model\nclass Post_model extends My_model\n{\n    protected $table = \"post_table\";\n}\n```\n\n\u003e You could set table alias by defining `protected $alias = 'A1';` for model.\n\n#### Table Name Guessing Rule\n\nIn our pattern, The naming between model class and table is the same, with supporting no matter singular or plural names:\n\n|Model Class Name|Table Name|\n|--|--|\n|Post_model|post|\n|Posts_model|posts|\n|User_info_model|user_info|\n\n#### Get Table Name\n\nYou could get table name from each Model:\n\n```php\n$tableName = $this-\u003ePostModel-\u003egetTable();\n```\n\n\n\n### Primary Keys\n\nYou may define a protected `$primaryKey` property to override this convention:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $primaryKey = \"sn\";\n}\n```\n\n\u003e Correct primary key setting of Model is neceesary for Active Record (ORM). \n\n### Timestamps\n\nBy 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`:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $timestamps = false;\n}\n```\n\nIf 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:\n\n```php\nclass My_model extends yidas\\Model\n{\n    /**\n     * Date format for timestamps.\n     *\n     * @var string unixtime(946684800)|datetime(2000-01-01 00:00:00)\n     */\n    protected $dateFormat = 'datetime';\n}\n```\n\nIf 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:\n\n```php\nclass My_model extends yidas\\Model\n{\n    const CREATED_AT = 'created_time';\n    const UPDATED_AT = 'updated_time';\n}\n```\n\nAlso, you could customized turn timestamps behavior off for specified column by assigning as empty:\n\n```php\nclass My_model extends yidas\\Model\n{\n    const CREATED_AT = 'created_time';\n    const UPDATED_AT = NULL;\n}\n```\n\n### Database Connection\n\nBy default, all models will use the default database connection `$this-\u003edb` configured for your application. If you would like to specify a different connection for the model, use the `$database` property:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $database = 'database2';\n}\n```\n\n\u003e More Database Connection settings: [Read \u0026 Write Connections](#read--write-connections)\n\n\n### Other settings\n\n```php\nclass My_model extends yidas\\Model\n{\n    // Enable ORM property check for write\n    protected $propertyCheck = true;\n}\n```\n\n---\n\nBASIC USAGE\n-----------\n\nAbove usage examples are calling Models out of model, for example in controller:\n\n```php\n$this-\u003eload-\u003emodel('post_model', 'Model');\n```\n\nIf you call methods in Model itself, just calling `$this` as model. For example, `$this-\u003efind()...` for `find()`;\n\n### Methods\n\n#### `find()`\n\nCreate an existent CI Query Builder instance with Model features for query purpose.\n\n```php\npublic CI_DB_query_builder find(boolean $withAll=false)\n```\n\n*Example:*\n```php\n$records = $this-\u003eModel-\u003efind()\n    -\u003eselect('*')\n    -\u003ewhere('is_public', '1')\n    -\u003elimit(25)\n    -\u003eorder_by('id')\n    -\u003eget()\n    -\u003eresult_array();\n```\n\n```php\n// Without any scopes \u0026 conditions for this query\n$records = $this-\u003eModel-\u003efind(true)\n    -\u003ewhere('is_deleted', '1')\n    -\u003eget()\n    -\u003eresult_array();\n    \n// This is equal to find(true) method\n$this-\u003eModel-\u003ewithAll()-\u003efind();\n```\n\n\u003e After starting `find()` from a model, it return original `CI_DB_query_builder` for chaining. The query builder could refer [CodeIgniter Query Builder Class Document](https://www.codeigniter.com/userguide3/database/query_builder.html)\n\n##### Query Builder Implementation\n\nYou could assign Query Builder as a variable to handle add-on conditions instead of using `$this-\u003eModel-\u003egetBuilder()`.\n\n```php\n$queryBuilder = $this-\u003eModel-\u003efind();\nif ($filter) {\n    $queryBuilder-\u003ewhere('filter', $filter);\n}\n$records = $queryBuilder-\u003eget()-\u003eresult_array();\n```\n\n#### `reset()`\n\nreset an CI Query Builder instance with Model.\n\n```php\npublic self reset()\n```\n\n*Example:*\n```php\n$this-\u003eModel-\u003ereset()-\u003efind();\n```\n\n#### `insert()`\n\nInsert a row with Timestamps feature into the associated database table using the attribute values of this record.\n\n```php\npublic boolean insert(array $attributes, $runValidation=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003einsert([\n    'name' =\u003e 'Nick Tsai',\n    'email' =\u003e 'myintaer@gmail.com',\n]);\n```\n\n#### `batchInsert()`\n\nInsert a batch of rows with Timestamps feature into the associated database table using the attribute values of this record.\n\n```php\npublic integer batchInsert(array $data, $runValidation=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003ebatchInsert([\n     ['name' =\u003e 'Nick Tsai', 'email' =\u003e 'myintaer@gmail.com'],\n     ['name' =\u003e 'Yidas', 'email' =\u003e 'service@yidas.com']\n]);\n```\n\n#### `replace()`\n\nReplace a row with Timestamps feature into the associated database table using the attribute values of this record.\n\n```php\npublic boolean replace(array $attributes, $runValidation=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003ereplace([\n    'id' =\u003e 1,\n    'name' =\u003e 'Nick Tsai',\n    'email' =\u003e 'myintaer@gmail.com',\n]);\n```\n\n#### `update()`\n\nSave the changes with Timestamps feature to the selected record(s) into the associated database table.\n\n```php\npublic boolean update(array $attributes, array|string $condition=NULL, $runValidation=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003eupdate(['status'=\u003e'off'], 123)\n```\n\n```php\n// Find conditions first then call again\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003eupdate(['status'=\u003e'off']);\n```\n\n```php\n// Counter set usage equal to `UPDATE mytable SET count = count+1 WHERE id = 123`\n$this-\u003eModel-\u003egetDB()-\u003eset('count','count + 1', FALSE);\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003eupdate([]);\n```\n\n\u003e Notice: You need to call `update` from Model but not from CI-DB builder chain, the wrong sample code: \n\u003e \n\u003e `$this-\u003eModel-\u003efind()-\u003ewhere('id', 123)-\u003eupdate('table', ['status'=\u003e'off']);`\n\n#### `batchUpdate()`\n\nUpdate a batch of update queries into combined query strings.\n\n```php\npublic integer batchUpdate(array $dataSet, boolean $withAll=false, interger $maxLength=4*1024*1024, $runValidation=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003ebatchUpdate([\n    [['title'=\u003e'A1', 'modified'=\u003e'1'], ['id'=\u003e1]],\n    [['title'=\u003e'A2', 'modified'=\u003e'1'], ['id'=\u003e2]],\n]);\n```\n\n#### `delete()`\n\nDelete the selected record(s) with Timestamps feature into the associated database table.\n\n```php\npublic boolean delete(array|string $condition=NULL, boolean $forceDelete=false, array $attributes=[])\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003edelete(123)\n```\n\n```php\n// Find conditions first then call again\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003edelete();\n```\n\n```php\n// Force delete for SOFT_DELETED mode \n$this-\u003eModel-\u003edelete(123, true);\n```\n\n#### `getLastInsertID()`\n\nGet the insert ID number when performing database inserts.\n\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003einsert(['name' =\u003e 'Nick Tsai']);\n$lastInsertID = $this-\u003eModel-\u003egetLastInsertID();\n```\n\n#### `getAffectedRows()`\n\nGet the number of affected rows when doing “write” type queries (insert, update, etc.).\n\n```php\npublic integer|string getLastInsertID()\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003eupdate(['name' =\u003e 'Nick Tsai'], 32);\n$affectedRows = $this-\u003eModel-\u003egetAffectedRows();\n```\n\n#### `count()`\n\nGet count from query\n\n```php\npublic integer count(boolean $resetQuery=true)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003efind()-\u003ewhere(\"age \u003c\", 20);\n$totalCount = $this-\u003eModel-\u003ecount();\n```\n\n#### `setAlias()`\n\nSet table alias\n\n```php\npublic self setAlias(string $alias)\n```\n\n*Example:*\n```php\n$query = $this-\u003eModel-\u003esetAlias(\"A1\")\n    -\u003efind()\n    -\u003ejoin('table2 AS A2', 'A1.id = A2.id');\n```\n\n---\n\nACTIVE RECORD (ORM)\n-------------------\n\nActive Record provides an object-oriented interface for accessing and manipulating data stored in databases. An Active Record Model class is associated with a database table, an Active Record instance corresponds to a row of that table, and an attribute of an Active Record instance represents the value of a particular column in that row.\n\n\u003e Active Record (ORM) supported events such as timestamp for insert and update.\n\n### Inserts\n\nTo create a new record in the database, create a new model instance, set attributes on the model, then call the `save` method:\n\n```php\n$this-\u003eload-\u003emodel('Posts_model');\n\n$post = new Posts_model;\n$post-\u003etitle = 'CI3';\n$result = $post-\u003esave();\n```\n\n### Updates\n\nThe `save` method may also be used to update models that already exist in the database. To update a model, you should retrieve it, set any attributes you wish to update, and then call the `save` method:\n\n```php\n$this-\u003eload-\u003emodel('Posts_model');\n\n$post = $this-\u003ePosts_model-\u003efindOne(1);\nif ($post) {\n    $post-\u003etitle = 'New CI3';\n    $result = $post-\u003esave();\n}\n```\n\n### Deletes\n\nTo delete a active record, call the `delete` method on a model instance:\n\n```php\n$this-\u003eload-\u003emodel('Posts_model');\n\n$post = $this-\u003ePosts_model-\u003efindOne(1);\n$result = $post-\u003edelete();\n```\n\n\u003e `delete()` supports soft deleted and points to self if is Active Record.\n\n### Accessing Data\n\nYou could access the column values by accessing the attributes of the Active Record instances likes `$activeRecord-\u003eattribute`, or get by array key likes `$activeRecord['attribute']`.\n\n```php\n$this-\u003eload-\u003emodel('Posts_model');\n\n// Set attributes\n$post = new Posts_model;\n$post-\u003etitle = 'CI3';\n$post['subtitle'] = 'PHP';\n$post-\u003esave();\n\n// Get attributes\n$post = $this-\u003ePosts_model-\u003efindOne(1);\n$title = $post-\u003etitle;\n$subtitle = $post['subtitle'];\n```\n\n### Relationships\n\nDatabase tables are often related to one another. For example, a blog post may have many comments, or an order could be related to the user who placed it. This library makes managing and working with these relationships easy, and supports different types of relationships:\n\n- [One To One](#hasone)\n- [One To Many](#hasmany)\n\nTo work with relational data using Active Record, you first need to declare relations in models. The task is as simple as declaring a `relation method` for every interested relation, like the following,\n\n```php\nclass CustomersModel extends yidas\\Model\n{\n    // ...\n\n    public function orders()\n    {\n        return $this-\u003ehasMany('OrdersModel', ['customer_id' =\u003e 'id']);\n    }\n}\n```\n\nOnce the relationship is defined, we may retrieve the related record using dynamic properties. Dynamic properties allow you to access relationship methods as if they were properties defined on the model:\n\n```php\n$orders = $this-\u003eCustomersModel-\u003efindOne(1)-\u003eorders;\n```\n\n\u003e The dynamic properties' names are same as methods' names, like [Laravel Eloquent](https://laravel.com/docs/5.7/eloquent-relationships)\n\nFor **Querying Relations**, You may query the `orders` relationship and add additional constraints with CI Query Builder to the relationship like so:\n\n```php\n$customer = $this-\u003eCustomersModel-\u003efindOne(1)\n\n$orders = $customer-\u003eorders()-\u003ewhere('active', 1)-\u003eget()-\u003eresult_array();\n```\n\n### Methods\n\n#### `findOne()`\n\nReturn a single active record model instance by a primary key or an array of column values.\n\n```php\npublic object findOne(array $condition=[])\n```\n\n*Example:*\n```php\n// Find a single active record whose primary key value is 10\n$activeRecord = $this-\u003eModel-\u003efindOne(10);\n\n// Find the first active record whose type is 'A' and whose status is 1\n$activeRecord = $this-\u003eModel-\u003efindOne(['type' =\u003e 'A', 'status' =\u003e 1]);\n\n// Query builder ORM usage\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 10);\n$activeRecord = $this-\u003eModel-\u003efindOne();\n```\n\n#### `findAll()`\n\nReturns a list of active record models that match the specified primary key value(s) or a set of column values.\n\n```php\npublic array findAll(array $condition=[], integer|array $limit=null)\n```\n\n*Example:*\n```php\n// Find the active records whose primary key value is 10, 11 or 12.\n$activeRecords = $this-\u003eModel-\u003efindAll([10, 11, 12]);\n\n// Find the active recordd whose type is 'A' and whose status is 1\n$activeRecords = $this-\u003eModel-\u003efindAll(['type' =\u003e 'A', 'status' =\u003e 1]);\n\n// Query builder ORM usage\n$this-\u003eModel-\u003efind()-\u003ewhere_in('id', [10, 11, 12]);\n$activeRecords = $this-\u003eModel-\u003efindAll();\n\n// Print all properties for each active record from array\nforeach ($activeRecords as $activeRecord) {\n    print_r($activeRecord-\u003etoArray());\n}\n```\n\n*Example of limit:*\n```php\n// LIMIT 10\n$activeRecords = $this-\u003eModel-\u003efindAll([], 10);\n\n// OFFSET 50, LIMIT 10\n$activeRecords = $this-\u003eModel-\u003efindAll([], [50, 10]);\n```\n\n#### `save()`\n\nActive Record (ORM) save for insert or update\n\n```php\npublic boolean save(boolean $runValidation=true)\n```\n\n#### `beforeSave()`\n\nThis method is called at the beginning of inserting or updating a active record\n\n\n```php\npublic boolean beforeSave(boolean $insert)\n```\n\n*Example:*\n```\npublic function beforeSave($insert)\n{\n    if (!parent::beforeSave($insert)) {\n        return false;\n    }\n\n    // ...custom code here...\n    return true;\n}\n```\n\n#### `afterSave()`\n\nThis method is called at the end of inserting or updating a active record\n\n\n```php\npublic boolean beforeSave(boolean $insert, array $changedAttributes)\n```\n\n#### `hasOne()`\n\nDeclares a has-one relation\n\n\n```php\npublic CI_DB_query_builder hasOne(string $modelName, string $foreignKey=null, string $localKey=null)\n```\n\n*Example:*\n```php\nclass OrdersModel extends yidas\\Model\n{\n    // ...\n    \n    public function customer()\n    {\n        return $this-\u003ehasOne('CustomersModel', 'id', 'customer_id');\n    }\n}\n```\n*Accessing Relational Data:*\n```php\n$this-\u003eload-\u003emodel('OrdersModel');\n// SELECT * FROM `orders` WHERE `id` = 321\n$order = $this-\u003eOrdersModel-\u003efindOne(321);\n\n// SELECT * FROM `customers` WHERE `customer_id` = 321\n// $customer is a Customers active record\n$customer = $order-\u003ecustomer;\n```\n\n#### `hasMany()`\n\nDeclares a has-many relation\n\n\n```php\npublic CI_DB_query_builder hasMany(string $modelName, string $foreignKey=null, string $localKey=null)\n```\n\n*Example:*\n```php\nclass CustomersModel extends yidas\\Model\n{\n    // ...\n    \n    public function orders()\n    {\n        return $this-\u003ehasMany('OrdersModel', 'customer_id', 'id');\n    }\n}\n```\n*Accessing Relational Data:*\n```php\n$this-\u003eload-\u003emodel('CustomersModel');\n// SELECT * FROM `customers` WHERE `id` = 123\n$customer = $this-\u003eCustomersModel-\u003efindOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders is an array of Orders active records\n$orders = $customer-\u003eorders;\n```\n\n#### `toArray()`\n\nActive Record transform to array record\n\n```php\npublic array toArray()\n```\n\n*Example:*\n```\nif ($activeRecord)\n    $record = $activeRecord-\u003etoArray();\n```\n\n\u003e It's recommended to use find() with CI builder instead of using ORM and turning it to array.\n\n---\n\nSOFT DELETED\n------------\n\nIn 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.\n\n### Configuration\n\nYou could enable SOFT DELETED feature by giving field name to `SOFT_DELETED`:\n\n```php\nclass My_model extends yidas\\Model\n{\n    const SOFT_DELETED = 'is_deleted';\n}\n```\n\nWhile `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:\n\n```php\nclass My_model extends yidas\\Model\n{\n    const SOFT_DELETED = 'is_deleted';\n    \n    // The actived value for SOFT_DELETED\n    protected $softDeletedFalseValue = '0';\n    \n    // The deleted value for SOFT_DELETED\n    protected $softDeletedTrueValue = '1';\n\n    const DELETED_AT = 'deleted_at';\n}\n```\n\nIf 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:\n\n```php\n// class My_model extends yidas\\Model\nclass Log_model extends My_model\n{\n    const SOFT_DELETED = false;\n}\n```\n\n### Methods\n\n#### `forceDelete()`\n\nForce Delete the selected record(s) with Timestamps feature into the associated database table.\n\n```php\npublic boolean forceDelete($condition=null)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003eforceDelete(123)\n```\n\n```php\n// Query builder ORM usage\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003eforceDelete();\n```\n\n#### `restore()`\n\nRestore SOFT_DELETED field value to the selected record(s) into the associated database table.\n\n```php\npublic boolean restore($condition=null)\n```\n\n*Example:*\n```php\n$result = $this-\u003eModel-\u003erestore(123)\n```\n\n```php\n// Query builder ORM usage\n$this-\u003eModel-\u003ewithTrashed()-\u003efind()-\u003ewhere('id', 123);\n$this-\u003eModel-\u003erestore();\n```\n\n#### `withTrashed()`\n\nWithout [SOFT DELETED](#soft-deleted) query conditions for next `find()`\n\n```php\npublic self withTrashed()\n```\n\n*Example:*\n```php\n$this-\u003eModel-\u003ewithTrashed()-\u003efind();\n```\n\n\n---\n\nQUERY SCOPES\n------------\n\nQuery 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](#soft-deleted) scope is a own scope which is not includes in global scope.\n\n### Configuration\n\nYou could override `_globalScopes` method to define your constraints:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $userAttribute = 'uid';\n    \n    /**\n     * Override _globalScopes with User validation\n     */\n    protected function _globalScopes()\n    {\n        $this-\u003edb-\u003ewhere(\n            $this-\u003e_field($this-\u003euserAttribute), \n            $this-\u003econfig-\u003eitem('user_id')\n            );\n        return parent::_globalScopes();\n    }\n```\n\nAfter 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()`.\n\n### Methods\n\n#### `withoutGlobalScopes()`\n\nWithout Global Scopes query conditions for next find()\n\n```php\npublic self withoutGlobalScopes()\n```\n\n*Example:*\n```php\n$this-\u003eModel-\u003ewithoutGlobalScopes()-\u003efind();\n```\n\n#### `withAll()`\n\nWithout all query conditions ([SOFT DELETED](#soft-deleted) \u0026 [QUERY SCOPES](#query-scope)) for next `find()`\n\nThat is, with all data set of Models for next `find()`\n\n```php\npublic self withAll()\n```\n\n*Example:*\n```php\n$this-\u003eModel-\u003ewithAll()-\u003efind();\n```\n---\n\nVALIDATION\n----------\n\nAs a rule of thumb, you should never trust the data received from end users and should always validate it before putting it to good use.\n\nThe ORM Model validation integrates [CodeIgniter Form Validation](https://www.codeigniter.com/userguide3/libraries/form_validation.html) that provides consistent and smooth way to deal with model data validation.\n\n### Validating Input\n\nGiven a model populated with user inputs, you can validate the inputs by calling the `validate()` method. The method will return a boolean value indicating whether the validation succeeded or not. If not, you may get the error messages from `getErrors()` method.\n\n#### `validate()`\n\nPerforms the data validation with filters\n\n\u003e ORM only performs validation for assigned properties.\n\n```php\npublic boolean validate($data=[], $returnData=false)\n```\n\n*Exmaple:*\n\n```php\n$this-\u003eload-\u003emodel('PostsModel');\n\nif ($this-\u003ePostsModel-\u003evalidate($inputData)) {\n    // all inputs are valid\n} else {\n    // validation failed: $errors is an array containing error messages\n    $errors = $this-\u003ePostsModel-\u003egetErrors();\n}\n```\n\n\u003e The methods of `yidas\\Model` for modifying such as `insert()` and `update()` will also perform validation. You can turn off `$runValidation` parameter of methods if you ensure that the input data has been validated.\n\n*Exmaple of ORM Model:*\n\n```php\n$this-\u003eload-\u003emodel('PostsModel');\n$post = new PostsModel;\n$post-\u003etitle = '';\n// ORM assigned or modified attributes will be validated by calling `validate()` without parameters\nif ($post-\u003evalidate()) {\n    // Already performing `validate()` so that turn false for $runValidation\n    $result = $post-\u003esave(false);\n} else {\n    // validation failed: $errors is an array containing error messages\n    $errors = post-\u003egetErrors();\n}\n```\n\n\u003e A ORM model's properties will be changed by filter after performing validation. If you have previously called `validate()`.\nYou can turn off `$runValidation` of `save()` for saving without repeated validation.\n\n### getErrors()\n\nValidation - Get error data referenced by last failed Validation\n\n```php\npublic array getErrors()\n```\n\n### Declaring Rules\n\nTo make `validate()` really work, you should declare validation rules for the attributes you plan to validate. This should be done by overriding the `rules()` method with returning [CodeIgniter Rules](https://www.codeigniter.com/userguide3/libraries/form_validation.html#setting-rules-using-an-array).\n\n#### `rules()`\n\nReturns the validation rules for attributes.\n\n```php\npublic array rules()\n```\n\n*Example:*\n\n```php\nclass PostsModel extends yidas\\Model\n{\n    protected $table = \"posts\";\n    \n    /**\n     * Override rules function with validation rules setting\n     */\n    public function rules()\n    {\n        return [\n            [\n                'field' =\u003e 'title',\n                'rules' =\u003e 'required|min_length[3]',\n            ],\n        ];\n    }\n}\n```\n\n\u003e The returning array format could refer [CodeIgniter - Setting Rules Using an Array](https://www.codeigniter.com/userguide3/libraries/form_validation.html#setting-rules-using-an-array), and the rules pattern could refer [CodeIgniter - Rule Reference](https://www.codeigniter.com/userguide3/libraries/form_validation.html#rule-reference)\n\n#### Error Message with Language\n\nWhen you are dealing with i18n issue of validation's error message, you can integrate [CodeIgniter language class](https://www.codeigniter.com/userguide3/libraries/language.html) into rules. The following sample code is available for you to implement:\n\n```php\npublic function rules()\n{\n    /**\n     * Set CodeIgniter language\n     * @see https://www.codeigniter.com/userguide3/libraries/language.html\n     */\n    $this-\u003elang-\u003eload('error_messages', 'en-US');\n\n    return [\n        [\n            'field' =\u003e 'title',\n            'rules' =\u003e 'required|min_length[3]',\n            'errors' =\u003e [\n                'required' =\u003e $this-\u003elang-\u003eline('required'),\n                'min_length' =\u003e $this-\u003elang-\u003eline('min_length'),\n            ],\n        ],\n    ];\n}\n```\n\nIn above case, the language file could be `application/language/en-US/error_messages_lang.php`:\n\n```php\n$lang['required'] = '`%s` is required';\n$lang['min_length'] = '`%s` requires at least %d letters';\n```\n\nAfter that, the `getErrors()` could returns field error messages with current language.\n\n### Filters\n\nUser inputs often need to be filtered or preprocessed. For example, you may want to trim the spaces around the username input. You may declare filter rules in `filter()` method to achieve this goal.\n\n\u003e In model's `validate()` process, the `filters()` will be performed before [`rules()`](#declaring-rules), which means the input data validated by [`rules()`](#declaring-rules) is already be filtered.\n\nTo enable filters for `validate()`, you should declare filters for the attributes you plan to perform. This should be done by overriding the `filters()` method.\n\n#### `filters()`\n\nReturns the filter rules for validation.\n\n```php\npublic array filters()\n```\n\n*Example:*\n\n```php\npublic function filters()\n{\n    return [\n        [['title', 'name'], 'trim'],    // Perform `trim()` for title \u0026 name input data\n        [['title'], 'static::method'],  // Perform `public static function method($value)` in this model\n        [['name'], function($value) {   // Perform defined anonymous function. 'value' =\u003e '[Filtered]value'\n            return \"[Filtered]\" . $value;\n        }],\n        [['content'], [$this-\u003esecurity, 'xss_clean']], // Perform CodeIgniter XSS Filtering for content input data\n    ];\n}\n```\n\n\u003e The filters format: `[[['attr1','attr2'], callable],]`\n\n\n---\n\nREAD \u0026 WRITE CONNECTIONS\n------------------------\n\nSometimes 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.\n\n### Configuration\n\nRead \u0026 Write Connections could be set in the model which extends `yidas\\Model`, you could defind the read \u0026 write databases in extended `My_model` for every models.\n\nThere are three types to set read \u0026 write databases:\n\n#### Codeigniter DB Connection\n\nIt recommends to previously prepare CI DB connections, you could assign to attributes directly in construct section before parent's constrcut: \n\n```php\nclass My_model extends yidas\\Model\n{\n    function __construct()\n    {\n        $this-\u003edatabase = $this-\u003edb;\n        \n        $this-\u003edatabaseRead = $this-\u003edbr;\n        \n        parent::__construct();\n    }\n}\n```\n\n\u003e If you already have `$this-\u003edb`, it would be the default setting for both connection.\n\n\u003e This setting way supports [Reconnection](#reconnection).\n\n#### Codeigniter Database Key\n\nYou could set the database key refered from `\\application\\config\\database.php` into model attributes of `database` \u0026 `databaseRead`, the setting connections would be created automatically:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $database = 'default';\n    \n    protected $databaseRead = 'slave';\n}\n```\n\n\u003e This method supports cache mechanism for DB connections, each model could define its own connections but share the same connection by key.\n\n#### Codeigniter Database Config Array\n\nThis 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: \n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $databaseRead = [\n        'dsn'   =\u003e '',\n        'hostname' =\u003e 'specified_db_host',\n        // Database Configuration...\n        ];\n}\n```\n\n### Load Balancing for Databases\n\nIn above case, you could set multiple databases and implement random selected connection for Read or Write Databases.\n\nFor example, configuring read databases in `application/config/database`:\n\n```php\n$slaveHosts = ['192.168.1.2', '192.168.1.3'];\n\n$db['slave']['hostname'] = $slaveHosts[mt_rand(0, count($slaveHosts) - 1)];\n```\n\nAfter that, you could use database key `slave` to load or assign it to attribute:\n\n```php\nclass My_model extends yidas\\Model\n{\n    protected $databaseRead = 'slave';\n}\n```\n\n### Reconnection\n\nIf you want to reconnect database for reestablishing the connection in Codeigniter 3, for `$this-\u003edb` example:\n\n```php\n$this-\u003edb-\u003eclose();\n$this-\u003edb-\u003einitialize();\n```\n\nThe model connections with [Codeigniter DB Connection](#codeigniter-db-connection) setting could be reset after reset the referring database connection.\n\n\u003e Do NOT use [`reconnect()`](https://www.codeigniter.com/userguide3/database/db_driver_reference.html#CI_DB_driver::reconnect) which is a useless method. \n\n---\n\nPESSIMISTIC LOCKING\n-------------------\n\nThe Model also includes a few functions to help you do \"pessimistic locking\" on your `select` statements. To run the statement with a \"shared lock\", you may use the `sharedLock` method to get a query. A shared lock prevents the selected rows from being modified until your transaction commits:\n\n```php\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003esharedLock()-\u003erow_array();\n```\n\nAlternatively, you may use the `lockForUpdate` method. A \"for update\" lock prevents the rows from being modified or from being selected with another shared lock:\n\n```php\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123);\n$result = $this-\u003eModel-\u003elockForUpdate()-\u003erow_array();\n```\n\n### Example Code\n\nThis transaction block will lock selected rows for next same selected rows with `FOR UPDATE` lock:\n\n```php\n$this-\u003eModel-\u003egetDB()-\u003etrans_start();\n$this-\u003eModel-\u003efind()-\u003ewhere('id', 123)\n$result = $this-\u003eModel-\u003elockForUpdate()-\u003erow_array();\n$this-\u003eModel-\u003egetDB()-\u003etrans_complete(); \n```\n\n---\n\nHELPERS\n-------\n\nThe model provides several helper methods:\n\n#### `indexBy()`\n\nIndex by Key\n\n```php\npublic array indexBy(array \u0026 $array, Integer $key=null, Boolean $obj2Array=false)\n```\n\n*Example:*\n```php\n$records = $this-\u003eModel-\u003efindAll();\n$this-\u003eModel-\u003eindexBy($records, 'sn');\n\n// Result example of $records:\n[\n    7 =\u003e ['sn'=\u003e7, title=\u003e'Foo'],\n    13 =\u003e ['sn'=\u003e13, title=\u003e'Bar']\n]\n```\n\n","funding_links":[],"categories":["Model","PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyidas%2Fcodeigniter-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyidas%2Fcodeigniter-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyidas%2Fcodeigniter-model/lists"}