Completed
Push — master ( 9ff43e...cfd0cd )
by Haralan
07:08
created

Kohana_Jam_Model   D

Complexity

Total Complexity 74

Size/Duplication

Total Lines 553
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 87.4%

Importance

Changes 0
Metric Value
wmc 74
lcom 1
cbo 13
dl 0
loc 553
ccs 215
cts 246
cp 0.874
rs 4.5631
c 0
b 0
f 0

23 Methods

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

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 384
	public function __construct($meta_name = NULL)
45
	{
46 384
		parent::__construct($meta_name);
47
48 384
		$this->meta()->events()->trigger('model.before_construct', $this);
49
50
		// Copy over the defaults into the original data.
51 384
		$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 384
		$this->meta()->events()->trigger('model.after_construct', $this);
54 384
	}
55
56
	/**
57
	 * Gets the value for a field.
58
	 *
59
	 * @param   string       $name The field's name
60
	 * @return  array|mixed
61
	 */
62 170
	public function get($name)
63
	{
64 170
		if ($association = $this->meta()->association($name))
65 170
		{
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 169
		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   string        $value
93
	 * @return  $this
94
	 */
95 63
	public function set($values, $value = NULL)
96
	{
97
		// Accept set('name', 'value');
98 63
		if ( ! is_array($values))
99 63
		{
100 51
			$values = array($values => $value);
101 51
		}
102
103 63
		foreach ($values as $key => & $value)
104
		{
105 63
			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 63
			{
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 63
		}
116
117 63
		parent::set($values);
118
119 63
		if ($this->_saved AND $this->changed())
120 63
		{
121 18
			$this->_saved = FALSE;
122 18
		}
123
124 63
		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 157
	public function load_fields($values)
137
	{
138
		// Clear the object
139 157
		$this->clear();
140
141 157
		$this->meta()->events()->trigger('model.before_load', $this);
142
143 157
		$this->_loaded = TRUE;
144
145 157
		$columns = array();
146
147 157
		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 157
			$columns[$field->column] = $field;
150 157
		}
151
152 157
		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 154
			if ($field = $this->meta()->field($key))
155 154
			{
156 154
				$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 154
			}
158 23
			elseif (isset($columns[$key]))
159
			{
160
				$field = $columns[$key];
161 1
				$this->_original[$field->name] = $field->set($this, $value, FALSE);
162
			}
163 23
			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 15
				$this->_unmapped[$key] = $value;
178
			}
179 157
		}
180
181
		// Model is now saved and loaded
182 157
		$this->_saved = TRUE;
183
184 157
		$this->meta()->events()->trigger('model.after_load', $this);
185
186 157
		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 11
	public function check($force = FALSE)
197
	{
198 11
		$this->_move_retrieved_to_changed();
199
200 11
		return parent::check( ! $this->loaded() OR $force);
201
	}
202
203 644
	protected function _move_retrieved_to_changed()
204
	{
205 644
		foreach ($this->_retrieved as $column => $value)
206
		{
207 11
			if ($value instanceof Jam_Array_Association	AND $value->changed())
208 11
			{
209 5
				$this->_changed[$column] = $value;
210 5
			}
211 644
		}
212 644
	}
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
	 */
240 54
	public function save($validate = NULL)
241
	{
242 10
		if ($this->_is_saving)
243 10
			throw new Kohana_Exception("Cannot save a model that is already in the process of saving");
244
245
246 10
		$key = $this->_original[$this->meta()->primary_key()];
247
248
		// Run validation
249 10
		if ($validate !== FALSE)
250 10
		{
251 10
			$this->check_insist();
252 10
		}
253
254 10
		$this->_is_saving = TRUE;
255
256
		// These will be processed later
257 10
		$values = $defaults = array();
258
259 10
		if ($this->meta()->events()->trigger('model.before_save', $this, array($this->_changed)) === FALSE)
260 10
		{
261
			return $this;
262
		}
263
264
		// Trigger callbacks and ensure we should proceed
265 10
		$event_type = $key ? 'update' : 'create';
266
267 10
		if ($this->meta()->events()->trigger('model.before_'.$event_type, $this, array($this->_changed)) === FALSE)
268 10
		{
269
			return $this;
270
		}
271
272 10
		$this->_move_retrieved_to_changed();
273
274
		// Iterate through all fields in original in case any unchanged fields
275
		// have convert() behavior like timestamp updating...
276
		//
277 10
		foreach (array_merge($this->_original, $this->_changed) as $column => $value)
278
		{
279 10
			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...
280 10
			{
281
				// Only save in_db values
282 10
				if ($field->in_db)
283 10
				{
284
					// See if field wants to alter the value on save()
285 10
					$value = $field->convert($this, $value, $key);
286
287
					// Only set the value to be saved if it's changed from the original
288 10
					if ($value !== $this->_original[$column])
289 10
					{
290 7
						$values[$field->column] = $value;
291 54
					}
292
					// Or if we're INSERTing and we need to set the defaults for the first time
293 10
					elseif ( ! $key AND ( ! $this->changed($field->name) OR $field->default === $value) AND ! $field->primary)
294
					{
295 8
						$defaults[$field->column] = $field->default;
296 8
					}
297 10
				}
298 10
			}
299 10
		}
300
301
		// If we have a key, we're updating
302
		if ($key)
303 10
		{
304
			// Do we even have to update anything in the row?
305
			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...
306 4
			{
307 3
				Jam::update($this)
308 3
					->where_key($key)
309 3
					->set($values)
310 3
					->execute();
311 3
			}
312 4
		}
313
		else
314
		{
315 8
			$insert_values = array_merge($defaults, $values);
316 8
			list($id) = Jam::insert($this)
317 8
				->columns(array_keys($insert_values))
318 8
				->values(array_values($insert_values))
319 8
				->execute();
320
321
			// Gotta make sure to set this
322 8
			$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...
323
		}
324
325
		// Re-set any saved values; they may have changed
326 10
		$this->set($values);
327
328 10
		$this->_loaded = $this->_saved = TRUE;
329
330 10
		$this->meta()->events()->trigger('model.after_save', $this, array($this->_changed, $event_type));
331
332 10
		$this->meta()->events()->trigger('model.after_'.$event_type, $this, array($this->_changed));
333
334
		// Set the changed data back as original
335 10
		$this->_original = array_merge($this->_original, $this->_changed);
336
337 10
		$this->_changed = array();
338
339 10
		foreach ($this->_retrieved as $name => $retrieved)
340
		{
341 7
			if (($association = $this->meta()->association($name)))
342 7
			{
343 2
				if ($association instanceof Jam_Association_Collection)
344 2
				{
345 1
					$retrieved->clear_changed();
346 1
				}
347 2
			}
348 7
			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...
349
			{
350 7
				unset($this->_retrieved[$name]);
351 7
			}
352 10
		}
353
354 10
		$this->_is_saving = FALSE;
355
356 10
		return $this;
357
	}
358
359
	/**
360
	 * Deletes a single record.
361
	 *
362
	 * @return  boolean
363
	 **/
364 10
	public function delete()
365
	{
366 10
		$result = FALSE;
367
368
		// Are we loaded? Then we're just deleting this record
369 10
		if ($this->_loaded)
370 10
		{
371 10
			$key = $this->_original[$this->meta()->primary_key()];
372
373 10
			if (($result = $this->meta()->events()->trigger('model.before_delete', $this)) !== FALSE)
374 10
			{
375 10
				$result = Jam::delete($this)->where_key($key)->execute();
376 10
			}
377 10
		}
378
379
		// Trigger the after-delete
380 10
		$this->meta()->events()->trigger('model.after_delete', $this, array($result));
381
382
		// Clear the object so it appears deleted anyway
383 10
		$this->clear();
384
385
		// Set the flag indicatig the model has been successfully deleted
386 10
		$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...
387
388 10
		return $this->_deleted;
389
	}
390
391
	/**
392
	 * Removes any changes made to a model.
393
	 *
394
	 * This method only works on loaded models.
395
	 *
396
	 * @return  $this
397
	 */
398
	public function revert()
399
	{
400
		if ($this->_loaded)
401
		{
402
			$this->_loaded =
403
			$this->_saved  = TRUE;
404
405
			parent::revert();
406
		}
407
408
		return $this;
409
	}
410
411
	/**
412
	 * Sets a model to its original state, as if freshly instantiated
413
	 *
414
	 * @return  $this
415
	 */
416 157
	public function clear()
417
	{
418 157
		$this->_loaded =
419 157
		$this->_saved  = FALSE;
420
421 157
		parent::clear();
422
423 157
		return $this;
424
	}
425
426 1
	public function get_insist($attribute_name)
427
	{
428 1
		$attribute = $this->__get($attribute_name);
429
430 1
		if ($attribute === NULL)
431 1
			throw new Jam_Exception_Notfound('The association :name was empty on :model_name', NULL, array(
432 1
				':name' => $attribute_name,
433 1
				':model_name' => (string) $this,
434 1
			));
435
436 1
		return $attribute;
437
	}
438
439 1
	public function build($association_name, array $attributes = array())
440
	{
441 1
		$association = $this->meta()->association($association_name);
442
443 1
		if ( ! $association)
444 1
			throw new Kohana_Exception('There is no association named :association_name on model :model', array(':association_name' => $association_name, ':model' => $this->meta()->model()));
445
446 1
		if ($association instanceof Jam_Association_Collection)
447 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()));
448
449 1
		$item = $association->build($this, $attributes);
450
451 1
		$this->set($association_name, $item);
452
453 1
		return $item;
454
	}
455
456
	/**
457
	 * Returns whether or not the model is loaded
458
	 *
459
	 * @return  boolean
460
	 */
461 73
	public function loaded()
462
	{
463 73
		return $this->_loaded;
464
	}
465
466
	public function loaded_insist()
467
	{
468
		if ( ! $this->loaded())
469
			throw new Jam_Exception_NotLoaded("Model not loaded", $this);
470
471
		return $this;
472
	}
473
474
	/**
475
	 * Whether or not the model is saved
476
	 *
477
	 * @return  boolean
478
	 */
479 10
	public function saved()
480
	{
481 10
		return $this->_saved;
482
	}
483
484
	/**
485
	 * Whether or not the model is in the process of being saved
486
	 *
487
	 * @return  boolean
488
	 */
489 4
	public function is_saving()
490
	{
491 4
		return $this->_is_saving;
492
	}
493
494
	/**
495
	 * Whether or not the model is deleted
496
	 *
497
	 * @return  boolean
498
	 */
499
	public function deleted()
500
	{
501
		return $this->_deleted;
502
	}
503
504
	/**
505
	 * Build a new model object based on the current one, but without an ID, so it can be saved as a new object
506
	 * @return Jam_Model
507
	 */
508 1
	public function duplicate()
509
	{
510 1
		$fields = $this->as_array();
511
512 1
		unset($fields[$this->meta()->primary_key()]);
513
514 1
		return Jam::build($this->meta()->model(), $fields);
515
	}
516
517
	/**
518
	 * Returns a string representation of the model in the
519
	 * form of `Model_Name (id)` or `Model_Name (NULL)` if
520
	 * the model is not loaded.
521
	 *
522
	 * This is designed to be useful for debugging.
523
	 *
524
	 * @return  string
525
	 */
526 2
	public function __toString()
527
	{
528 2
		return (string) get_class($this).'('.($this->loaded() ? $this->id() : 'NULL').')';
529
	}
530
531 644
	public function serialize()
532
	{
533 644
		$this->_move_retrieved_to_changed();
534
535 644
		return serialize(array(
536 644
			'original' => $this->_original,
537 644
			'changed' => $this->_changed,
538 644
			'unmapped' => $this->_unmapped,
539 644
			'saved' => $this->_saved,
540 644
			'loaded' => $this->_loaded,
541 644
			'deleted' => $this->_deleted,
542 644
		));
543
	}
544
545 644
	public function unserialize($data)
546
	{
547 644
		$data = unserialize($data);
548
549 644
		$this->_meta = Jam::meta($this);
550 644
		$this->_original = Arr::merge($this->meta()->defaults(), $data['original']);
551 644
		$this->_changed = $data['changed'];
552 644
		$this->_unmapped = $data['unmapped'];
553 644
		$this->_saved = $data['saved'];
554 644
		$this->_loaded = $data['loaded'];
555 644
		$this->_deleted = $data['deleted'];
556
557 644
		foreach ($this->_changed as $name => $attribute)
558
		{
559 644
			$association = $this->meta()->association($name);
560 644
			if ($association AND $association instanceof Jam_Association_Collection)
561 644
			{
562 644
				$association->assign_internals($this, $attribute);
563 644
			}
564 644
		}
565 644
	}
566
}
567