Why another PHP ORM (Object-Relational Mapping) Package?
This package aims to provide only the commonly used data access and manipulation features that most PHP applications need. ORM packages like Propel, Doctrine ORM, Eloquent, etc also provide these features and more features which may never be needed in many PHP applications. LeanOrm's code base is designed to be
- easily comprehensible
- easily extensible (via inheritance or composition) and
- compact (with one Model class, two Record classes (Read-Only and Read-Write), a Collection class, a DBConnector class (that talks to the database via PDO) and a File containing Exception class definitions).
For the most part, users of this package will only be interacting with the Model class, one or both of the two Record classes and the Collection class.
LeanOrm is an implementation of the Generic Data Objects (GDAO) package (a package containing an abstract Model class, two interfaces (CollectionInterface and RecordInterface), a RecordList class (a collection data structure that stores only instances of RecordInterface) and a File containing Exception class definitions). The GDAO package was also designed to be compact, easily comprehensible and extensible.
Paris is another PHP ORM that adheres to some of the same principles (a compact code-base and easy comprehensibility) as LeanORM. However, the Active Record pattern (where records contain data, data access & manipulation logic and business logic) implemented in the Paris package leads to tight coupling of database access and application / business logic.
LeanOrm implements the Table Data Gateway and Data Mapper patterns. This allows for very loose coupling. Business logic dealing with a single row of data in a database table can be put in the Record class for the table in question, business logic dealing with multiple rows of data in a database table can be put in the Collection class for the table in question while database access logic remains in the Model class for the table in question.
The Model class generates all the SQL for accessing and manipulating data in the database; it uses the DBConnector class to execute the SQL statements. The Model class together with the DBConnector class act as a Table Data Gateway.
The Model class also acts as a Data Mapper by being able to map:
- a row of data in a database table to a Record object
- rows of data in a database table in to a Collection object containing one or more Record objects
- foreign key relationship(s) between database tables into attribute(s) of a record object (an instance of a record per Has-One or Belongs-To relationship and an instance of a collection per Has-Many or Has-Many-Through relationship)
Deliberately Omitted Features
- virtual / calculated columns,
- soft deletion,
- single table inheritance,
- magic fetch methods (eg. fetchBySomeColName),
- sanitization & validation of data (packages like Aura Filter , Respect Validate , Valitron , etc. can be used),
- automatic serialization and unserialization of database columns,
- migration (packages like PHPMig , Phinx , Migrations , etc. can be used)
Code snippets illustrating how to implement some of the deliberately omitted features will be included in this documentation.
Installation & Requirements
-
This library requires PHP 5.4 or later. It is currently only installable via composer:
composer require rotexsoft/leanorm
Each table in your database is expected to have a single auto-incrementing numeric primary key column (composite primary keys are not supported).
Running tests
./vendor/bin/phpunit --coverage-text
Getting Started
LeanOrm will create only one connection to a specific database (ie. one connection per unique dsn string). Creating one or more Model objects for one or more tables with the same dsn string will lead to the creation of only one connection to the database; that one connection will be shared amongst all model instances created with that same dsn string. If you use two different dsn strings, two connections to the database(s) will be created by LeanOrm. Consequently, using three different dsn strings will lead to the creation of three different database connections, and so on for four or more different dsn strings.
All examples are based on the schema below:
Defining and Creating Model Objects
There are two basic ways to use this package:
-
Create Model classes for each database table in your application's database. Each of these classes must extend \LeanOrm\Model. This is the recommended approach for large applications. An example of a Model class (AuthorsModel) associated with an authors table in the database:
<?php class AuthorsModel extends \LeanOrm\Model { protected $_collection_class_name = 'AuthorsCollection'; //If not set, \LeanOrm\Model\Collection will be used by default protected $_record_class_name = 'AuthorRecord'; //If not set, \LeanOrm\Model\Record will be used by default protected $_created_timestamp_column_name = 'date_created'; protected $_updated_timestamp_column_name = 'm_timestamp'; protected $_primary_col = 'author_id'; protected $_table_name = 'authors'; protected $_relations = [ //Entry below specifies that an author can have one or more posts (ie. a has-many relationship). 'posts' => [ 'relation_type' => \LeanOrm\Model::RELATION_TYPE_HAS_MANY, 'foreign_key_col_in_my_table' => 'author_id', 'foreign_table' => 'posts', 'foreign_key_col_in_foreign_table' => 'author_id', 'primary_key_col_in_foreign_table' => 'post_id', 'foreign_models_class_name' => 'PostsModel', 'foreign_models_collection_class_name' => 'PostsCollection', 'foreign_models_record_class_name' => 'PostRecord', ] ]; } //create the model object $authors_model = new AuthorsModel( "mysql:some-host-name;dbname=blog", "username", "password", [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'] ); ?>
-
Or create instances of \LeanOrm\Model (one for each database table in your application's database). This approach is good for small to medium sized projects.
<?php $rel = [ //Entry below specifies that an author can have one or more posts (ie. a has-many relationship). 'posts' => [ 'relation_type' => \LeanOrm\Model::RELATION_TYPE_HAS_MANY, 'foreign_key_col_in_my_table' => 'author_id', 'foreign_table' => 'posts', 'foreign_key_col_in_foreign_table' => 'author_id', 'primary_key_col_in_foreign_table' => 'post_id', 'foreign_models_class_name' => 'PostsModel', 'foreign_models_collection_class_name' => 'PostsCollection', 'foreign_models_record_class_name' => 'PostRecord', ] ]; $authors_model = new \LeanOrm\Model( "mysql:some-host-name;dbname=blog", "username", "password", [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'], [ 'primary_col' => 'author_id', 'table_name' => 'authors', 'relations' => $rel, 'created_timestamp_column_name' => 'date_created', 'updated_timestamp_column_name' => 'm_timestamp', 'collection_class_name' => 'AuthorsCollection', //If not set, \LeanOrm\Model\Collection will be used by default 'record_class_name' => 'AuthorRecord', //If not set, \LeanOrm\Model\Record will be used by default ] ); ?>
For both methods, you can decide if you want to use the base Record and Collection classes (\LeanOrm\Model\Collection and \LeanOrm\Model\Record (or \LeanOrm\Model\ReadOnlyRecord if needed) ) or if you want to extend the base Record and Collection classes eg:
<?php
class AuthorsCollection extends \LeanOrm\Model\Collection {
//some custom code for collection objects that are meant to hold records belonging to the 'authors' database table
}
class AuthorRecord extends \LeanOrm\Model\Record {
//some custom code for records belonging to the 'authors' database table
}
?>
Creating Records & Inserting Data into the Database
<?php
//Method 1:
$new_record = $authors_model->createNewRecord(); //create a blank new record
$new_record->name = 'Joe Blow'; //set a value for the 'name' column
$new_record->save();//save to the database
//Method 2:
//create a new record and set a value for the 'name' column
$new_record = $authors_model->createNewRecord([ 'name' => 'Joe Blow']);
$new_record->save();//save to the database
//NOTE: createNewRecord also accepts a second parameter, an associative array
// that can be used to supply values for the properties of the record
// object to be created.
// Eg. $authors_model->createNewRecord([ 'name' => 'Joe Blow'], ['_is_new'=>false ])
// that is marked as not new by setting the _is_new property of the Record object
// that was created to false.
//Method 3:
//$inserted_data is an associative array of the data just inserted into the
//database. This data will include an auto-generated primary key value and
//values of the columns that keep track of the time the record was inserted
//and last updated (if the $_created_timestamp_column_name and
//$_updated_timestamp_column_name were correctly set).
$inserted_data = $authors_model->insert([ 'name' => 'Joe Blow']);
//Multiple Inserts:
//Below is the most efficient way insert multiple rows to the database.
//$all_successfully_inserted will be === true if all the inserts were
//successful, otherwise it will be === false which means the multiple
//insert was unsuccessful (nothing is saved to the database in this case).
$all_successfully_inserted = $authors_model->insertMany([ ['name' => 'Joe Blow'], ['name' => 'Jane Doe'] ]);
//NOTE: if you have a collection (an instance of \LeanOrm\Model\Collection)
// containing 2 or more records you can also efficiently save the records
// by calling \LeanOrm\Model\Collection::saveAll(true). See the documentation
// for Collections for more details.
?>
Fetching data from the Database - Parameter Definition
There are eight Model methods for fetching data from the database:
-
fetchCol(array $params = array()):
selects data from a single database table's column and returns an array of the column values.
-
fetchOneRecord(array $params = array()):
selects a single row of data from a database table and returns it as an instance of \LeanOrm\Model\Record (or any of its subclasses).
-
fetchPairs(array $params = array()):
selects data from two database table columns and returns an array whose keys are values from the first column and whose values are the values from the second column.
-
fetch($ids, array $params=array(), $use_records=false, $use_collections=false):
selects one row of data from a database table if
$ids
contains an integer value matching the primary key value of a row in the database table (returns it as an instance of \LeanOrm\Model\Record). Selects one or more rows of data from a database table if$ids
is an array of integer values matching the primary key values of one or more rows of data from a database table and returns them as instances of \LeanOrm\Model\Record (or any of its subclasses) inside an array or an instance of \LeanOrm\Model\Collection (or any of its subclasses) or returns them as an array of arrays. -
fetchRecordsIntoArray(array $params = array()):
selects one or more rows of data from a database table and returns them as instances of \LeanOrm\Model\Record (or any of its subclasses) inside an array.
-
fetchRecordsIntoCollection(array $params = array()):
selects one or more rows of data from a database table and returns them as instances of \LeanOrm\Model\Record (or any of its subclasses) inside an instance of \LeanOrm\Model\Collection (or any of its subclasses).
-
fetchRowsIntoArray(array $params = array()):
selects one or more rows of data from a database table and returns them as associative arrays inside an array.
-
fetchValue(array $params = array()):
selects a single value from a single column of a single row of data from a database table and returns the value (eg. as a string, or an appropriate data type).
They all accept one parameter; an associative array ($params) for specifying how the data should be retrieved (except for fetch(..) which accepts additional parameters). Below is the specification for the structure of the array:
-
relations_to_include: this entry in the $params array should contain an array of relation names defined for a specific model. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params=["relations_to_include"=>['posts']];
-
distinct: this entry in the $params array should contain a boolean value of true if the DISTINCT keyword should be added to the SELECT statement that will be generated by the fetch method to fetch data or false if the DISTINCT keyword should be ommitted. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params=["distinct"=>true];
-
cols: this entry in the $params array should contain an array of the name(s) of column(s) to be returned in the data returned by the fetch method. If this entry is not set in the $params array, all columns will be returned in the result of the fetch method if applicable. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params=["cols"=>['author_id', 'name']];
-
where: this entry in the $params array should contain an array of parameters for building a WHERE clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. For example, the definitions below would be valid for the AuthorsModel defined earlier in this document:
<?php $params = [ 'where' => [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ] ]; //Generates the WHERE clause below: /* WHERE ( name = 'Joe Blow' AND author_id > '2' AND date_created >= '2015-08-19 14:24:48' ) */ $params = [ 'where' => [ [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], ], 'OR'=> [ [ 'col'=>'name', 'op'=>'!=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'<', 'val'=>50 ] ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], 'OR#2'=> [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Jane Doe' ], [ 'col'=>'author_id', 'op'=>'not-in', 'val'=>[58, 65] ], ] ] ]; //Generates the WHERE clause below: /* WHERE ( ( name = 'Joe Blow' AND author_id > 2 ) OR ( name <> 'Joe Blow' AND author_id > 50 ) AND date_created >= '2015-08-19 14:24:48' OR ( name = 'Jane Doe' AND author_id NOT IN (58,65) ) ) */ // NOTE 1: The 'op' could be assigned any one of these values: // '=', '>', '>=', '<', '<=', 'in', 'is-null', 'like', '!=', 'not-in', 'not-like', 'not-null' // NOTE 2: For multiple OR conditions, append a # and a unique string after // the # to the OR key so that the subsequent OR condition(s) do not override // the previous ones. // NOTE 3: Do not to make the first key in the 'where' array or the first key // in any of the array(s) inside the 'where' array an 'OR' or 'OR#...' key. // NOTE 4: For any sub-array containing an item with a key named 'op' with a // value of either 'not-null' or 'is-null', there must not be any item in that // sub-array with a key named 'val', but there must be a corresponding item // with a key named 'col' with a string value. // NOTE 5: The operators: 'in' and 'not-in' allow 'val' to be set to an array // or string value. If 'val' is a string, it must be a valid value that a // NOT IN or IN operator expects including the opening and closing brackets. // Eg. "( 1, 2, 3 )" or "( '4', '5', '6' )". ?>
-
group: this entry in the $params array should contain an array of the name(s) of column(s) for building a GROUP BY clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params=['group' => ['name']]; generates GROUP BY name
-
having: this entry in the $params array should contain an array of parameters for building a HAVING clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. For example, the definitions below would be valid for the AuthorsModel defined earlier in this document:
<?php $params = [ 'having' => [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ] ]; //Generates the HAVING clause below: /* HAVING ( name = 'Joe Blow' AND author_id > '2' AND date_created >= '2015-08-19 14:24:48' ) */ $params = [ 'having' => [ [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], ], 'OR'=> [ [ 'col'=>'name', 'op'=>'!=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'<', 'val'=>50 ] ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], 'OR#2'=> [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Jane Doe' ], [ 'col'=>'author_id', 'op'=>'not-in', 'val'=>[58, 65] ], ] ] ]; //Generates the HAVING clause below: /* HAVING ( ( name = 'Joe Blow' AND author_id > 2 ) OR ( name <> 'Joe Blow' AND author_id > 50 ) AND date_created >= '2015-08-19 14:24:48' OR ( name = 'Jane Doe' AND author_id NOT IN (58,65) ) ) */ // NOTE 1: The 'op' could be assigned any one of these values: // '=', '>', '>=', '<', '<=', 'in', 'is-null', 'like', '!=', 'not-in', 'not-like', 'not-null' // NOTE 2: For multiple OR conditions, append a # and a unique string after // the # to the OR key so that the subsequent OR condition(s) do not override // the previous ones. // NOTE 3: Do not to make the first key in the 'having' array or the first key // in any of the array(s) inside the 'having' array an 'OR' or 'OR#...' key. // NOTE 4: For any sub-array containing an item with a key named 'op' with a // value of either 'not-null' or 'is-null', there must not be any item in that // sub-array with a key named 'val', but there must be a corresponding item // with a key named 'col' with a string value. // NOTE 5: The operators: 'in' and 'not-in' allow 'val' to be set to an array // or string value. If 'val' is a string, it must be a valid value that a // NOT IN or IN operator expects including the opening and closing brackets. // Eg. "( 1, 2, 3 )" or "( '4', '5', '6' )". ?>
-
order: this entry in the $params array should contain an array of parameters for building an ORDER BY clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params = [ 'order' => ['name DESC'] ]; generates ORDER BY name DESC
-
limit_size: this entry in the $params array should contain an int value for building a LIMIT clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params = [ 'limit_size'=>50 ]; generates LIMIT 50 for a mysql database. It would be different for MS SQLServer.
-
limit_offset: this entry in the $params array should contain an int value for building the OFFSET part of the LIMIT clause to be added to the SELECT statement that will be generated by the fetch method to fetch data. It has no effect when limit_size has not been set. For example, the definition below would be valid for the AuthorsModel defined earlier in this document:
$params = [ 'limit_offset' => 10, 'limit_size'=>50 ]; generates LIMIT 50 OFFSET 10 for a mysql database. It would be different for MS SQLServer (ie. OFFSET 10 ROWS FETCH NEXT 50 ROWS ONLY).
Fetching data from the Database - Data Retreival
-
Fetching one or more rows of data (via fetch):
<?php $params = [ "cols"=>['author_id', 'name', 'date_created'], ]; //The $params array above causes $authors_model->fetch(2, $params) to generate the SQL //statement below: /* SELECT author_id, name, date_created FROM `authors` WHERE ( author_id = 2 ); */ //The call below fetches a database table row having an author_id value of 2 //into an instance of AuthorRecord which is a sub-class of \LeanOrm\Model\Record. $author_record = $authors_model->fetch(2, $params); //You can access the fetched data like so using object notation: echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //You can access the fetched data like so using array syntax: echo " author_id: {$author_record['author_id']}, name: {$author_record['name']} , date_created: {$author_record['date_created']} <br>"; //To return related data (if any) defined in the Model's _relations array, //add a 'relations_to_include' entry to the $params array defined ealier //above. For example, to get the posts belonging to an author (note that //this relationship has been defined in the AuthorsModel class earlier //above) modify the $params array like so: $params['relations_to_include'] = ['posts']; $author_record = $authors_model->fetch(2, $params); //You can access the fetched data like so using object notation: echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Access the posts echo " Posts: <br>"; //In this case, $author_record->posts will be an instance of PostsCollection //and each $post_record will be an instance of PostRecord based on the //relationship definintion in the AuthorsModel class. foreach($author_record->posts as $post_record) { echo " post_id: {$post_record->post_id}, title: {$post_record->title} , body: {$post_record->body} <br>"; } //You can fetch a collection of AuthorRecord(s) with author_id values of 1, 2 and 3 via //the call below: $authors_model->fetch( [1, 2, 3], $params, true, true); //You can fetch an array of AuthorRecord(s) with author_id values of 1, 2 and 3 via //the call below: $authors_model->fetch( [1, 2, 3], $params, true, false); //You can fetch an array of rows in the authors table with author_id values of 1, 2 and 3 via //the call below: $authors_model->fetch( [1, 2, 3], $params, false, false); ?>
-
Fetching data as Records into a Collection (via fetchRecordsIntoCollection):
<?php $params = [ "distinct"=>true, "cols"=>['author_id', 'name', 'date_created'], 'where' => [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'group' => ['name'], 'having'=> [ [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'order' => ['name DESC'], 'limit_offset' => 0, 'limit_size' => 50, ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT DISTINCT author_id, name, date_created FROM `authors` WHERE ( name = 'Joe Blow' AND author_id > 2 AND date_created >= '2015-08-19 14:24:48' ) GROUP BY name HAVING ( date_created >= '2015-08-19 14:24:48' ) ORDER BY name DESC LIMIT 50; */ //The fetchRecordsIntoCollection call below returns an instance //of \LeanOrm\Model\Collection containing one or more instances of //\LeanOrm\Model\Record each containing a row of data from the row(s) //returned by executing the generated select query above. $collection_of_authors = $authors_model->fetchRecordsIntoCollection($params); //You can loop through the collection like so: foreach ($collection_of_authors as $collection_key => $author_record) { echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Accessing a database table's columns in a record object using array syntax also works like below: echo " author_id: {$author_record['author_id']}, name: {$author_record['name']} , date_created: {$author_record['date_created']} <br>"; } //To return all the rows in a database table, call fetchRecordsIntoCollection with //no parameters. In this case fetchRecordsIntoCollection will just perform a //"SELECT authors.* FROM `authors`" query and return all the rows of data in //the authors table. $collection_of_all_authors = $authors_model->fetchRecordsIntoCollection(); //To return related data (if any) defined in the Model's _relations array, //add a 'relations_to_include' entry to the $params array defined ealier //above. For example, to get the posts belonging to an author (note that //this relationship has been defined in the AuthorsModel class earlier //above) modify the $params array like so: $params['relations_to_include'] = ['posts']; $collection_of_authors_with_posts = $authors_model->fetchRecordsIntoCollection($params); //You can loop through the collection like so: foreach ($collection_of_authors_with_posts as $collection_key => $author_record) { echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Access the posts echo " Posts: <br>"; //In this case, $author_record->posts will be an instance of PostsCollection //and each $post_record will be an instance of PostRecord based on the //relationship definintion in the AuthorsModel class. foreach($author_record->posts as $post_record) { echo " post_id: {$post_record->post_id}, title: {$post_record->title} , body: {$post_record->body} <br>"; } } ?>
-
Fetching data as Records into an array (via fetchRecordsIntoArray ): Use this method if you don't need to call any of the Collection methods on the returned data. fetchRecordsIntoArray executes slightly faster than fetchRecordsIntoCollection and is also slightly more memory efficient than fetchRecordsIntoCollection.
<?php $params = [ "distinct"=>true, "cols"=>['author_id', 'name', 'date_created'], 'where' => [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'group' => ['name'], 'having'=> [ [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'order' => ['name DESC'], 'limit_offset' => 0, 'limit_size' => 50, ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT DISTINCT author_id, name, date_created FROM `authors` WHERE ( name = 'Joe Blow' AND author_id > 2 AND date_created >= '2015-08-19 14:24:48' ) GROUP BY name HAVING ( date_created >= '2015-08-19 14:24:48' ) ORDER BY name DESC LIMIT 50; */ //The fetchRecordsIntoArray call below returns an array containing one or more //instances of \LeanOrm\Model\Record each containing a row of data from the //row(s) returned by executing the generated select query above. $array_of_author_records = $authors_model->fetchRecordsIntoArray($params); //You can loop through the array like so: foreach ($array_of_author_records as $array_key => $author_record) { echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Accessing a database table's columns in a record object using array syntax also works like below: echo " author_id: {$author_record['author_id']}, name: {$author_record['name']} , date_created: {$author_record['date_created']} <br>"; } //To return all the rows in a database table, call fetchRecordsIntoArray with //no parameters. In this case fetchRecordsIntoArray will just perform a //"SELECT authors.* FROM `authors`" query and return all the rows of data in //the authors table. $array_of_all_author_records = $authors_model->fetchRecordsIntoArray(); //To return related data (if any) defined in the Model's _relations array, //add a 'relations_to_include' entry to the $params array defined ealier //above. For example, to get the posts belonging to an author (note that //this relationship has been defined in the AuthorsModel class earlier //above) modify the $params array like so: $params['relations_to_include'] = ['posts']; $array_of_authors_with_posts = $authors_model->fetchRecordsIntoArray($params); //You can loop through the array like so: foreach ($array_of_authors_with_posts as $array_key => $author_record) { echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Access the posts echo " Posts: <br>"; //In this case, $author_record->posts will be an array. //Each $post_record will be an instance of PostRecord based on the //relationship definintion in the AuthorsModel class. foreach($author_record->posts as $post_record) { echo " post_id: {$post_record->post_id}, title: {$post_record->title} , body: {$post_record->body} <br>"; } } ?>
-
Fetching data as arrays into an array (via fetchRowsIntoArray ): Use this method if you don't need to call any of the Collection or Record methods on the returned data. fetchRowsIntoArray executes faster than fetchRecordsIntoArray and fetchRecordsIntoCollection and is also more memory efficient than fetchRecordsIntoArray and fetchRecordsIntoCollection.
<?php $params = [ "distinct"=>true, "cols"=>['author_id', 'name', 'date_created'], 'where' => [ [ 'col'=>'name', 'op'=>'=', 'val'=>'Joe Blow' ], [ 'col'=>'author_id', 'op'=>'>', 'val'=>2 ], [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'group' => ['name'], 'having'=> [ [ 'col'=>'date_created', 'op'=>'>=', 'val'=>'2015-08-19 14:24:48' ], ], 'order' => ['name DESC'], 'limit_offset' => 0, 'limit_size' => 50, ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT DISTINCT author_id, name, date_created FROM `authors` WHERE ( name = 'Joe Blow' AND author_id > 2 AND date_created >= '2015-08-19 14:24:48' ) GROUP BY name HAVING ( date_created >= '2015-08-19 14:24:48' ) ORDER BY name DESC LIMIT 50; */ //The fetchRowsIntoArray call below returns an array containing one or more //array(s) each containing a row of data from the row(s) returned by //executing the generated select query above. $array_of_authors = $authors_model->fetchRowsIntoArray($params); //You can loop through the array like so: foreach ($array_of_authors as $array_key => $array_of_author_data) { echo " author_id: {$array_of_author_data['author_id']}, name: {$array_of_author_data['name']} , date_created: {$array_of_author_data['date_created']} <br>"; } //To return all the rows in a database table, call fetchRowsIntoArray with //no parameters. In this case fetchRowsIntoArray will just perform a //"SELECT authors.* FROM `authors`" query and return all the rows of data in //the authors table. $array_of_all_authors = $authors_model->fetchRowsIntoArray(); //To return related data (if any) defined in the Model's _relations array, //add a 'relations_to_include' entry to the $params array defined ealier //above. For example, to get the posts belonging to an author (note that //this relationship has been defined in the AuthorsModel class earlier //above) modify the $params array like so: $params['relations_to_include'] = ['posts']; $array_of_authors_with_posts = $authors_model->fetchRowsIntoArray($params); //You can loop through the array like so: foreach ($array_of_authors_with_posts as $array_key => $array_of_author_data) { echo " author_id: {$array_of_author_data['author_id']}, name: {$array_of_author_data['name']} , date_created: {$array_of_author_data['date_created']} <br>"; //Access the posts echo " Posts: <br>"; //In this case, $array_of_author_data['posts'] will be an array. //Each $post_record will be an array containing a row of data from the //posts database table. The related data is retrieved based on the relationship //definintion in the AuthorsModel class. foreach($array_of_author_data['posts'] as $post_record) { echo " post_id: {$post_record['post_id']}, title: {$post_record['title']} , body: {$post_record['body']} <br>"; } } ?>
-
Fetching one row of data as a Record (via fetchOneRecord):
<?php $params = [ "cols"=>['author_id', 'name', 'date_created'], 'where' => [ [ 'col'=>'author_id', 'op'=>'=', 'val'=>2 ], ], ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT author_id, name, date_created FROM `authors` WHERE ( author_id = 2 ); */ //fetchOneRecord returns an instance of \LeanOrm\Model\Record from the first //row returned by executing the generated select query above. //In this case the first database table row having an author_id value of 2 is //returned as an instance of AuthorRecord which is a sub-class of //\LeanOrm\Model\Record because the _record_class_name property of the //AuthorsModel class was set to 'AuthorRecord'. $author_record = $authors_model->fetchOneRecord($params); //You can access the fetched data like so using object notation: echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //You can access the fetched data like so using array syntax: echo " author_id: {$author_record['author_id']}, name: {$author_record['name']} , date_created: {$author_record['date_created']} <br>"; //Calling fetchOneRecord with no parameters returns an instance of //\LeanOrm\Model\Record from the first row returned by executing //"SELECT authors.* FROM `authors`". In this case an instance of //AuthorRecord is returned. $author_record = $authors_model->fetchOneRecord(); //To return related data (if any) defined in the Model's _relations array, //add a 'relations_to_include' entry to the $params array defined ealier //above. For example, to get the posts belonging to an author (note that //this relationship has been defined in the AuthorsModel class earlier //above) modify the $params array like so: $params['relations_to_include'] = ['posts']; $author_record = $authors_model->fetchOneRecord($params); //You can access the fetched data like so using object notation: echo " author_id: {$author_record->author_id}, name: {$author_record->name} , date_created: {$author_record->date_created} <br>"; //Access the posts echo " Posts: <br>"; //In this case, $author_record->posts will be an instance of PostsCollection //and each $post_record will be an instance of PostRecord based on the //relationship definintion in the AuthorsModel class. foreach($author_record->posts as $post_record) { echo " post_id: {$post_record->post_id}, title: {$post_record->title} , body: {$post_record->body} <br>"; } ?>
-
Fetching data from two database table columns into an array (via fetchPairs), where values from the first column will be the keys in the returned array and the values from the second column will be the corresponding values in the returned array:
<?php $params = [ "cols"=>['author_id', 'name', 'date_created'], 'where' => [ [ 'col'=>'author_id', 'op'=>'in', 'val'=>[1,3] ], ], ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT author_id, name, date_created FROM `authors` WHERE ( author_id IN (1,3) ); --------------------------------------------------------------------------- | author_id | name | m_timestamp | date_created | --------------------------------------------------------------------------- | 1 | 'Joe Blow' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 2 | 'Jack Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 3 | 'Jane Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- Given an authors database table containing the data above calling fetchPairs like below will return [ 1=>'Joe Blow', 3=>'Jane Doe'] (NOTE: it uses the first two columns in $params['cols']): */ $data = $authors_model->fetchPairs($params); /* Given an authors database table containing the data above calling fetchPairs like below will return [ 1=>'Joe Blow', 2=>'Jack Doe', 3=>'Jane Doe'] (NOTE: it uses the first two columns in the authors table; ie. author_id and name): */ $data = $authors_model->fetchPairs(); ?>
-
Fetching data from one database table column into an array (via fetchCol):
<?php $params = [ "cols"=>['name', 'date_created'], 'where' => [ [ 'col'=>'author_id', 'op'=>'in', 'val'=>[1,3] ], ], ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT name, date_created FROM `authors` WHERE ( author_id IN (1,3) ); --------------------------------------------------------------------------- | author_id | name | m_timestamp | date_created | --------------------------------------------------------------------------- | 1 | 'Joe Blow' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 2 | 'Jack Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 3 | 'Jane Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- Given an authors database table containing the data above calling fetchCol like below will return ['Joe Blow', 'Jane Doe'] (NOTE: it uses the first column in $params['cols']): */ $data = $authors_model->fetchCol($params); /* Given an authors database table containing the data above calling fetchCol like below will return [ 1, 2, 3] (NOTE: it uses the first column in the authors table; ie. author_id): */ $data = $authors_model->fetchCol(); ?>
-
Fetching data from a single row and column of a database table (via fetchValue):
<?php $params = [ "cols"=>['name', 'date_created'], 'where' => [ [ 'col'=>'author_id', 'op'=>'in', 'val'=>[1,3] ], ], ]; //The $params array above causes any of the fetch* methods to generate the SQL //statement below (which would be used by the fetch* method to retrieve data from the database): /* SELECT name, date_created FROM `authors` WHERE ( author_id IN (1,3) ); --------------------------------------------------------------------------- | author_id | name | m_timestamp | date_created | --------------------------------------------------------------------------- | 1 | 'Joe Blow' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 2 | 'Jack Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- | 3 | 'Jane Doe' | '2015-08-19 14:24:48' | '2015-08-19 14:24:48' | --------------------------------------------------------------------------- Given an authors database table containing the data above calling fetchValue like below will return 'Joe Blow' (NOTE: it uses the value of first column in $params['cols'] in the first row of data returned): */ $data = $authors_model->fetchValue($params); /* Given an authors database table containing the data above calling fetchValue like below will return 1 (NOTE: it uses the value of first column in the authors table (ie. author_id) in the first row of data returned): */ $data = $authors_model->fetchValue(); ?>
Updating Data
There are two ways to update data in the database:
- By fetching one or more existing records from the database into record objects, then assigning new value(s) to each of the fetched records and then finally calling the save method on each Record object. If the records were fetched into a Collection object, then the saveAll method on the Collection object could be called instead (this will cause all the records in the collection to be saved).
-
By calling the updateMatchingDbTableRows method on a Model object. This method does not involve the retrieval of Record objects, rather only the conditions for matching the rows of data in the database table to be updated and the new data to be used for the update need to be supplied to updateMatchingDbTableRows.
Note: there is also an updateSpecifiedRecord method in the Model class which accepts a Record object as parameter and saves it to the database. The updateSpecifiedRecord method does not really need to be called, since the save method in the Record class calls it internally when save is called on a Record object that needs to be updated.
<?php //Method 1: $params = [ 'where' => [ [ 'col'=>'author_id', 'op'=>'=', 'val'=>2 ] ], ]; //Fetch the first row of data with an author_id value of 2 from the authors //table into a Record object. $author_record = $authors_model->fetchOneRecord($params); //Change the value of a field in the fetched record. $author_record->name = 'A new Name'; //Save the change made to the Record object to the database. $author_record->save(); //NOTE: that the steps above can be applied to the Record objects returned // by fetchRecordsIntoArray and fetchRecordsIntoCollection. //Method 2: //Update all rows of data with an author_id value of 2 in the authors //table, set the value of the name column in each of these rows to //'Another new Name'. $authors_model ->updateMatchingDbTableRows(['name'=>'Another new Name'], ['author_id'=>2]); //NOTE: updateMatchingDbTableRows only supports conditions using the equality operator ('=') // to match the rows to be updated. If you need to match rows using other operators like // >, <=, LIKE, etc. then use any of the fetch methods that return Record objects to fetch // the data you want to update and either call the save method on each Record object or call // the saveAll method on a Collection object containing the record objects. The fetch methods // allows you to specify more complex conditions for matching rows of data than // updateMatchingDbTableRows does. ?>
Deleting Data
There are two ways of deleting data from the database:
- By fetching one or more existing records from the database into record objects and then calling the delete method on each Record object (NOTE: the data is deleted from the database but the Record object still contains the data and is automatically marked as new. To make sure the data is both deleted from the database and cleared out of the Record object the delete method on the Record object must be called with a boolean value of true as its first parameter). If the records were fetched into a Collection object, then the deleteAll method on the Collection object could be called instead (this will cause all the records in the collection to be deleted from the database, but the Record objects will still be in the Collection object with their data intact. removeAll should additionally be called on the Collection object to clear the Record objects and their data from the Collection object; if the Record Objects are not referenced anywhere else outside of the Collection object, then the __destruct method will be automatically called on each Record object and all the memory used by the Record objects will be released).
-
By calling the deleteMatchingDbTableRows method on a Model object. This method does not involve the retrieval of Record objects, rather only the conditions for matching the rows of data in the database table to be deleted needs to be supplied to deleteMatchingDbTableRows.
Note: there is also a deleteSpecifiedRecord method in the Model class which accepts a Record object as parameter and deletes the database row associated with the Record object, sets the primary key value of the Record object to null and also sets the _is_new property of the Record object to the boolean value of true (NOTE: other data in the Record object remains). The deleteSpecifiedRecord method does not really need to be called, since the delete method in the Record class calls it internally when delete is called on a Record object that needs to be deleted.
<?php //Method 1: $params = [ 'where' => [ [ 'col'=>'author_id', 'op'=>'=', 'val'=>2 ] ], ]; //Fetch the first row of data with an author_id value of 2 from the authors //table into a Record object. $author_record = $authors_model->fetchOneRecord($params);; //Delete the row corresponding to the Record object from the database. $author_record->delete(); // OR $author_record->delete(true); to both delete from the database //and set the Record object's data to an empty array internally //NOTE: that the steps above can be applied to the Record objects returned // by fetchRecordsIntoArray and fetchRecordsIntoCollection. //Method 2: //Delete all rows of data with an author_id value of 2 in the authors //table. $authors_model->deleteMatchingDbTableRows(['author_id'=>2]); //NOTE: deleteMatchingDbTableRows only supports conditions using the equality operator ('=') // to match the rows to be deleted. If you need to match rows using other operators like // >, <=, LIKE, etc. then use any of the fetch methods that return Record objects to fetch // the data you want to delete and either call the delete method on each Record object or call // the deleteAll method on a Collection object containing the record objects. The fetch methods // allow you to specify more complex conditions for matching rows of data than // deleteMatchingDbTableRows does. ?>