/
DocumentSet.php
211 lines (187 loc) · 6.19 KB
/
DocumentSet.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<?php
/**
* Lithium: the most rad php framework
*
* @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/
namespace lithium\data\collection;
class DocumentSet extends \lithium\data\Collection {
/**
* PHP magic method used when setting properties on the `Document` instance, i.e.
* `$document->title = 'Lorem Ipsum'`. If `$value` is a complex data type (i.e. associative
* array), it is wrapped in a sub-`Document` object before being appended.
*
* @param $name The name of the field/property to write to, i.e. `title` in the above example.
* @param $value The value to write, i.e. `'Lorem Ipsum'`.
* @return void
*/
public function __set($name, $value = null) {
if (is_array($name) && !$value) {
foreach ($name as $key => $value) {
$this->__set($key, $value);
}
return;
}
if (is_string($name) && strpos($name, '.')) {
$current = $this;
$path = explode('.', $name);
$length = count($path) - 1;
for ($i = 0; $i < $length; $i++) {
$key = $path[$i];
$next = $current->__get($key);
if (!is_object($next) && ($model = $this->_model)) {
$next = $model::connection()->cast($this, $next);
$current->_data[$key] = $next;
}
$current = $next;
}
$current->__set(end($path), $value);
}
if (is_array($value)) {
$value = $this->_relation('set', $name, $value);
}
$this->_data[$name] = $value;
}
/**
* PHP magic method used to check the presence of a field as document properties, i.e.
* `$document->_id`.
*
* @param $name The field name, as specified with an object property.
* @return boolean True if the field specified in `$name` exists, false otherwise.
*/
public function __isset($name) {
return isset($this->_data[$name]);
}
/**
* PHP magic method used when unset() is called on a `Document` instance.
* Use case for this would be when you wish to edit a document and remove a field, ie. :
* {{{ $doc = Post::find($id); unset($doc->fieldName); $doc->save(); }}}
*
* @param unknown_type $name
* @return unknown_type
*/
public function __unset($name) {
unset($this->_data[$name]);
}
/**
* Allows several properties to be assigned at once.
*
* For example:
* {{{
* $doc->set(array('title' => 'Lorem Ipsum', 'value' => 42));
* }}}
*
* @param $values An associative array of fields and values to assign to the `Document`.
* @return void
*/
public function set($values) {
foreach ($values as $key => $val) {
$this[$key] = $val;
}
}
/**
* Allows document fields to be accessed as array keys, i.e. `$document['_id']`.
*
* @param mixed $offset String or integer indicating the offset or index of a document in a set,
* or the name of a field in an individual document.
* @return mixed Returns either a sub-object in the document, or a scalar field value.
*/
public function offsetGet($offset) {
$data = null;
$null = null;
$model = $this->_model;
if (!isset($this->_data[$offset]) && !$data = $this->_populate(null, $offset)) {
return $null;
}
if (is_array($data = $this->_data[$offset]) && $model) {
$this->_data[$offset] = $model::connection()->cast($this, $data);
}
if (isset($this->_data[$offset])) {
return $this->_data[$offset];
}
return $null;
}
/**
* Rewinds the collection of sub-`Document`s to the beginning and returns the first one found.
*
* @return object Returns the first `Document` object instance in the collection.
*/
public function rewind() {
$data = parent::rewind() ?: $this->_populate();
$key = key($this->_data);
if (is_object($data)) {
return $data;
}
if (isset($this->_data[$key])) {
return $this->offsetGet($key);
}
}
public function current() {
return $this->offsetGet(key($this->_data));
}
/**
* Returns the next document in the set, and advances the object's internal pointer. If the end
* of the set is reached, a new document will be fetched from the data source connection handle
* If no more documents can be fetched, returns `null`.
*
* @return mixed Returns the next document in the set, or `null`, if no more documents are
* available.
*/
public function next() {
$prev = key($this->_data);
$this->_valid = !(next($this->_data) === false && key($this->_data) === null);
$cur = key($this->_data);
if (!$this->_valid && $cur !== $prev && $cur !== null) {
$this->_valid = true;
}
$this->_valid = $this->_valid ?: !is_null($this->_populate());
return $this->_valid ? $this->offsetGet(key($this->_data)) : null;
}
public function export(array $options = array()) {
$map = function($doc) use ($options) {
return is_array($doc) ? $doc : $doc->export();
};
return array_map($map, $this->_data);
}
/**
* Lazy-loads a document from a query using a reference to a database adapter and a query
* result resource.
*
* @param array $data
* @param mixed $key
* @return array
*/
protected function _populate($data = null, $key = null) {
if ($this->closed() || !($model = $this->_model)) {
return;
}
$conn = $model::connection();
if (($data = $data ?: $this->_result->next()) === null) {
return $this->close();
}
$options = array('exists' => true, 'first' => true, 'pathKey' => $this->_pathKey);
return $this->_data[] = $conn->cast($this, array($key => $data), $options);
}
/**
* Instantiates a new `Document` object as a descendant of the current object, and sets all
* default values and internal state.
*
* @param string $classType The type of class to create, either `'entity'` or `'set'`.
* @param string $key The key name to which the related object is assigned.
* @param array $data The internal data of the related object.
* @param array $options Any other options to pass when instantiating the related object.
* @return object Returns a new `Document` object instance.
*/
protected function _relation($classType, $key, $data, $options = array()) {
$parent = $this;
$model = $this->_model;
if (is_object($data) && $data instanceof Document) {
$data->assignTo($this, compact('model', 'pathKey'));
return $data;
}
$options += compact('model', 'data', 'parent');
return new $this->_classes[$classType]($options);
}
}
?>