Permalink
Browse files

Moving class constructor from `\data\collection\Document` to `\data\C…

…ollection`. Making `closed()` and `close()` methods public. Refactoring offset and key handling in `Document` and `RecordSet`. Adding `'locked'` flag to meta information in `Model`.
  • Loading branch information...
1 parent b6783d4 commit 1a4a9d0c7bb54171a59e513e29672a489f043a93 @nateabele nateabele committed May 14, 2010
@@ -55,5 +55,4 @@
return $chain->next($self, $params, $chain);
});
-
?>
@@ -77,6 +77,21 @@
'items', 'classes' => 'merge', 'handle', 'model', 'result', 'query'
);
+ /**
+ * Class constructor
+ *
+ * @param array $config
+ * @return void
+ */
+ public function __construct(array $config = array()) {
+ if (isset($config['data']) && !isset($config['items'])) {
+ $config['items'] = $config['data'];
+ unset($config['data']);
+ }
+ parent::__construct($config);
+ $this->_items = (array) $this->_items;
+ }
+
/**
* Returns the model which this particular collection is based off of.
*
@@ -136,7 +151,7 @@ public function meta() {
* @return object This collection instance.
*/
public function each($filter) {
- if (!$this->_closed()) {
+ if (!$this->closed()) {
while($this->next()) {}
}
return parent::each($filter);
@@ -155,7 +170,7 @@ public function each($filter) {
* @return array|object The filtered items.
*/
public function map($filter, array $options = array()) {
- if (!$this->_closed()) {
+ if (!$this->closed()) {
while($this->next()) {}
}
return parent::map($filter, $options);
@@ -175,26 +190,14 @@ public function stats($name = null) {
return $this->_stats;
}
- /**
- * Magic alias for `_close()`. Ensures that the data set's connection is closed when the object
- * is destroyed.
- *
- * @return void
- */
- public function __destruct() {
- $this->_close();
- }
-
- abstract protected function _populate($data = null, $key = null);
-
/**
* Executes when the associated result resource pointer reaches the end of its data set. The
* resource is freed by the connection, and the reference to the connection is unlinked.
*
* @return void
*/
- protected function _close() {
- if (!$this->_closed()) {
+ public function close() {
+ if (!$this->closed()) {
$this->_result = $this->_handle->result('close', $this->_result, $this);
unset($this->_handle);
}
@@ -207,9 +210,34 @@ protected function _close() {
* @return boolean Returns true if all records are loaded and the database resources have been
* freed, otherwise returns false.
*/
- protected function _closed() {
+ public function closed() {
return (empty($this->_result) || !isset($this->_handle) || empty($this->_handle));
}
+
+ /**
+ * Ensures that the data set's connection is closed when the object is destroyed.
+ *
+ * @return void
+ */
+ public function __destruct() {
+ $this->close();
+ }
+
+ /**
+ * A method to be implemented by concrete `Collection` classes which, provided a reference to a
+ * backend data source (see the `$_handle` property), and a resource representing a query result
+ * cursor, fetches new result data and wraps it in the appropriate object type, which is added
+ * into the `Collection` and returned.
+ *
+ * @param mixed $data Data (in an array or object) that is manually added to the data
+ * collection. If `null`, data is automatically fetched from the associated backend
+ * data source, if available.
+ * @param mixed $key String, integer or array key representing the unique key of the data
+ * object. If `null`, the key will be extracted from the data passed or fetched,
+ * using the associated `Model` class.
+ * @return object Returns a `Record` or `Document` object, or other data object.
+ */
+ abstract protected function _populate($data = null, $key = null);
}
?>
@@ -198,6 +198,7 @@ class Model extends \lithium\core\StaticObject {
'name' => null,
'title' => null,
'class' => null,
+ 'locked' => true,
'source' => null,
'connection' => 'default',
'initialized' => false
@@ -209,9 +210,9 @@ class Model extends \lithium\core\StaticObject {
* The schema is lazy-loaded by the first call to `Model::schema()`, unless it has been
* manually defined in the `Model` subclass.
*
- * For schemaless persistent storage (e.g. MongoDB), this is never populated
- * automatically - if you desire a fixed schema to interact with in those cases, you will
- * be required to define it yourself.
+ * For schemaless persistent storage (e.g. MongoDB), this is never populated automatically - if
+ * you desire a fixed schema to interact with in those cases, you will be required to define it
+ * yourself.
*
* Example:
* {{{
@@ -222,6 +223,26 @@ class Model extends \lithium\core\StaticObject {
* );
* }}}
*
+ * For MongoDB specifically, you can also implement a callback in your database connection
+ * configuration that fetches and returns the schema data, as in the following:
+ *
+ * {{{
+ * Connections::add('default', array(
+ * 'type' => 'MongoDb',
+ * 'host' => 'localhost',
+ * 'database' => 'app_name',
+ * 'schema' => function($db, $collection, $meta) {
+ * $result = $db->connection->schemas->findOne(compact('collection'));
+ * return $result ? $result['data'] : array();
+ * }
+ * ));
+ * }}}
+ *
+ * This example defines an optional MongoDB convention in which the schema for each individual
+ * collection is stored in a "schemas" collection, where each document contains the name of
+ * a collection, along with a `'data'` key, which contains the schema for that collection.
+ *
+ * @see lithium\data\source\MongoDb::$_schema
* @var array
*/
protected $_schema = array();
@@ -340,8 +361,7 @@ public static function config(array $options = array()) {
*/
protected static function _findFilters() {
$self = static::_instance();
- $query =& $self->_query;
- $classes = $self->_classes;
+ $_query =& $self->_query;
return array(
'first' => function($self, $params, $chain) {
@@ -361,15 +381,15 @@ function($record) use (&$result, $meta) {
);
return $result;
},
- 'count' => function($self, $params, $chain) {
+ 'count' => function($self, $params, $chain) use ($_query) {
$model = $self;
$type = $params['type'];
- $options = array_filter($params['options']);
+ $options = array_diff_key($params['options'], $_query);
$classes = $options['classes'];
unset($options['classes']);
- if (!isset($options['conditions']) && $options) {
+ if ($options && !isset($options['conditions'])) {
$options = array('conditions' => $options);
}
$options += compact('classes', 'model');
@@ -393,26 +413,30 @@ function($record) use (&$result, $meta) {
*/
public static function __callStatic($method, $params) {
$self = static::_instance();
+ $isFinder = isset($self->_finders[$method]);
+
+ if ($isFinder && count($params) === 2 && is_array($params[1])) {
+ $params = array($params[1] + array($method => $params[0]));
+ }
- if ($method == 'all' || isset($self->_finders[$method])) {
- if (isset($params[0]) && (is_string($params[0]) || is_int($params[0]))) {
+ if ($method == 'all' || $isFinder) {
+ if ($params && is_scalar($params[0])) {
$params[0] = array('conditions' => array($self->_meta['key'] => $params[0]));
}
return $self::find($method, $params ? $params[0] : array());
}
- $pattern = '/^findBy(?P<field>\w+)$|^find(?P<type>\w+)By(?P<fields>\w+)$/';
+ preg_match('/^findBy(?P<field>\w+)$|^find(?P<type>\w+)By(?P<fields>\w+)$/', $method, $args);
- if (preg_match($pattern, $method, $m)) {
- $field = Inflector::underscore($m['field'] ? $m['field'] : $m['fields']);
- $type = isset($m['type']) ? $m['type'] : 'first';
- $type[0] = strtolower($type[0]);
-
- $conditions = array($field => array_shift($params));
- return $self::find($type, compact('conditions') + $params);
+ if (!$args) {
+ $message = "Method %s not defined or handled in class %s";
+ throw new BadMethodCallException(sprintf($message, $method, get_class($self)));
}
+ $field = Inflector::underscore($args['field'] ? $args['field'] : $args['fields']);
+ $type = isset($args['type']) ? $args['type'] : 'first';
+ $type[0] = strtolower($type[0]);
- $message = "Method %s not defined or handled in class %s";
- throw new BadMethodCallException(sprintf($message, $method, get_class($self)));
+ $conditions = array($field => array_shift($params));
+ return $self::find($type, compact('conditions') + $params);
}
/**
@@ -450,7 +474,7 @@ public static function find($type, array $options = array()) {
'conditions' => null, 'fields' => null, 'order' => null, 'limit' => null, 'page' => 1
);
- if ($type != 'all' && !isset($self->_finders[$type])) {
+ if ($type != 'all' && !isset($self->_finders[$type]) && is_scalar($type)) {
$options['conditions'] = array($self->_meta['key'] => $type);
$type = 'first';
}
@@ -671,7 +695,12 @@ public function save($record, $data = null, array $options = array()) {
$classes = $self->_classes;
$meta = array('model' => get_called_class()) + $self->_meta;
- $defaults = array('validate' => true, 'whitelist' => null, 'callbacks' => true);
+ $defaults = array(
+ 'validate' => true,
+ 'whitelist' => null,
+ 'callbacks' => true,
+ 'locked' => $self->_meta['locked'],
+ );
$options += $defaults + compact('classes');
$params = compact('record', 'data', 'options');
@@ -113,21 +113,6 @@ class Document extends \lithium\data\Collection {
'result', 'query', 'parent', 'exists', 'stats', 'pathKey'
);
- /**
- * Class constructor
- *
- * @param array $config
- * @return void
- */
- public function __construct(array $config = array()) {
- if (isset($config['data']) && !isset($config['items'])) {
- $config['items'] = $config['data'];
- unset($config['data']);
- }
- parent::__construct($config);
- $this->_items = (array) $this->_items;
- }
-
/**
* Magic PHP method used when model method is called on document instance.
* If no model is set returns `null`.
@@ -153,7 +138,6 @@ public function __call($method, $params = array()) {
* types in sub-`Document` objects.
*/
public function &__get($name) {
- $model = $this->_model;
$items = null;
$null = null;
@@ -171,11 +155,14 @@ public function &__get($name) {
return $current;
}
- foreach ($model::relations() as $relation => $config) {
- $linkKey = $config->data('key');
- if ($linkKey === $name) {
- $items = isset($this->_items[$name]) ? $this->_items[$name] : array();
- $this->_items[$name] = $this->_child('recordSet', $name, $items);
+ if ($model = $this->_model) {
+ foreach ($model::relations() as $relation => $config) {
+ $linkKey = $config->data('fieldName');
+ if ($linkKey === $name) {
+ $items = isset($this->_items[$name]) ? $this->_items[$name] : array();
+ $this->_items[$name] = $this->_relation('recordSet', $name, $items);
+ break;
+ }
}
}
@@ -185,7 +172,7 @@ public function &__get($name) {
$items = $items ?: $this->_items[$name];
if ($this->_isComplexType($items) && !$items instanceof Iterator) {
- $this->_items[$name] = $this->_child('recordSet', $name, $this->_items[$name]);
+ $this->_items[$name] = $this->_relation('recordSet', $name, $this->_items[$name]);
}
return $this->_items[$name];
}
@@ -215,15 +202,15 @@ public function __set($name, $value = null) {
$next = $current->__get($key);
if (!$next instanceof Document) {
- $next = $current->_items[$key] = $this->_child('recordSet', $key, array());
+ $next = $current->_items[$key] = $this->_relation('recordSet', $key, array());
}
$current = $next;
}
$current->__set(end($path), $value);
}
if ($this->_isComplexType($value) && !$value instanceof Iterator) {
- $value = $this->_child('recordSet', $name, $value);
+ $value = $this->_relation('recordSet', $name, $value);
}
$this->_items[$name] = $value;
}
@@ -409,15 +396,13 @@ protected function _isComplexType($data) {
* @return array
*/
protected function _populate($items = null, $key = null) {
- if ($this->_closed()) {
+ if ($this->closed()) {
return;
}
- $items = $items ?: $this->_handle->result('next', $this->_result, $this);
-
- if (!isset($items)) {
- return $this->_close();
+ if (($items = $items ?: $this->_handle->result('next', $this->_result, $this)) === null) {
+ return $this->close();
}
- return $this->_items[] = $this->_child('record', $key, $items);
+ return $this->_items[] = $this->_relation('record', $key, $items);
}
/**
@@ -429,13 +414,13 @@ protected function _populate($items = null, $key = null) {
* @param array $options
* @return object Returns a new `Document` object instance.
*/
- protected function _child($classType, $key, $items, $options = array()) {
+ protected function _relation($classType, $key, $items, $options = array()) {
$parent = $this;
- $model = $this->_model;
$exists = $this->_exists;
+ $key = ($key === null) ? count($this->_items) : $key;
$pathKey = trim("{$this->_pathKey}.{$key}", '.');
- if ($key) {
+ if (($key || $key === 0) && $model = $this->_model) {
foreach ($model::relations() as $name => $relation) {
$linkKey = $relation->data('key');
Oops, something went wrong.

0 comments on commit 1a4a9d0

Please sign in to comment.