Skip to content

Commit

Permalink
Add ResultSet functionality to Idiorm
Browse files Browse the repository at this point in the history
  • Loading branch information
treffynnon committed Jan 15, 2013
1 parent 5f2fbc8 commit 5a6d20c
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Features
* Requires no model classes, no XML configuration and no code generation: works out of the box, given only a connection string.
* Consists of just one class called `ORM`. Minimal global namespace pollution.
* Database agnostic. Currently supports SQLite and MySQL. 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.

Documentation
-------------
Expand Down Expand Up @@ -50,6 +51,9 @@ Changelog
* Add HAVING clause functionality
* Fix issue with aggregate functions always returning `int` when is `float` sometimes required - closes issue #92
* Documentation moved to [idiorm.rtfd.org](http://idiorm.rtfd.org) and built using [Sphinx](http://sphinx-doc.org/)
* 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 some PHPUnit tests for newer features

#### 1.2.3 - release 2012-11-28

Expand Down
18 changes: 18 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,24 @@ configuration array shortcut:
'password' => 'top_secret'
));

Result sets
^^^^^^^^^^^

Setting: ``return_result_sets``

Collections of results can be returned as an array (default) or as a result set.
See the `find_result_set()` documentation for more information.

::

ORM::configure('return_result_sets', true); // returns result sets


.. note::

It is recommended that you setup your projects to use result sets as they
are more flexible.

PDO Driver Options
^^^^^^^^^^^^^^^^^^

Expand Down
61 changes: 61 additions & 0 deletions docs/querying.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ To find a single record by ID, you can pass the ID directly to the
Multiple records
^^^^^^^^^^^^^^^^

.. note::

It is recommended that you use results sets over arrays - see `As a result set`
below.

Any method chain that ends in ``find_many()`` will return an *array* of
ORM class instances, one for each row matched by your query. If no rows
were found, an empty array will be returned.
Expand All @@ -70,6 +75,62 @@ To find all records where the ``gender`` is ``female``:

$females = ORM::for_table('person')->where('gender', 'female')->find_many();

As a result set
'''''''''''''''

.. note::

There is a configuration setting ``return_result_sets`` that will cause
``find_many()`` to return result sets by default. It is recommended that you
turn this setting on:

::

ORM::configure('return_result_sets', true);

You can also find many records as a result set instead of an array of Idiorm
instances. This gives you the advantage that you can run batch operations on a
set of results.

So for example instead of running this:

::

$people = ORM::for_table('person')->find_many();
foreach ($people as $person) {
$person->age = 50;
$person->save();
}

You can simple do this instead:

::

ORM::for_table('person')->find_result_set()
->set('age', 50)
->save();

To do this substitute any call to ``find_many()`` with
``find_result_set()``.

A result set will also behave like an array so you can `count()` it and `foreach`
over it just like an array.

::

foreach(ORM::for_table('person')->find_result_set() as $record) {
echo $person->name;
}

::

echo count(ORM::for_table('person')->find_result_set());

.. note::

For deleting many records it is recommended that you use `delete_many()` as it
is more efficient than calling `delete()` on a result set.

As an associative array
'''''''''''''''''''''''

Expand Down
103 changes: 103 additions & 0 deletions idiorm.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class ORM implements ArrayAccess {
'identifier_quote_character' => null, // if this is null, will be autodetected
'logging' => false,
'caching' => false,
'return_result_sets' => false,
);

// Database connection, instance of the PDO class
Expand Down Expand Up @@ -439,12 +440,37 @@ public function find_one($id=null) {
* from your query, and execute it. Will return an array
* of instances of the ORM class, or an empty array if
* no rows were returned.
* @return array|\IdiormResultSet
*/
public function find_many() {
if(self::$_config['return_result_sets']) {
return $this->find_result_set();
}
return $this->_find_many();
}

/**
* Tell the ORM that you are expecting multiple results
* from your query, and execute it. Will return an array
* of instances of the ORM class, or an empty array if
* no rows were returned.
* @return array
*/
protected function _find_many() {
$rows = $this->_run();
return array_map(array($this, '_create_instance_from_row'), $rows);
}

/**
* Tell the ORM that you are expecting multiple results
* from your query, and execute it. Will return a result set object
* containing instances of the ORM class.
* @return \IdiormResultSet
*/
public function find_result_set() {
return new IdiormResultSet($this->_find_many());
}

/**
* Tell the ORM that you are expecting multiple results
* from your query, and execute it. Will return an array,
Expand Down Expand Up @@ -1757,6 +1783,83 @@ protected function _str_replace_outside_quotes_cb($matches) {
}
}

/**
* A result set class for working with collections of model instances
* @author Simon Holywell <treffynnon@php.net>
*/
class IdiormResultSet implements Countable, IteratorAggregate {
/**
* The current result set as an array
* @var array
*/
protected $_results = array();

/**
* Optionally set the contents of the result set by passing in array
* @param array $results
*/
public function __construct(array $results = array()) {
$this->set_results($results);
}

/**
* Set the contents of the result set by passing in array
* @param array $results
*/
public function set_results(array $results) {
$this->_results = $results;
}

/**
* Get the current result set as an array
* @return array
*/
public function get_results() {
return $this->_results;
}

/**
* Get the current result set as an array
* @return array
*/
public function as_array() {
return $this->get_results();
}

/**
* Get the number of records in the result set
* @return int
*/
public function count() {
return count($this->_results);
}

/**
* Get an iterator for this object. In this case it supports foreaching
* over the result set.
* @return \ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->_results);
}

/**
* Call a method on all models in a result set. This allows for method
* chaining such as setting a property on all models in a result set or
* any other batch operation across models.
* @example ORM::for_table('Widget')->find_many()->set('field', 'value')->save();
* @param string $method
* @param array $params
* @return \IdiormResultSet
*/
public function __call($method, $params = array()) {
foreach($this->_results as $model) {
call_user_func_array(array($model, $method), $params);
}
return $this;
}
}

/**
* A placeholder for exceptions eminating from the IdiormString class
*/
Expand Down
67 changes: 67 additions & 0 deletions test/IdiormResultSetTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

class IdiormResultSetTest extends PHPUnit_Framework_TestCase {

public function testGet() {
$IdiormResultSet = new IdiormResultSet();
$this->assertInternalType('array', $IdiormResultSet->get_results());
}

public function testConstructor() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet($result_set);
$this->assertSame($IdiormResultSet->get_results(), $result_set);
}

public function testSetResultsAndGetResults() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet();
$IdiormResultSet->set_results($result_set);
$this->assertSame($IdiormResultSet->get_results(), $result_set);
}

public function testAsArray() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet();
$IdiormResultSet->set_results($result_set);
$this->assertSame($IdiormResultSet->as_array(), $result_set);
}

public function testCount() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet($result_set);
$this->assertSame($IdiormResultSet->count(), 1);
$this->assertSame(count($IdiormResultSet), 1);
}

public function testGetIterator() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet($result_set);
$this->assertInstanceOf('ArrayIterator', $IdiormResultSet->getIterator());
}

public function testForeach() {
$result_set = array('item' => new stdClass);
$IdiormResultSet = new IdiormResultSet($result_set);
$return_array = array();
foreach($IdiormResultSet as $key => $record) {
$return_array[$key] = $record;
}
$this->assertSame($result_set, $return_array);
}

public function testCallingMethods() {
$result_set = array('item' => ORM::for_table('test'), 'item2' => ORM::for_table('test'));
$IdiormResultSet = new IdiormResultSet($result_set);
$IdiormResultSet->set('field', 'value')->set('field2', 'value');

foreach($IdiormResultSet as $record) {
$this->assertTrue(isset($record->field));
$this->assertSame($record->field, 'value');

$this->assertTrue(isset($record->field2));
$this->assertSame($record->field2, 'value');
}
}

}
31 changes: 31 additions & 0 deletions test/ORMTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
<?php

require_once 'test_classes.php';

class ORMTest extends PHPUnit_Framework_TestCase {

public function setUp() {
// Enable logging
ORM::configure('logging', true);

// Set up the dummy database connection
$db = new MockPDO('sqlite::memory:');
ORM::set_db($db);
}

public function testStaticAtrributes() {
$this->assertEquals('0', ORM::CONDITION_FRAGMENT);
$this->assertEquals('1', ORM::CONDITION_VALUES);
Expand Down Expand Up @@ -44,4 +55,24 @@ public function testArrayAccess() {
$this->assertFalse(isset($model['test']));
}

public function testFindResultSet() {
$result_set = ORM::for_table('test')->find_result_set();
$this->assertInstanceOf('IdiormResultSet', $result_set);
$this->assertSame(count($result_set), 5);
}

public function testFindResultSetByDefault() {
ORM::configure('return_result_sets', true);

$result_set = ORM::for_table('test')->find_many();
$this->assertInstanceOf('IdiormResultSet', $result_set);
$this->assertSame(count($result_set), 5);

ORM::configure('return_result_sets', false);

$result_set = ORM::for_table('test')->find_many();
$this->assertInternalType('array', $result_set);
$this->assertSame(count($result_set), 5);
}

}

0 comments on commit 5a6d20c

Please sign in to comment.