Skip to content

Commit

Permalink
Refactoring base classes in \data, implementing one-to-many relatio…
Browse files Browse the repository at this point in the history
…nship support for MongoDB. Implementing GridFS support in MongoDB. Making `\util\Collection::toArray()` public.
  • Loading branch information
nateabele committed Jun 18, 2010
1 parent c96df6d commit f7ae4c1
Show file tree
Hide file tree
Showing 13 changed files with 542 additions and 287 deletions.
29 changes: 28 additions & 1 deletion libraries/lithium/data/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public function offsetExists($offset) {
* @return object Returns the first `Entity` instance in the set.
*/
public function rewind() {
$this->_valid = (reset($this->_data) || count($this->_data));
$this->_valid = (reset($this->_data) || count($this->_data));

if (!$this->_valid && !$this->_hasInitialized) {
$this->_hasInitialized = true;
Expand Down Expand Up @@ -224,6 +224,33 @@ public function map($filter, array $options = array()) {
return parent::map($filter, $options);
}

/**
* Converts the current state of the data structure to an array.
*
* @return array Returns the array value of the data in this `Collection`.
*/
public function data() {
return $this->to('array');
}

/**
* Adds the specified object to the `Collection` instance, and assigns associated metadata to
* the added object.
*
* @param string $offset The offset to assign the value to.
* @param mixed $data The entity object to add.
* @return mixed Returns the set `Entity` object.
*/
public function offsetSet($offset, $data) {
if (is_array($data)) {
$class = $this->_classes['entity'];
$data = new $class(compact('data'));
} else {
$data->assignTo($this);
}
return $this->_data[] = $data;
}

/**
* Gets the stat or stats associated with this `Collection`.
*
Expand Down
66 changes: 44 additions & 22 deletions libraries/lithium/data/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
namespace lithium\data;

use \RuntimeException;
use \lithium\data\Source;
use \lithium\util\Collection as Col;

/**
* `Entity` class. Represents data such as a row or document in a database. Entities have fields
Expand All @@ -22,18 +24,10 @@ class Entity extends \lithium\core\Object {
protected $_model = null;

/**
* Associative array of the records fields with values
* Associative array of the entity's fields and values.
*/
protected $_data = array();

/**
* If this object is chained off of an originally queried object, contains an instance of
* `Relationship` defining the relationship from the origin object to this one.
*
* @var object
*/
protected $_relationship = null;

/**
* An array containing all related records and recordsets, keyed by relationship name, as
* defined in the bound model class.
Expand Down Expand Up @@ -279,25 +273,53 @@ public function exists() {
}

/**
* Called after a `Record` is saved. Updates the object's internal state to reflect the
* Called after an `Entity` is saved. Updates the object's internal state to reflect the
* corresponding database record, and sets the `Record`'s primary key, if this is a
* newly-created object.
*
* @param $id The ID to assign, where applicable.
* @param mixed $id The ID to assign, where applicable.
* @param array $data Any additional generated data assigned to the object by the database.
* @return void
*/
public function update($id = null, $data = array()) {
if ($id) {
$id = (array) $id;
$model = $this->_model;
foreach ((array) $model::meta('key') as $i => $key) {
$this->_data[$key] = $id[$i];
}
foreach ($data as $key => $value) {
$this->_data[$key] = $value;
public function update($id = null, array $data = array()) {
$this->_modified = array();
$this->_exists = true;

if (!$id) {
return;
}

$model = $this->_model;
$key = $model::meta('key');

if (is_array($key)) {
foreach ($key as $i => $k) {
$this->_data[$k] = $id[$i];
}
} else {
$this->_data[$key] = $id;
}
$this->_exists = true;
foreach ($data as $key => $value) {
$this->_data[$key] = $value;
}
}

/**
* Gets the array of fields modified on this entity.
*
* @return array Returns an array where the keys are entity field names, and the values are
* always `true`.
*/
public function modified() {
if (!$this->_exists) {
$keys = array_keys($this->_data);
return array_combine($keys, array_fill(0, count($keys), true));
}
return $this->_modified;
}

public function export(Source $dataSource, array $options = array()) {
return array_intersect_key($this->_data, $this->_modified);
}

/**
Expand All @@ -324,7 +346,7 @@ public function assignTo($parent, array $config = array()) {
public function to($format, array $options = array()) {
switch ($format) {
case 'array':
$result = $this->_data;
$result = Col::toArray($this->_data);
break;
default:
$result = $this;
Expand Down
11 changes: 7 additions & 4 deletions libraries/lithium/data/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class Model extends \lithium\core\StaticObject {
* extended but not directly interacted with) must be present in this list. Models can declare
* themselves as base models using the following code:
* {{{
* public function __init() {
* public static function __init() {
* static::_isBase(__CLASS__, true);
* parent::__init();
* }
Expand Down Expand Up @@ -422,12 +422,13 @@ public static function __callStatic($method, $params) {
public static function find($type, array $options = array()) {
$self = static::_instance();
$classes = $self->_classes;
$finder = array();

$defaults = array(
'conditions' => null, 'fields' => null, 'order' => null, 'limit' => null, 'page' => 1
);

if ($type != 'all' && !isset($self->_finders[$type]) && is_scalar($type)) {
if ($type != 'all' && is_scalar($type) && !isset($self->_finders[$type])) {
$options['conditions'] = array($self->_meta['key'] => $type);
$type = 'first';
}
Expand All @@ -443,7 +444,9 @@ public static function find($type, array $options = array()) {
$connection = $self::invokeMethod('_connection');
return $connection->read(new $query(array('type' => 'read') + $options), $options);
};
$finder = isset($self->_finders[$type]) ? array($self->_finders[$type]) : array();
if (is_string($type) && isset($self->_finders[$type])) {
$finder = is_callable($self->_finders[$type]) ? array($self->_finders[$type]) : array();
}
return static::_filter(__FUNCTION__, $params, $filter, $finder);
}

Expand Down Expand Up @@ -613,7 +616,7 @@ public static function hasField($field) {
* {{{
* $post = Post::create(array("title" => "New post"));
* echo $post->title; // echoes "New post"
* $post->save();
* $success = $post->save();
* }}}
*
* @param array $data Any data that this record should be populated with initially.
Expand Down
105 changes: 45 additions & 60 deletions libraries/lithium/data/collection/DocumentSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,18 @@
namespace lithium\data\collection;

use \Iterator;
use \lithium\data\Source;
use \lithium\util\Collection;

class DocumentSet extends \lithium\data\Collection {

/**
* An array containing all related documents, keyed by relationship name, as defined in the
* bound model class.
*
* @var array
*/
protected $_relationships = array();

/**
* The class dependencies for `Document`.
*
* @var array
*/
protected $_classes = array(
'entity' => '\lithium\data\entity\Document',
'entity' => 'lithium\data\entity\Document',
'set' => __CLASS__
);

Expand Down Expand Up @@ -68,7 +62,6 @@ public function __set($name, $value = null) {
$value = $this->_relation('set', $name, $value);
}
$this->_data[$name] = $value;
$this->_modified[$name] = true;
}

/**
Expand Down Expand Up @@ -106,7 +99,9 @@ public function __unset($name) {
* @return void
*/
public function set($values) {
$this->__set($values);
foreach ($values as $key => $val) {
$this[$key] = $val;
}
}

/**
Expand All @@ -117,7 +112,22 @@ public function set($values) {
* @return mixed Returns either a sub-object in the document, or a scalar field value.
*/
public function offsetGet($offset) {
return $this->__get($offset);
$data = null;
$null = null;

if (!isset($this->_data[$offset]) && !$data = $this->_populate(null, $offset)) {
return $null;
}
$data = $data ?: $this->_data[$offset];

if (is_a($data, $this->_classes['entity'])) {
return $data;
}

if ($this->_isComplexType($data)) {
$this->_data[$offset] = $this->_relation('entity', $offset, $this->_data[$offset]);
}
return $this->_data[$offset];
}

/**
Expand All @@ -126,7 +136,21 @@ public function offsetGet($offset) {
* @return object Returns the first `Document` object instance in the collection.
*/
public function rewind() {
return ($entity = parent::rewind()) ? $entity : $this->__get(key($this->_data));
$data = parent::rewind();
$key = key($this->_data);

if (is_a($data, $this->_classes['entity'])) {
return $data;
}

if ($this->_isComplexType($data)) {
$this->_data[$key] = $this->_relation('entity', $key, $this->_data[$key]);
}
return isset($this->_data[$key]) ? $this->_data[$key] : null;
}

public function current() {
return $this->offsetGet(key($this->_data));
}

/**
Expand All @@ -146,43 +170,14 @@ public function next() {
$this->_valid = true;
}
$this->_valid = $this->_valid ?: !is_null($this->_populate());
return $this->_valid ? $this->__get(key($this->_data)) : null;
return $this->_valid ? $this->offsetGet(key($this->_data)) : null;
}

/**
* PHP magic method used when accessing fields as document properties, i.e. `$document->_id`.
*
* @param $name The field name, as specified with an object property.
* @return mixed Returns the value of the field specified in `$name`, and wraps complex data
* types in sub-`Document` objects.
*/
public function &__get($name) {
$data = null;
$null = null;

if (strpos($name, '.')) {
$current = $this;
$path = explode('.', $name);
$length = count($path) - 1;

foreach ($path as $i => $key) {
$current =& $current->__get($key);
if (!$current instanceof Document && $i < $length) {
return $null;
}
}
return $current;
}

if (!isset($this->_data[$name]) && !$data = $this->_populate(null, $name)) {
return $null;
}
$data = $data ?: $this->_data[$name];

if ($this->_isComplexType($data) && !$data instanceof \lithium\data\Entity) {
$this->_data[$name] = $this->_relation('entity', $name, $this->_data[$name]);
}
return $this->_data[$name];
public function export(Source $dataSource, array $options = array()) {
$map = function($doc) use ($dataSource, $options) {
return is_array($doc) ? $doc : $doc->export($dataSource, $options);
};
return array_map($map, $this->_data);
}

/**
Expand Down Expand Up @@ -240,23 +235,13 @@ protected function _populate($data = null, $key = null) {
*/
protected function _relation($classType, $key, $data, $options = array()) {
$parent = $this;
$key = ($key === null) ? count($this->_data) : $key;
$pathKey = trim("{$this->_pathKey}.{$key}", '.');

if (($key || $key === 0) && $model = $this->_model) {
foreach ($model::relations() as $name => $relation) {
if ($relation && ($key === $relation->data('fieldName'))) {
$model = $relation->data('to');
break;
}
}
}
$model = $this->_model;

if (is_object($data) && $data instanceof Document) {
$data->assignTo($this, compact('model', 'pathKey'));
return $data;
}
$options += compact('model', 'data', 'parent', 'pathKey');
$options += compact('model', 'data', 'parent');
return new $this->_classes[$classType]($options);
}
}
Expand Down
Loading

0 comments on commit f7ae4c1

Please sign in to comment.