Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 399 lines (365 sloc) 11.65 kb
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
1 <?php
2 /**
3 * Lithium: the most rad php framework
4 *
14de7bf @gwoo Happy 2012!
gwoo authored
5 * @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
6 * @license http://opensource.org/licenses/bsd-license.php The BSD License
7 */
8
9 namespace lithium\data;
10
00ce46c @nateabele Implemented core exceptions. Implemented `\core\Libraries::instance()` t...
nateabele authored
11 use BadMethodCallException;
318a6dd @nateabele Adding test case for `\data\Entity`.
nateabele authored
12 use UnexpectedValueException;
ab41ff6 @nateabele First pass at refactoring SQL relationship support.
nateabele authored
13 use lithium\data\Collection;
e8c2c6a @Howard3 Adding a fix to retain fidelity on exporting document data as an array.
Howard3 authored
14 use lithium\util\Set;
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
15
16 /**
a93f529 @nateabele Expanding class documentation for `\data\Entity`.
nateabele authored
17 * `Entity` is a smart data object which represents data such as a row or document in a
18 * database. Entities have fields (often known as columns in databases), and track changes to its
19 * fields, as well as associated validation errors, etc.
20 *
21 * The `Entity` class can also be used as a base class for your own custom data objects, and is the
22 * basis for generating forms with the `Form` helper.
23 *
24 * @see lithium\template\helper\Form
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
25 */
26 class Entity extends \lithium\core\Object {
27
28 /**
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
29 * Fully-namespaced class name of model that this record is bound to. Instance methods declared
30 * in the model may be called on the entity. See the `Model` class documentation for more
31 * information.
32 *
33 * @see lithium\data\Model
34 * @see lithium\data\Entity::__call()
35 * @var string
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
36 */
37 protected $_model = null;
38
39 /**
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
40 * Associative array of the entity's fields and values.
39e131a @nateabele Updating CRUD integration test, and fixing CRUD operations for CouchDB. ...
nateabele authored
41 *
42 * @var array
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
43 */
44 protected $_data = array();
45
46 /**
47 * An array containing all related records and recordsets, keyed by relationship name, as
48 * defined in the bound model class.
49 *
50 * @var array
51 */
52 protected $_relationships = array();
53
54 /**
55 * If this record is chained off of another, contains the origin object.
56 *
57 * @var object
58 */
59 protected $_parent = null;
60
61 /**
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
62 * The list of validation errors associated with this object, where keys are field names, and
63 * values are arrays containing one or more validation error messages.
64 *
65 * @see lithium\data\Entity::errors()
66 * @var array
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
67 */
68 protected $_errors = array();
69
70 /**
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
71 * Contains the values of updated fields. These values will be persisted to the backend data
72 * store when the document is saved.
73 *
74 * @var array
75 */
76 protected $_updated = array();
77
78 /**
79 * An array of key/value pairs corresponding to fields that should be updated using atomic
80 * incrementing / decrementing operations. Keys match field names, and values indicate the value
a0930f4 @phishy fixed spelling errors
phishy authored
81 * each field should be incremented or decremented by.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
82 *
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
83 * @see lithium\data\Entity::increment()
84 * @see lithium\data\Entity::decrement()
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
85 * @var array
86 */
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
87 protected $_increment = array();
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
88
89 /**
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
90 * A flag indicating whether or not this entity exists. Set to `false` if this is a
91 * newly-created entity, or if this entity has been loaded and subsequently deleted. Set to
92 * `true` if the entity has been loaded from the database, or has been created and subsequently
93 * saved.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
94 *
95 * @var boolean
96 */
97 protected $_exists = false;
98
99 /**
100 * A local copy of the schema definition. This is the same as `lithium\data\Model::$_schema`,
101 * but can be defined here if this is a one-off object or class used for a single purpose, i.e.
102 * to create a form.
103 *
104 * @var array
105 */
106 protected $_schema = array();
107
108 /**
109 * Auto configuration.
110 *
111 * @var array
112 */
113 protected $_autoConfig = array(
6857c6b @nateabele Data in `\data\Entity` now shows up above related data in calls to `data...
nateabele authored
114 'classes' => 'merge', 'parent', 'schema', 'data',
115 'model', 'exists', 'pathKey', 'relationships'
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
116 );
117
118 /**
119 * Creates a new record object with default values.
120 *
121 * Options defined:
122 * - 'data' _array_: Data to enter into the record. Defaults to an empty array.
123 * - 'model' _string_: Class name that provides the data-source for this record.
124 * Defaults to `null`.
125 *
126 * @param array $config
127 * @return object Record object.
128 */
129 public function __construct(array $config = array()) {
4fa7aa7 @Howard3 Reworking Relationships
Howard3 authored
130 $defaults = array('model' => null, 'data' => array(), 'relationships' => array());
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
131 parent::__construct($config + $defaults);
132 }
133
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
134 protected function _init() {
135 parent::_init();
136 $this->_updated = $this->_data;
137 }
138
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
139 /**
140 * Overloading for reading inaccessible properties.
141 *
142 * @param string $name Property name.
143 * @return mixed Result.
144 */
145 public function &__get($name) {
146 if (isset($this->_relationships[$name])) {
147 return $this->_relationships[$name];
148 }
39e131a @nateabele Updating CRUD integration test, and fixing CRUD operations for CouchDB. ...
nateabele authored
149 if (isset($this->_updated[$name])) {
150 return $this->_updated[$name];
151 }
a39b648 @nateabele Stripping out more dead and refactored code.
nateabele authored
152 $null = null;
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
153 return $null;
154 }
155
156 /**
157 * Overloading for writing to inaccessible properties.
158 *
159 * @param string $name Property name.
160 * @param string $value Property value.
161 * @return mixed Result.
162 */
b51972c @nateabele Beginning refactoring of... stuff.
nateabele authored
163 public function __set($name, $value = null) {
164 if (is_array($name) && !$value) {
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
165 return array_map(array(&$this, '__set'), array_keys($name), array_values($name));
b51972c @nateabele Beginning refactoring of... stuff.
nateabele authored
166 }
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
167 $this->_updated[$name] = $value;
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
168 }
169
170 /**
171 * Overloading for calling `isset()` or `empty()` on inaccessible properties.
172 *
173 * @param string $name Property name.
174 * @return mixed Result.
175 */
176 public function __isset($name) {
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
177 return isset($this->_updated[$name]);
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
178 }
179
180 /**
181 * Magic method that allows calling of model methods on this record instance, i.e.:
182 * {{{
183 * $record->validates();
184 * }}}
185 *
186 * @param string $method
187 * @param array $params
188 * @return mixed
189 */
190 public function __call($method, $params) {
191 if (!($model = $this->_model) || !method_exists($model, $method)) {
583059b @davidpersson Adding and replacing ticks/quotes with backticks in error and exception ...
davidpersson authored
192 $message = "No model bound or unhandled method call `{$method}`.";
c219785 @nateabele Implementing nested object support in `Form` helper.
nateabele authored
193 throw new BadMethodCallException($message);
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
194 }
195 array_unshift($params, $this);
a81b5d4 @nateabele Renaming `\data\Model::_instance()` to `\data\Model::_object()`.
nateabele authored
196 $class = $model::invokeMethod('_object');
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
197 return call_user_func_array(array(&$class, $method), $params);
198 }
199
200 /**
201 * Allows several properties to be assigned at once, i.e.:
202 * {{{
203 * $record->set(array('title' => 'Lorem Ipsum', 'value' => 42));
204 * }}}
205 *
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
206 * @param array $data An associative array of fields and values to assign to this `Entity`
207 * instance.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
208 * @return void
209 */
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
210 public function set(array $data) {
211 foreach ($data as $name => $value) {
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
212 $this->__set($name, $value);
213 }
214 }
215
216 /**
379e432 @nateabele Fixing docblock formatting in `\data\Entity`, and adding a minor fix to ...
nateabele authored
217 * Access the data fields of the record. Can also access a $named field.
218 *
219 * @param string $name Optionally included field name.
c25e039 @Howard3 Cleaning up PHPDoc @return tags with multiple return types defined.
Howard3 authored
220 * @return mixed Entire data array if $name is empty, otherwise the value from the named field.
379e432 @nateabele Fixing docblock formatting in `\data\Entity`, and adding a minor fix to ...
nateabele authored
221 */
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
222 public function data($name = null) {
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
223 if ($name) {
224 return $this->__get($name);
225 }
d9ee039 @Howard3 Document: to(array) now works recursively. Patch by Nate Abele
Howard3 authored
226 return $this->to('array');
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
227 }
228
229 /**
230 * Returns the model which this entity is bound to.
231 *
232 * @return string The fully qualified model class name.
233 */
234 public function model() {
235 return $this->_model;
236 }
237
238 public function schema($field = null) {
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
239 $schema = array();
240
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
241 switch (true) {
242 case ($this->_schema):
243 $schema = $this->_schema;
244 break;
245 case ($model = $this->_model):
246 $schema = $model::schema();
247 break;
248 }
249 if ($field) {
318a6dd @nateabele Adding test case for `\data\Entity`.
nateabele authored
250 return isset($schema[$field]) ? $schema[$field] : null;
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
251 }
252 return $schema;
253 }
254
255 /**
256 * Access the errors of the record.
257 *
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
258 * @see lithium\data\Entity::$_errors
259 * @param array|string $field If an array, overwrites `$this->_errors`. If a string, and
260 * `$value` is not `null`, sets the corresponding key in `$this->_errors` to `$value`.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
261 * @param string $value Value to set.
c25e039 @Howard3 Cleaning up PHPDoc @return tags with multiple return types defined.
Howard3 authored
262 * @return mixed Either the `$this->_errors` array, or single value from it.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
263 */
264 public function errors($field = null, $value = null) {
265 if ($field === null) {
266 return $this->_errors;
267 }
268 if (is_array($field)) {
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
269 return ($this->_errors = $field);
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
270 }
271 if ($value === null && isset($this->_errors[$field])) {
272 return $this->_errors[$field];
273 }
274 if ($value !== null) {
275 return $this->_errors[$field] = $value;
276 }
277 return $value;
278 }
279
280 /**
281 * A flag indicating whether or not this record exists.
282 *
283 * @return boolean `True` if the record was `read` from the data-source, or has been `create`d
284 * and `save`d. Otherwise `false`.
285 */
286 public function exists() {
287 return $this->_exists;
288 }
289
290 /**
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
291 * Called after an `Entity` is saved. Updates the object's internal state to reflect the
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
292 * corresponding database entity, and sets the `Entity` object's key, if this is a newly-created
ad3ccf3 @nateabele Cleanup and documentation for misc. `\data` classes. Refactored `Documen...
nateabele authored
293 * object. **Do not** call this method if you intend to update the database's copy of the
294 * entity. Instead, see `Model::save()`.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
295 *
ad3ccf3 @nateabele Cleanup and documentation for misc. `\data` classes. Refactored `Documen...
nateabele authored
296 * @see lithium\data\Model::save()
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
297 * @param mixed $id The ID to assign, where applicable.
298 * @param array $data Any additional generated data assigned to the object by the database.
b58092f @nateabele Renaming `Entity::update()` to `Entity::sync()` to better reflect functi...
nateabele authored
299 * @param array $options Method options:
300 * - `'materialize'` _boolean_: Determines whether or not the flag should be set
301 * that indicates that this entity exists in the data store. Defaults to `true`.
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
302 * @return void
303 */
b58092f @nateabele Renaming `Entity::update()` to `Entity::sync()` to better reflect functi...
nateabele authored
304 public function sync($id = null, array $data = array(), array $options = array()) {
305 $defaults = array('materialize' => true);
306 $options += $defaults;
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
307 $model = $this->_model;
308 $key = array();
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
309
b58092f @nateabele Renaming `Entity::update()` to `Entity::sync()` to better reflect functi...
nateabele authored
310 if ($options['materialize']) {
311 $this->_exists = true;
312 }
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
313 if ($id && $model) {
314 $key = $model::meta('key');
315 $key = is_array($key) ? array_combine($key, $id) : array($key => $id);
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
316 }
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
317 $this->_data = $this->_updated = ($key + $data + $this->_updated);
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
318 }
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
319
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
320 /**
321 * Safely (atomically) increments the value of the specified field by an arbitrary value.
322 * Defaults to `1` if no value is specified. Throws an exception if the specified field is
323 * non-numeric.
324 *
a0930f4 @phishy fixed spelling errors
phishy authored
325 * @param string $field The name of the field to be incremented.
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
326 * @param string $value The value to increment the field by. Defaults to `1` if this parameter
327 * is not specified.
6cbb2cd @nateabele Fixing misc. QA issues and adding docblocks.
nateabele authored
328 * @return integer Returns the current value of `$field`, based on the value retrieved from the
329 * data source when the entity was loaded, plus any increments applied. Note that it may
330 * not reflect the most current value in the persistent backend data source.
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
331 * @throws UnexpectedValueException Throws an exception when `$field` is set to a non-numeric
332 * type.
333 */
334 public function increment($field, $value = 1) {
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
335 if (!isset($this->_updated[$field])) {
336 return $this->_updated[$field] = $value;
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
337 }
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
338 if (!is_numeric($this->_updated[$field])) {
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
339 throw new UnexpectedValueException("Field '{$field}' cannot be incremented.");
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
340 }
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
341 return $this->_updated[$field] += $value;
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
342 }
343
344 /**
345 * Decrements a field by the specified value. Works identically to `increment()`, but in
346 * reverse.
347 *
348 * @see lithium\data\Entity::increment()
349 * @param string $field The name of the field to decrement.
350 * @param string $value The value by which to decrement the field. Defaults to `1`.
6cbb2cd @nateabele Fixing misc. QA issues and adding docblocks.
nateabele authored
351 * @return integer Returns the new value of `$field`, after modification.
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
352 */
353 public function decrement($field, $value = 1) {
354 return $this->increment($field, $value * -1);
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
355 }
356
357 /**
358 * Gets the array of fields modified on this entity.
359 *
360 * @return array Returns an array where the keys are entity field names, and the values are
361 * always `true`.
362 */
363 public function modified() {
a0e3f7d @Howard3 Related to #155 - simplifying Entity->modified with more up-to-date synt...
Howard3 authored
364 return array_fill_keys(array_keys($this->_updated), true);
f7ae4c1 @nateabele Refactoring base classes in `\data`, implementing one-to-many relationsh...
nateabele authored
365 }
366
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
367 public function export() {
368 return array(
369 'exists' => $this->_exists,
370 'data' => $this->_data,
371 'update' => $this->_updated,
26c2192 @davidpersson QA: Removing trailing comma in arrays, lowercasing some keywords.
davidpersson authored
372 'increment' => $this->_increment
eaa5770 @nateabele Rewriting change-tracking in data entities. Original state and changes a...
nateabele authored
373 );
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
374 }
375
376 /**
377 * Converts the data in the record set to a different format, i.e. an array.
378 *
379 * @param string $format currently only `array`
380 * @param array $options
381 * @return mixed
382 */
383 public function to($format, array $options = array()) {
384 switch ($format) {
385 case 'array':
18b6a8c @nateabele Refactoring MongoDB changeset calculation for update operations, to impr...
nateabele authored
386 $data = $this->_updated;
4e7d186 @nateabele Fixing array mapping in `Entity` and `Document` classes.
nateabele authored
387 $rel = array_map(function($obj) { return $obj->data(); }, $this->_relationships);
dae6dcd @Howard3 Updating entity to concat arrays instead of merging, this makes it more ...
Howard3 authored
388 $data = $rel + $data;
4e7d186 @nateabele Fixing array mapping in `Entity` and `Document` classes.
nateabele authored
389 $result = Collection::toArray($data, $options);
27bd48e @nateabele Adding missing files from last commit.
nateabele authored
390 break;
391 default:
392 $result = $this;
393 break;
394 }
395 return $result;
396 }
397 }
398
1281928 @daschl QA: Misc. Fixes
daschl authored
399 ?>
Something went wrong with that request. Please try again.