Permalink
Browse files

Refactoring `util\Collection` to remove dependency on `http\Media`. A…

…dded `Collection::formats()` to enable registering format handler classes.
  • Loading branch information...
nateabele committed Feb 3, 2010
1 parent 0227823 commit 14d8e1527e09968e4938794b3936b2aef7f96971
View
@@ -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');
+
?>
@@ -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(
@@ -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.
*
@@ -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']);
@@ -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);
}
@@ -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'));
@@ -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.
*
@@ -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
*/
@@ -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);
+ }
}
}
@@ -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.