A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5.

Last update: May 17, 2022


Build Status Latest Stable Version Total Downloads Code Climate


Feature/API complete

Idiorm is now considered to be feature complete as of version 1.5.0. Whilst it will continue to be maintained with bug fixes there will be no further new features added from this point on. This means that if a pull request makes breaking changes to the API or requires anything other than a patch version bump of the library then it will not be merged.

Please do not submit feature requests or API breaking changes as they will be closed without ceremony.

A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5 and above.

Tested on PHP 5.2.0+ - may work on earlier versions with PDO and the correct database drivers.

Released under a BSD license.

See Also: Paris, an Active Record implementation built on top of Idiorm.


  • Makes simple queries and simple CRUD operations completely painless.
  • Gets out of the way when more complex SQL is required.
  • Built on top of PDO.
  • Uses prepared statements throughout to protect against SQL injection attacks.
  • Requires no model classes, no XML configuration and no code generation: works out of the box, given only a connection string.
  • Consists of one main class called ORM. Additional classes are prefixed with Idiorm. Minimal global namespace pollution.
  • Database agnostic. Currently supports SQLite, MySQL, Firebird and PostgreSQL. May support others, please give it a try!
  • Supports collections of models with method chaining to filter or apply actions to multiple results at once.
  • Multiple connections supported
  • PSR-1 compliant methods (any method can be called in camelCase instead of underscores eg. find_many() becomes findMany()) - you'll need PHP 5.3+


The documentation is hosted on Read the Docs: idiorm.rtfd.org

Building the Docs

You will need to install Sphinx and then in the docs folder run:

make html

The documentation will now be in docs/_build/html/index.html

Let's See Some Code

$user = ORM::for_table('user')
    ->where_equal('username', 'j4mie')

$user->first_name = 'Jamie';

$tweets = ORM::for_table('tweet')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ->where_equal('user.username', 'j4mie')

foreach ($tweets as $tweet) {
    echo $tweet->text;


Tests are written with PHPUnit and be run through composer

composer test

To make testing on PHP 5.2 (Idiorm maintains support back to this version of PHP) there is a Docker setup in ./test/docker_for_php52 - check the readme in there for more.


1.5.7 - released 2020-04-29

1.5.6 - released 2018-05-31

  • Assign null to self::$_db on reset_db() to ensure PDO closes the connections [bleakgadfly] - issue #338

1.5.5 - released 2018-01-05

  • Add a docker setup for testing with PHP 5.2 (uses PHPUnit 3.6.12, which is the last version released compatible with PHP 5.2) [Treffynnon]

1.5.4 - released 2018-01-04

1.5.3 - released 2017-03-21

  • Document the raw_execute() method and add a note for get_db() in the querying documentation - [Treffynnon]

1.5.2 - released 2016-12-14

1.5.1 - released 2014-06-23

1.5.0 - released 2014-06-22

1.4.1 - released 2013-12-12

Patch update to remove a broken pull request - may have consequences for users of 1.4.0 that exploited the "find_many() now returns an associative array with the databases primary ID as the array keys" change that was merged in 1.4.0.

  • Back out pull request/issue #133 as it breaks backwards compatibility in previously unexpected ways (see #162, #156 and #133) - sorry for merging this change into Idiorm - closes issue 156

1.4.0 - released 2013-09-05

1.3.0 - released 2013-01-31

  • Documentation moved to idiorm.rtfd.org and now built using Sphinx
  • Add support for multiple database connections - closes issue #15 [tag]
  • Add in raw_execute - closes issue #40 [tag]
  • Add get_last_statement() - closes issue #84 [tag]
  • Add HAVING clause functionality - closes issue #50
  • Add is_new method - closes issue #85
  • Add ArrayAccess support to the model instances allowing property access via $model['field'] as well as $model->field - issue #51
  • Add a result set object for collections of models that can support method chains to filter or apply actions to multiple results at once - issue #51 and #22
  • Add support for Firebird with ROWS and TO result set limiting and identifier quoting [mapner] - issue #98
  • Fix last insert ID for PostgreSQL using RETURNING - closes issues #62 and #89 [laacz]
  • Reset Idiorm after performing a query to allow for calling count() and then find_many() [fayland] - issue #97
  • Change Composer to use a classmap so that autoloading is better supported [javierd] - issue #96
  • Add query logging to delete_many [tag]
  • Fix when using set_expr alone it doesn't trigger query creation - closes issue #90
  • Escape quote symbols in "_quote_identifier_part" - close issue #74
  • Fix issue with aggregate functions always returning int when is float sometimes required - closes issue #92
  • Move testing into PHPUnit to unify method testing and query generation testing

1.2.3 - released 2012-11-28

  • Fix issue #78 - remove use of PHP 5.3 static call

1.2.2 - released 2012-11-15

  • Fix bug where input parameters were sent as part-indexed, part associative

1.2.1 - released 2012-11-15

  • Fix minor bug caused by IdiormStringException not extending Exception

1.2.0 - released 2012-11-14

  • Setup composer for installation via packagist (j4mie/idiorm)
  • Add order_by_expr method [sandermarechal]
  • Add support for raw queries without parameters argument [sandermarechal]
  • Add support to set multiple properties at once by passing an associative array to set method [sandermarechal]
  • Allow an associative array to be passed to configure method [jordanlev]
  • Patch to allow empty Paris models to be saved ([j4mie/paris]) - issue #58
  • Add select_many and select_many_expr - closing issues #49 and #69
  • Add support for MIN, AVG, MAX and SUM - closes issue #16
  • Add group_by_expr - closes issue #24
  • Add set_expr to allow database expressions to be set as ORM properties - closes issues #59 and #43 [brianherbert]
  • Prevent ambiguous column names when joining tables - issue #66 [hellogerard]
  • Add delete_many method [CBeerta]
  • Allow unsetting of ORM parameters [CBeerta]
  • Add find_array to get the records as associative arrays [Surt] - closes issue #17
  • Fix bug in _log_query with ? and % supplied in raw where statements etc. - closes issue #57 [ridgerunner]

1.1.1 - released 2011-01-30

  • Fix bug in quoting column wildcard. j4mie/paris#12
  • Small documentation improvements

1.1.0 - released 2011-01-24

  • Add is_dirty method
  • Add basic query caching
  • Add distinct method
  • Add group_by method

1.0.0 - released 2010-12-01

  • Initial release


  • 1. findMany() returns only the last record in a set

    I cloned the database one week ago and the latest version of iodiorm would only return the last record in a set.

    The example I used to prove it out was to create table called users and insert 16 users. Using the follow code I could only ever return the last record. A friend of mine working in the same code base confirmed the issue. I am on Linux and he is on OSX.

    \ORM::configure('username', '----');
    \ORM::configure('password', '----');
    \ORM::configure('logging', true);
    $people = \ORM::forTable('users')->findMany();

    I have a little more info on Stackoverflow and this was all done using the latest code from Git.

    Reviewed by risingfish at 2013-09-21 00:11
  • 2. [improvement] find_many returns an associative array

    Pull based on https://github.com/j4mie/idiorm/issues/132

    Added find_many return associative keys. Micro optimizations for Idiorm and Paris. set and set_expr returns the instance, to admit a fluent query

    Reviewed by Surt at 2013-06-13 10:35
  • 3. Added a none function that guarantees an empty set.


    I added a function.


    That is guaranteed to return an empty set. This code devolves down to the following query:

    SELECT * FROM `person` WHERE 0;

    The reasons for having a dedicated function are:

    • Syntatic sugar: none or a similarly named function makes more sense than where_raw(0)
    • Possibility to optimise (by not actually executing the query)

    An example use of this code in Paris would be:

    class User extends Model {
        public static function has_cookie($orm, $cookie_name) {
            if (isset($_COOKIE[$cookie_name]))  {
                $cookie = $_COOKIE[$cookie_name];
                return $orm->where('cookie', $cookie);
            // Return empty set
            //return $orm->where_raw(0);
            return $orm->none();
    $users = Model::Factory('user')->filter('has_cookie', 'user')->find_many();
    Reviewed by markchalloner at 2012-11-28 11:16
  • 4. MSSQL TOP Limit style support

    As requested, this is a manual merge of the code that was in pull request #37 with support of multiple connections and forcing the use of the TOP style limit for the dblib driver.

    It is currently in use in it's current form in one my projects so I can attest that it's working.

    Reviewed by numkem at 2013-04-12 14:48
  • 5. rowCount


    Why not use PDOStatement :: rowCount to get the number of rows affected by the last statement.

    This property could be populated in the _run function after the statement execution like this: $this->_rowCount = $statement->rowCount();


    Reviewed by featureless at 2012-11-29 10:37
  • 6. Add a RAW JOIN source to the query

    Hi, I've added the raw_join function to the idiorm class. I find it very useful on optimizing the query and selecting the extra data from composite tables. Let me know if you find it appropriate, it actually copy/pasted version of the function _add_join_source without distinction between $join_operator and $table and skiping the _quote_identifier.

    Reviewed by moiseevigor at 2013-10-23 23:05
  • 7. Cache queries in idiorm

    I'm using Paris and idiorm for one of my sites and I've got lots of foreign key relationships expressed like this:

    public function children() {
      return $this->has_many('Child')->find_many();

    The problem is that each time I execute the children() function on the same object, idiorm executes the following SQL (for example):

    SELECT * FROM child WHERE parent_id = 1

    If I'm using information about the children several times on the same page - e.g. checking whether there are any children using count(), then iterating over them - there are lots of unnecessary queries made. I'm never going to change the data once I've entered a view, so if idiorm was able to cache these queries that would be really helpful for my application.

    Reviewed by pwaring at 2011-01-03 15:56
  • 8. Issue with ORM::get_db()->exec() and ORM::raw_execute()


    I'm using Idiorm 1.3.0 with MySQL 5.5.9 / PHP 5.3.5, and I'm getting troubles when using both ORM::get_db()->exec() and ORM::raw_execute().

    I'm trying to use this as a raw query : TRUNCATE table_name. But, when using these two methods :

    ORM::get_db()->exec('TRUNCATE table_name');
    ORM::raw_execute('TRUNCATE table_name');

    I get the same error :

    Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 1 near "TRUNCATE": syntax error'

    But I don't have any ideas from where this comes from :/

    Reviewed by EpocDotFr at 2013-02-11 11:25
  • 9. Refactor join() to implode() as of warning in PHP 7.4

    Refactor join() to implode() as of deprecated warning in PHP 7.4... therefore some strict unit testing suites (like mine) that turn warnings into errors are going to fail.

    While fixing PHPUnit to support 5.x up to 7.4 it turns out that we cannot support both. PHP 7.4 needs PHPUnit 8 at least - that release enforces return types for setUp and tearDown that will cause syntax error in 5.x...

    So now this is an dead end accept we create a testing suite for 5.x and 7.4+

    • I had to put lot of work into fixing Travis CI - PHP 7.4 is not passing at the moment!
    • HHVM has been removed because composer no longer supports it: https://github.com/composer/composer/issues/8127
    • Added hack to bootstrap to support old and new PHPUnit namespace
    if (class_exists('\PHPUnit\Framework\TestCase')) {
    	class PHPUnit_Framework_TestCase extends \PHPUnit\Framework\TestCase

    Supported PHPUnit versions: https://phpunit.de/supported-versions.html

    Reviewed by henryruhs at 2019-07-24 22:44
  • 10. Formatting - camelCase rather than underscore

    How do you feel about having the methods converted to camel case so it is compliant with the PSR-1 spec? (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)

    If you are open to it I can definitely help with the changes!

    Reviewed by crhayes at 2013-03-21 18:07
  • 11. Identifier quoting in Postgres


    I was quite impressed by compact and clean code of idiorm - so, I'm giving it a try in project where Postgresql database is be used. Problem I had was with ORM::_quote_identifier_part() - which quotes table/column names with "`", but a SQL statement in postgres commits seppuku when treated that way :) Could quoting character be made as a configuration option?

    .. code-block:: php

    protected function _quote_identifier_part($part) {
       $quote = self::$_config['identifier_quote_ch'];
       return $quote . $part . $quote;

    Or - probably, identifier quoting could be called as a callback, leaving ORM::_quote_identifier_part just as a default method for that and be overridable in configuration?

    How do You feel about such changes? What the correct approach would be?

    Reviewed by arcijsg at 2010-11-02 09:12
  • 12. PHP 8.1 compatibility

    PHP 8.1 was released last thursday. I'd like to update this as upstream to become compatible. Unfortunately the ArrayAccess interface changed in a breaking way. Hence cutting any previous php version support in composer.json and the travis file.

    Here is the pull request to make idiorm work in php 8.1 https://github.com/j4mie/idiorm/pull/370

    Reviewed by edlerd at 2021-12-01 07:35
  • 13. Various fixes for php 8.1 compatibility

    ~~There are breaking changes in 8.1 to the ArrayAccess interface. The changes adapt to the new typed interface. Various style fixes and an updated composer file. Since older php version will not be compatible, I am cutting any references in the travis file as well.~~ Edit: Found a more elegant solution by just annotating the ArrayAccess methods with #[\ReturnTypeWillChange]. This way everything stays backwards compatible and it works with php 8.1 as well.

    Reviewed by edlerd at 2021-12-01 07:32
  • 14. Num Rows from Result?

    Hi, new to Idiorm, great library and am relearning PHP.

    Wondering how to get the equivalent of ->num_rows from a result? From the documentation, it looks like I need to use the count() method? Thanks

    Reviewed by TilP at 2021-03-17 12:04
  • 15. Offset support for MSSQL / SQLSRV

    MSSQL fails with an SQLSTATE[42000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server] syntax error when using offset()...

    1. TOP and OFFSET are not allowed to be used in one SELECT
    2. OFFSET 10 ROWS with FETCH NEXT 10 ROWS ONLY should be used
    3. Therefore _build_limit() and _build_offset() need some rework

    This are my approaches but I think something is missing to work properly:

    protected static function _detect_limit_clause_style($connection_name) {
        switch(self::get_db($connection_name)->getAttribute(PDO::ATTR_DRIVER_NAME)) {
            case 'dblib':
                return ORM::LIMIT_STYLE_TOP_N;
            case 'sqlsrv':
            case 'mssql':
                return ORM::LIMIT_STYLE_FETCH;
                return ORM::LIMIT_STYLE_LIMIT;
    protected function _build_limit() {
        $fragment = '';
        if (!is_null($this->_limit)) {
            if (self::$_config[$this->_connection_name]['limit_clause_style'] == ORM::LIMIT_STYLE_LIMIT) {
                if (self::get_db($this->_connection_name)->getAttribute(PDO::ATTR_DRIVER_NAME) == 'firebird') {
                    $fragment = 'ROWS';
                } else {
                    $fragment = 'LIMIT';
                $fragment .= " {$this->_limit}";
            } else if (self::$_config[$this->_connection_name]['limit_clause_style'] == ORM::LIMIT_STYLE_FETCH) {
                $fragment = 'FETCH NEXT ' . $this->_limit . ' ROWS ONLY';
        return $fragment;
    protected function _build_offset() {
        if (!is_null($this->_offset)) {
            $clause = 'OFFSET';
            if (self::get_db($this->_connection_name)->getAttribute(PDO::ATTR_DRIVER_NAME) == 'firebird') {
                $clause = 'TO';
            if (self::$_config[$this->_connection_name]['limit_clause_style'] == ORM::LIMIT_STYLE_FETCH) {
                return $clause . ' ' . $this->_offset . '  ROWS';
            return $clause . ' ' . $this->_offset;
        return '';

    Reference: http://www.sqlservertutorial.net/sql-server-basics/sql-server-offset-fetch/ https://stackoverflow.com/questions/2135418/equivalent-of-limit-and-offset-for-sql-server

    Reviewed by henryruhs at 2019-06-27 00:16
  • 16. where_id_in with joints is broken

    Hi If i join 2 tables with the same pk named ID the where_id_is function works great. But the where_id_in function doesn't rename the ID column to tablename.ID as where_id_is would do.

    So the query fails.

    Reviewed by kkrell2016 at 2018-12-13 11:53
Related tags
Doctrine Object Relational Mapper (ORM)

3.0.x 2.9.x 2.8.x Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence for PHP objects. It sits on top o

May 28, 2022
Convention-based Object-Relational Mapper

Corma Corma is a high-performance, convention-based ORM based on Doctrine DBAL. Corma is great because: No complex and difficult to verify annotations

Nov 15, 2021
A SQL query builder with zero dependencies

Latitude Query Builder A SQL query builder with zero dependencies. Attempts to be PSR-1, PSR-2, and PSR-4 compliant. Install composer require latitude

May 17, 2022
Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP

Propel2 Propel2 is an open-source Object-Relational Mapping (ORM) for PHP. Requirements Propel uses the following Symfony Components: Config Console F

Apr 29, 2022
MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query and get result in a fastest way. ( WEBSITE VERSION )
MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query and get result in a fastest way. ( WEBSITE VERSION )

Mysql Optimizer mysql optimizer also known as MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query

Feb 14, 2022
A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)

Laravel MongoDB This package adds functionalities to the Eloquent model and Query builder for MongoDB, using the original Laravel API. This library ex

May 24, 2022
SQL to Laravel Query Builder

Marwan - SQL To Laravel Builder SQL to Laravel Query Builder, A Converter written in PHP Features Converts SQL Queries to Laravel Query Builder. Assis

May 22, 2022
Get MYSQL statement from query builder in laravel helper

Get MYSQL statement laravel This package allows to get mysql statement that query builder in laravel made it for debugging purposes. Basic usage Dump

Sep 1, 2021
A data mapper implementation for your persistence model in PHP.

Atlas.Orm Atlas is a data mapper implementation for persistence models (not domain models). As such, Atlas uses the term "record" to indicate that its

Apr 22, 2022
Analogue ORM : Data Mapper ORM for Laravel/PHP

(this project is looking for a new maintainer) Analogue ORM Analogue is a flexible, easy-to-use ORM for PHP. It is a transposition of the Eloquent ORM

Apr 5, 2022
Articulate - An alternative ORM for Laravel, making use of the data mapper pattern

Articulate Laravel: 8.* PHP: 8.* License: MIT Author: Ollie Read Author Homepage: https://ollie.codes Articulate is an alternative ORM for Laravel bas

Jan 4, 2022
A simple PHP library to transfer data from a source (object or array) to an object.

SimplexMapper A simple PHP library to transfer data from a source (object or array) to an object. $dbData = [ 'username' => 'pfazzi', 'emailAd

Jan 22, 2022
Independent query builders for MySQL, PostgreSQL, SQLite, and Microsoft SQL Server.

Aura.SqlQuery Provides query builders for MySQL, Postgres, SQLite, and Microsoft SQL Server. These builders are independent of any particular database

May 14, 2022
Eloquent Filter is a package for filter data of models by the query strings. Easy to use and fully dynamic.
Eloquent Filter is a package for filter data of models by the query strings. Easy to use and fully dynamic.

Eloquent Filter Eloquent Filter adds custom filters to your Eloquent Models in Laravel. It's easy to use and fully dynamic. Table of Content Introduct

May 24, 2022
The query filter bundle allows you to filter data from QueryBuilder and the Database
The query filter bundle allows you to filter data from QueryBuilder and the Database

The query filter bundle allows you to filter data from QueryBuilder and the Database. you can filter multiple columns at the same time and also you can filter relation fields with two-level deep and without any join in your query builder.

Apr 8, 2022
A simple program to query mysql data and display the queried data in JSON format

A simple program to query mysql data and display the queried data in JSON format. The data displayed in JSON format will change and update as the data in your mysql database changes.

Mar 7, 2022
Database Abstraction Layer, Schema Introspection, Schema Generation, Query Builders

Cycle DBAL Secure, multiple SQL dialects (MySQL, PostgreSQL, SQLite, SQLServer), schema introspection, schema declaration, smart identifier wrappers,

Apr 22, 2022
A PHP client driver for the RethinkDB query language (ReQL).

PHP-RQL A PHP client driver for the RethinkDB query language (ReQL).

Apr 29, 2022
PHP Object Model Manager for Postgresql

POMM: The PHP Object Model Manager for Postgresql Note This is the 1,x version of Pomm. This package is not maintained anymore, the stable Pomm 2.0 is

Mar 30, 2022