Skip to content

Commit

Permalink
Refactoring util\Collection to remove dependency on http\Media. A…
Browse files Browse the repository at this point in the history
…dded `Collection::formats()` to enable registering format handler classes.
  • Loading branch information
nateabele committed Feb 5, 2010
1 parent 0227823 commit 14d8e15
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 41 deletions.
19 changes: 19 additions & 0 deletions app/config/bootstrap.php
Expand Up @@ -60,4 +60,23 @@
// 'default' => array('adapter' => 'Php')
// ));

/**
* The `Collection` class, which serves as the base class for some of Lithium's data objects
* (`RecordSet` and `Document`) provides a way to manage data collections in a very flexible and
* intuitive way, using closures and SPL interfaces. The `to()` method allows a `Collection` (or
* subclass) to be converted to another format, such as an array. The `Collection` class also allows
* other classes to be connected as handlers to convert `Collection` objects to other formats.
*
* The following connects the `Media` class as a format handler, which allows `Collection`s to be
* exported to any format with a handler provided by `Media`, i.e. JSON. This enables things like
* the following:
* {{{
* $posts = Post::find('all');
* return $posts->to('json');
* }}}
*/
use \lithium\util\Collection;

Collection::formats('\lithium\http\Media');

?>
4 changes: 1 addition & 3 deletions libraries/lithium/g11n/Message.php
Expand Up @@ -82,9 +82,7 @@ public static function translate($id, $options = array()) {
*/
public static function contentFilters() {
$t = function($message, $options = array()) {
return Message::translate($message, $options + array(
'default' => $message
));
return Message::translate($message, $options + array('default' => $message));
};
$tn = function($message1, $message2, $count, $options = array()) {
return Message::translate($message1, $options + compact('count') + array(
Expand Down
30 changes: 30 additions & 0 deletions libraries/lithium/net/http/Media.php
Expand Up @@ -136,6 +136,36 @@ public static function types() {
return array_keys(static::$_types);
}

/**
* Alias for `types()`; included for interface compatibility with
* `lithium\util\Collection::to()`, which allows a collection object to be exported to any
* format supported by a `Media` handler. See the documentation for `Collection::to()` for more
* information.
*
* @see lithium\net\http\Media
* @return array Returns the value of `Media::types()`.
*/
public static function formats() {
return static::types();
}

/**
* Alias for `encode()`; included for interface compatibility with
* `lithium\util\Collection::to()`, which allows a collection object to be exported to any
* format supported by a `Media` handler. See the documentation for `Collection::to()` for more
* information.
*
* @param mixed $format Format into which data will be converted, i.e. `'json'`.
* @param mixed $data Either an array or object (usually an instance of `Collection`) which will
* be converted into the specified format.
* @param array $options Additional handler-specific options to pass to the content handler.
* @return mixed
*/
public static function to($format, $data, $options = array()) {
$data = is_object($data) ? $data->to('array') : $data;
return static::encode($format, $data, $options);
}

/**
* Map an extension to a particular content-type (or types) with a set of options.
*
Expand Down
4 changes: 4 additions & 0 deletions libraries/lithium/tests/cases/net/http/MediaTest.php
Expand Up @@ -27,6 +27,8 @@ public function testMediaTypes() {
$this->assertTrue(in_array('json', $result));
$this->assertFalse(in_array('my', $result));

$this->assertEqual($result, Media::formats());

$result = Media::type('json');
$expected = 'application/json';
$this->assertEqual($expected, $result['content']);
Expand Down Expand Up @@ -345,6 +347,8 @@ public function testMediaEncoding() {
$result = Media::encode('json', $data);
$this->assertEqual($expected, $result);

$this->assertEqual($result, Media::to('json', $data));

$result = Media::encode('badness', $data);
$this->assertNull($result);
}
Expand Down
1 change: 1 addition & 0 deletions libraries/lithium/tests/cases/util/CollectionTest.php
Expand Up @@ -219,6 +219,7 @@ public function testInternalKeys() {
}

public function testCollectionFormatConversion() {
Collection::formats('\lithium\http\Media');
$items = array('hello', 'goodbye', 'foo' => array('bar', 'baz' => 'dib'));
$collection = new Collection(compact('items'));

Expand Down
120 changes: 82 additions & 38 deletions libraries/lithium/util/Collection.php
Expand Up @@ -14,6 +14,17 @@
*/
class Collection extends \lithium\core\Object implements \ArrayAccess, \Iterator, \Countable {

/**
* A central registry of global format handlers for `Collection` objects and subclasses.
* Accessed via the `formats()` method.
*
* @see \lithium\util\Collection::formats()
* @var array
*/
protected static $_formats = array(
'array' => '\lithium\util\Collection::_toArray'
);

/**
* The items contained in the collection.
*
Expand All @@ -34,19 +45,29 @@ class Collection extends \lithium\core\Object implements \ArrayAccess, \Iterator
*
* @var array
*/
protected $_classes = array(
'media' => '\lithium\net\http\Media'
);

protected $_autoConfig = array('items');

/**
* undocumented variable
* Accessor method for adding format handlers to instances and subclasses of `Collection`.
*
* @var array
* @param string $format
* @param mixed $handler
* @return mixed
*/
protected $_autoConfig = array('items');
public static function formats($format, $handler = null) {
if ($format === false) {
return static::$_formats = array();
}
if ((is_null($handler)) && class_exists($format)) {
return static::$_formats[] = $format;
}
return static::$_formats[$format] = $handler;
}

/**
* undocumented function
* Initializes the collection object by merging in collection items and removing redundant
* object properties.
*
* @return void
*/
Expand Down Expand Up @@ -125,37 +146,30 @@ public function __call($method, $parameters = array()) {
public function to($format, $options = array()) {
$defaults = array('internal' => false);
$options += $defaults;
$state = $options['internal'] ? $this->_items : $this;
$result = null;

switch ($format) {
case 'array':
$result = array();

foreach ($state as $key => $value) {
if (is_object($value)) {
switch (true) {
case method_exists($value, 'to'):
$value = $value->to('array');
break;
case (is_object($value) && $vars = get_object_vars($value)):
$value = $vars;
break;
case method_exists($value, '__toString'):
$value = $value->__toString();
break;
}
}
$result[$key] = $value;
}
return $result;
default:
$media = $this->_classes['media'];

if (in_array($format, $media::types())) {
return $media::encode($format, $this->to('array', $options));
}
break;
$data = $options['internal'] ? $this->_items : $this;

if (is_object($format) && is_callable($format)) {
return $format($data, $options);
}

if (isset(static::$_formats[$format]) && is_callable(static::$_formats[$format])) {
$handler = static::$_formats[$format];
$handler = is_string($handler) ? explode('::', $handler, 2) : $handler;

if (is_array($handler)) {
list($class, $method) = $handler;
return $class::$method($data, $options);
}
return $handler($data, $options);
}

foreach (static::$_formats as $key => $handler) {
if (!is_int($key)) {
continue;
}
if (in_array($format, $handler::formats($format, $data, $options))) {
return $handler::to($format, $data, $options);
}
}
}

Expand Down Expand Up @@ -379,6 +393,36 @@ public function count() {
public function keys() {
return array_keys($this->_items);
}

/**
* Exports a `Collection` instance to an array. Used by `Collection::to()`.
*
* @param mixed $data Either a `Collection` instance, or an array representing a `Collection`'s
* internal state.
* @return array Returns the value of `$data` as a pure PHP array, recursively converting all
* sub-objects and other values to their closest array or scalar equivalents.
*/
protected static function _toArray($data) {
$result = array();

foreach ($data as $key => $item) {
switch (true) {
case (!is_object($item)):
$result[$key] = $item;
break;
case (method_exists($item, 'to')):
$result[$key] = $item->to('array');
break;
case ($vars = get_object_vars($item)):
$result[$key] = $vars;
break;
case (method_exists($item, '__toString')):
$result[$key] = (string) $item;
break;
}
}
return $result;
}
}

?>

0 comments on commit 14d8e15

Please sign in to comment.