Completed
Pull Request — master (#87)
by Georgi
12:00 queued 09:37
created

Kohana_Jam_Model   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 555
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 87.04%

Importance

Changes 0
Metric Value
wmc 75
lcom 1
cbo 14
dl 0
loc 555
ccs 215
cts 247
cp 0.8704
rs 2.4
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A __isset() 0 4 2
A check() 0 6 2
A clear() 0 9 1
A loaded_insist() 0 7 2
A saved() 0 4 1
A is_saving() 0 4 1
A deleted() 0 4 1
A duplicate() 0 8 1
A __toString() 0 4 2
A get() 0 11 2
B set() 0 31 7
B load_fields() 0 52 7
A _move_retrieved_to_changed() 0 10 4
A update_fields() 0 19 3
A get_insist() 0 12 2
A serialize() 0 13 1
F save() 0 118 22
A delete() 0 26 3
A revert() 0 12 2
A build() 0 16 3
A loaded() 0 4 1
A unserialize() 0 21 4

How to fix   Complexity   

Complex Class

Complex classes like Kohana_Jam_Model often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Kohana_Jam_Model, and based on these observations, apply Extract Interface, too.

1
<?php defined('SYSPATH') OR die('No direct script access.');
2
/**
3
 * Jam Model
4
 *
5
 * Jam_Model is the class all models must extend. It handles
6
 * various CRUD operations and relationships to other models.
7
 *
8
 * @package    Jam
9
 * @category   Models
10
 * @author     Ivan Kerin
11
 * @copyright  (c) 2011-2012 Despark Ltd.
12
 * @license    http://www.opensource.org/licenses/isc-license.txt
13
 */
14
abstract class Kohana_Jam_Model extends Jam_Validated {
15
16
	/**
17
	 * @var  boolean  Whether or not the model is loaded
18
	 */
19
	protected $_loaded = FALSE;
20
21
	/**
22
	 * @var  boolean  Whether or not the model is saved
23
	 */
24
	protected $_saved = FALSE;
25
26
	/**
27
	 * @var  boolean  Whether or not the model is saving
28
	 */
29
	protected $_is_saving = FALSE;
30
31
	/**
32
	 * @var  Boolean  A flag that indicates a record has been deleted from the database
33
	 */
34
	 protected $_deleted = FALSE;
35
36
	/**
37
	 * Constructor.
38
	 *
39
	 * A key can be passed to automatically load a model by its
40
	 * unique key.
41
	 *
42
	 * @param  mixed|null  $key
0 ignored issues
show
Bug introduced by
There is no parameter named $key. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
43
	 */
44 441
	public function __construct($meta_name = NULL)
45
	{
46 441
		parent::__construct($meta_name);
47
48 441
		$this->meta()->events()->trigger('model.before_construct', $this);
49
50
		// Copy over the defaults into the original data.
51 441
		$this->_original = $this->meta()->defaults();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->meta()->defaults() of type * is incompatible with the declared type array of property $_original.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
52
53 441
		$this->meta()->events()->trigger('model.after_construct', $this);
54 441
	}
55
56
	/**
57
	 * Gets the value for a field.
58
	 *
59
	 * @param   string       $name The field's name
60
	 * @return  array|mixed
61
	 */
62 218
	public function get($name)
63
	{
64 218
		if ($association = $this->meta()->association($name))
65 218
		{
66 20
			$name = $association->name;
67
68 20
			return $association->get($this, Arr::get($this->_changed, $name), $this->changed($name));
0 ignored issues
show
Bug introduced by
The method get does only exist in Jam_Association, but not in Kohana_Jam_Meta.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
69
		}
70
71 217
		return parent::get($name);
72
	}
73
74
	public function __isset($name)
75
	{
76
		return ($this->meta()->association($name) OR parent::__isset($name));
77
	}
78
79
	/**
80
	 * Sets the value of a field.
81
	 *
82
	 * You can pass an array of key => value pairs
83
	 * to set multiple fields at the same time:
84
	 *
85
	 *    $model->set(array(
86
	 *        'field1' => 'value',
87
	 *        'field2' => 'value',
88
	 *         ....
89
	 *    ));
90
	 *
91
	 * @param   array|string  $values
92
	 * @param   mixed|null    $value
93
	 * @return  $this
94
	 */
95 95
	public function set($values, $value = NULL)
96
	{
97
		// Accept set('name', 'value');
98 95
		if ( ! is_array($values))
99 95
		{
100 80
			$values = array($values => $value);
101 80
		}
102
103 95
		foreach ($values as $key => & $value)
104
		{
105 95
			if ($association = $this->meta()->association($key))
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $association is correct as $this->meta()->association($key) (which targets Kohana_Jam_Meta::association()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
106 95
			{
107 20
				if ($association->readonly)
108 20
					throw new Kohana_Exception('Cannot change the value of :name, its readonly', array(':name' => $association->name));
109
110 20
				$this->_changed[$association->name] = $association->set($this, $value, TRUE);
111
112 20
				unset($this->_retrieved[$association->name]);
113 20
				unset($values[$key]);
114 20
			}
115 95
		}
116
117 95
		parent::set($values);
118
119 95
		if ($this->_saved AND $this->changed())
120 95
		{
121 27
			$this->_saved = FALSE;
122 27
		}
123
124 95
		return $this;
125
	}
126
127
	/**
128
	 * Clears the object and loads an array of values into the object.
129
	 *
130
	 * This should only be used for setting from database results
131
	 * since the model declares itself as saved and loaded after.
132
	 *
133
	 * @param   Jam_Model|array  $values
134
	 * @return  $this
135
	 */
136 198
	public function load_fields($values)
137
	{
138
		// Clear the object
139 198
		$this->clear();
140
141 198
		$this->meta()->events()->trigger('model.before_load', $this);
142
143 198
		$this->_loaded = TRUE;
144
145 198
		$columns = array();
146
147 198
		foreach ($this->meta()->fields() as $key => $field)
0 ignored issues
show
Bug introduced by
The expression $this->meta()->fields() of type array|object<Jam_Meta> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
148
		{
149 198
			$columns[$field->column] = $field;
150 198
		}
151
152 198
		foreach ($values as $key => $value)
0 ignored issues
show
Bug introduced by
The expression $values of type object<Jam_Model>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
153
		{
154 195
			if ($field = $this->meta()->field($key))
155 195
			{
156 195
				$this->_original[$field->name] = $field->set($this, $value, FALSE);
0 ignored issues
show
Unused Code introduced by
The call to Kohana_Jam_Meta::set() has too many arguments starting with FALSE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
157 195
			}
158 48
			elseif (isset($columns[$key]))
159
			{
160
				$field = $columns[$key];
161 1
				$this->_original[$field->name] = $field->set($this, $value, FALSE);
162
			}
163 48
			elseif ($association = $this->meta()->association($key))
164
			{
165 8
				$association_value = $association->load_fields($this, $value, FALSE);
166 8
				if (is_object($association_value))
167 8
				{
168 8
					$this->_retrieved[$association->name] = $association->load_fields($this, $value, FALSE);
169 8
				}
170
				else
171
				{
172 1
					$this->_changed[$association->name] = $association_value;
173
				}
174 8
			}
175
			else
176
			{
177 40
				$this->_unmapped[$key] = $value;
178
			}
179 198
		}
180
181
		// Model is now saved and loaded
182 198
		$this->_saved = TRUE;
183
184 198
		$this->meta()->events()->trigger('model.after_load', $this);
185
186 198
		return $this;
187
	}
188
189
	/**
190
	 * Validates the current model's data
191
	 *
192
	 * @throws  Jam_Exception_Validation
193
	 * @param   Validation|null   $extra_validation
0 ignored issues
show
Bug introduced by
There is no parameter named $extra_validation. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
194
	 * @return  bool
195
	 */
196 21
	public function check($force = FALSE)
197
	{
198 21
		$this->_move_retrieved_to_changed();
199
200 21
		return parent::check( ! $this->loaded() OR $force);
201
	}
202
203 756
	protected function _move_retrieved_to_changed()
204
	{
205 756
		foreach ($this->_retrieved as $column => $value)
206
		{
207 19
			if ($value instanceof Jam_Array_Association	AND $value->changed())
208 19
			{
209 5
				$this->_changed[$column] = $value;
210 5
			}
211 756
		}
212 756
	}
213
214 15
	public function update_fields($values, $value = NULL)
215
	{
216 15
		if ( ! $this->loaded())
217
			throw new Kohana_Exception('Model must be loaded to use update_fields method');
218
219
		if ( ! is_array($values))
220
		{
221
			$values = array($values => $value);
222
		}
223
224
		$this->set($values);
225
226
		Jam::update($this)
227
			->where_key($this->id())
228
			->set($values)
229
			->execute();
230
231
		return $this;
232
	}
233
234
	/**
235
	 * Creates or updates the current record.
236
	 *
237
	 * @param   bool|null        $validate
238
	 * @return  $this
239
	 * @throws Kohana_Exception
240
	 * @throws Jam_Exception_Validation
241
	 */
242 80
	public function save($validate = NULL)
243
	{
244 20
		if ($this->_is_saving)
245 20
			throw new Kohana_Exception("Cannot save a model that is already in the process of saving");
246
247
248 20
		$key = $this->_original[$this->meta()->primary_key()];
249
250
		// Run validation
251 20
		if ($validate !== FALSE && !$this->check())
252 20
		{
253
			throw new Jam_Exception_Validation('There was an error validating the :model: :errors', $this);
254
		}
255
256 20
		$this->_is_saving = TRUE;
257
258
		// These will be processed later
259 20
		$values = $defaults = array();
260
261 20
		if ($this->meta()->events()->trigger('model.before_save', $this, array($this->_changed)) === FALSE)
262 20
		{
263
			return $this;
264
		}
265
266
		// Trigger callbacks and ensure we should proceed
267 20
		$event_type = $key ? 'update' : 'create';
268
269 20
		if ($this->meta()->events()->trigger('model.before_'.$event_type, $this, array($this->_changed)) === FALSE)
270 20
		{
271
			return $this;
272
		}
273
274 20
		$this->_move_retrieved_to_changed();
275
276
		// Iterate through all fields in original in case any unchanged fields
277
		// have convert() behavior like timestamp updating...
278
		//
279 20
		foreach (array_merge($this->_original, $this->_changed) as $column => $value)
280
		{
281 20
			if ($field = $this->meta()->field($column))
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $field is correct as $this->meta()->field($column) (which targets Kohana_Jam_Meta::field()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
282 20
			{
283
				// Only save in_db values
284 20
				if ($field->in_db)
285 20
				{
286
					// See if field wants to alter the value on save()
287 20
					$value = $field->convert($this, $value, $key);
288
289
					// Only set the value to be saved if it's changed from the original
290 20
					if ($value !== $this->_original[$column])
291 20
					{
292 80
						$values[$field->column] = $value;
293 16
					}
294
					// Or if we're INSERTing and we need to set the defaults for the first time
295 20
					elseif ( ! $key AND ( ! $this->changed($field->name) OR $field->default === $value) AND ! $field->primary)
296
					{
297 18
						$defaults[$field->column] = $field->default;
298 18
					}
299 20
				}
300 20
			}
301 20
		}
302
303
		// If we have a key, we're updating
304
		if ($key)
305 20
		{
306
			// Do we even have to update anything in the row?
307
			if ($values)
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
308 4
			{
309 3
				Jam::update($this)
310 3
					->where_key($key)
311 3
					->set($values)
312 3
					->execute();
313 3
			}
314 4
		}
315
		else
316
		{
317 18
			$insert_values = array_merge($defaults, $values);
318 18
			list($id) = Jam::insert($this)
319 18
				->columns(array_keys($insert_values))
320 18
				->values(array_values($insert_values))
321 18
				->execute();
322
323
			// Gotta make sure to set this
324 18
			$key = $values[$this->meta()->primary_key()] = $id;
0 ignored issues
show
Unused Code introduced by
$key is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
325
		}
326
327
		// Re-set any saved values; they may have changed
328 20
		$this->set($values);
329
330 20
		$this->_loaded = $this->_saved = TRUE;
331
332 20
		$this->meta()->events()->trigger('model.after_save', $this, array($this->_changed, $event_type));
333
334 20
		$this->meta()->events()->trigger('model.after_'.$event_type, $this, array($this->_changed));
335
336
		// Set the changed data back as original
337 20
		$this->_original = array_merge($this->_original, $this->_changed);
338
339 20
		$this->_changed = array();
340
341 20
		foreach ($this->_retrieved as $name => $retrieved)
342
		{
343 17
			if (($association = $this->meta()->association($name)))
344 17
			{
345 2
				if ($association instanceof Jam_Association_Collection)
346 2
				{
347 1
					$retrieved->clear_changed();
348 1
				}
349 2
			}
350 17
			elseif (($field = $this->meta()->field($name)) AND $field->in_db)
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $field is correct as $this->meta()->field($name) (which targets Kohana_Jam_Meta::field()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
351
			{
352 17
				unset($this->_retrieved[$name]);
353 17
			}
354 20
		}
355
356 20
		$this->_is_saving = FALSE;
357
358 20
		return $this;
359
	}
360
361
	/**
362
	 * Deletes a single record.
363
	 *
364
	 * @return  boolean
365
	 **/
366 9
	public function delete()
367 1
	{
368 9
		$result = FALSE;
369
370
		// Are we loaded? Then we're just deleting this record
371 9
		if ($this->_loaded)
372 9
		{
373 9
			$key = $this->_original[$this->meta()->primary_key()];
374
375 9
			if (($result = $this->meta()->events()->trigger('model.before_delete', $this)) !== FALSE)
376 9
			{
377 9
				$result = Jam::delete($this)->where_key($key)->execute();
378 9
			}
379 9
		}
380
381
		// Trigger the after-delete
382 9
		$this->meta()->events()->trigger('model.after_delete', $this, array($result));
383
384
		// Clear the object so it appears deleted anyway
385 9
		$this->clear();
386
387
		// Set the flag indicatig the model has been successfully deleted
388 9
		$this->_deleted = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result can also be of type object. However, the property $_deleted is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
389
390 9
		return $this->_deleted;
391
	}
392
393
	/**
394
	 * Removes any changes made to a model.
395
	 *
396
	 * This method only works on loaded models.
397
	 *
398
	 * @return  $this
399
	 */
400
	public function revert()
401
	{
402
		if ($this->_loaded)
403
		{
404
			$this->_loaded =
405
			$this->_saved  = TRUE;
406
407
			parent::revert();
408
		}
409
410
		return $this;
411
	}
412
413
	/**
414
	 * Sets a model to its original state, as if freshly instantiated
415
	 *
416
	 * @return  $this
417
	 */
418 198
	public function clear()
419
	{
420 198
		$this->_loaded =
421 198
		$this->_saved  = FALSE;
422
423 198
		parent::clear();
424
425 198
		return $this;
426
	}
427
428 1
	public function get_insist($attribute_name)
429
	{
430 1
		$attribute = $this->__get($attribute_name);
431
432 1
		if ($attribute === NULL)
433 1
			throw new Jam_Exception_Notfound('The association :name was empty on :model_name', NULL, array(
434 1
				':name' => $attribute_name,
435 1
				':model_name' => (string) $this,
436 1
			));
437
438 1
		return $attribute;
439
	}
440
441 1
	public function build($association_name, array $attributes = array())
442
	{
443 1
		$association = $this->meta()->association($association_name);
444
445 1
		if ( ! $association)
446 1
			throw new Kohana_Exception('There is no association named :association_name on model :model', array(':association_name' => $association_name, ':model' => $this->meta()->model()));
447
448 1
		if ($association instanceof Jam_Association_Collection)
449 1
			throw new Kohana_Exception(':association_name association must not be a collection on model :model', array(':association_name' => $association_name, ':model' => $this->meta()->model()));
450
451 1
		$item = $association->build($this, $attributes);
452
453 1
		$this->set($association_name, $item);
454
455 1
		return $item;
456
	}
457
458
	/**
459
	 * Returns whether or not the model is loaded
460
	 *
461
	 * @return  boolean
462
	 */
463 93
	public function loaded()
464
	{
465 93
		return $this->_loaded;
466 1
	}
467
468
	public function loaded_insist()
469
	{
470
		if ( ! $this->loaded())
471
			throw new Jam_Exception_NotLoaded("Model not loaded", $this);
472
473
		return $this;
474
	}
475
476
	/**
477
	 * Whether or not the model is saved
478
	 *
479
	 * @return  boolean
480
	 */
481 10
	public function saved()
482
	{
483 10
		return $this->_saved;
484
	}
485
486
	/**
487
	 * Whether or not the model is in the process of being saved
488
	 *
489
	 * @return  boolean
490
	 */
491 4
	public function is_saving()
492
	{
493 4
		return $this->_is_saving;
494
	}
495
496
	/**
497
	 * Whether or not the model is deleted
498
	 *
499
	 * @return  boolean
500
	 */
501
	public function deleted()
502
	{
503
		return $this->_deleted;
504
	}
505
506
	/**
507
	 * Build a new model object based on the current one, but without an ID, so it can be saved as a new object
508
	 * @return Jam_Model
509
	 */
510 1
	public function duplicate()
511
	{
512 1
		$fields = $this->as_array();
513
514 1
		unset($fields[$this->meta()->primary_key()]);
515
516 1
		return Jam::build($this->meta()->model(), $fields);
517
	}
518
519
	/**
520
	 * Returns a string representation of the model in the
521
	 * form of `Model_Name (id)` or `Model_Name (NULL)` if
522
	 * the model is not loaded.
523
	 *
524
	 * This is designed to be useful for debugging.
525
	 *
526
	 * @return  string
527
	 */
528 4
	public function __toString()
529
	{
530 4
		return (string) get_class($this).'('.($this->loaded() ? $this->id() : 'NULL').')';
531
	}
532
533 756
	public function serialize()
534
	{
535 756
		$this->_move_retrieved_to_changed();
536
537 756
		return serialize(array(
538 756
			'original' => $this->_original,
539 756
			'changed' => $this->_changed,
540 756
			'unmapped' => $this->_unmapped,
541 756
			'saved' => $this->_saved,
542 756
			'loaded' => $this->_loaded,
543 756
			'deleted' => $this->_deleted,
544 756
		));
545
	}
546
547 756
	public function unserialize($data)
548
	{
549 756
		$data = unserialize($data);
550
551 756
		$this->_meta = Jam::meta($this);
552 756
		$this->_original = Arr::merge($this->meta()->defaults(), $data['original']);
553 756
		$this->_changed = $data['changed'];
554 756
		$this->_unmapped = $data['unmapped'];
555 756
		$this->_saved = $data['saved'];
556 756
		$this->_loaded = $data['loaded'];
557 756
		$this->_deleted = $data['deleted'];
558
559 756
		foreach ($this->_changed as $name => $attribute)
560
		{
561 756
			$association = $this->meta()->association($name);
562 756
			if ($association AND $association instanceof Jam_Association_Collection)
563 756
			{
564 756
				$association->assign_internals($this, $attribute);
565 756
			}
566 756
		}
567 756
	}
568
}
569