Skip to content

Commit

Permalink
Merge pull request #427 from jpfuentes2/getloaded-flag-dirty-only-if-…
Browse files Browse the repository at this point in the history
…attribute-changes

Tracking attribute changes
  • Loading branch information
koenpunt committed Jul 17, 2014
2 parents 4f2070d + 232d2de commit de4166f
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 6 deletions.
85 changes: 80 additions & 5 deletions lib/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ class Model
*/
private $attributes = array();

/**
* Contains changes made to model attributes as
* $attribute_name => array($original_value, $current_value)
*
* @var array
* @access private
*/
private $changed_attributes = array();

/**
* Contains changes made to model attributes before it was saved
*
* @var array
* @access private
*/
private $previously_changed = array();

/**
* Flag whether or not this model's attributes have been modified since it will either be null or an array of column_names that have been modified
*
Expand Down Expand Up @@ -468,8 +485,16 @@ public function assign_attribute($name, $value)
if ($value instanceof DateTime)
$value->attribute_of($this,$name);

$this->attributes[$name] = $value;
$this->flag_dirty($name);
// only update the attribute if it isn't set or has changed
if (!isset($this->attributes[$name]) || ($this->attributes[$name] !== $value)) {
// track changes to the attribute
if (array_key_exists($name, $this->attributes) && !isset($this->changed_attributes[$name]))
$this->changed_attributes[$name] = $this->attributes[$name];

// set the attribute and flag as dirty
$this->attributes[$name] = $value;
$this->flag_dirty($name);
}
return $value;
}

Expand All @@ -489,8 +514,10 @@ public function &read_attribute($name)
$name = static::$alias_attribute[$name];

// check for attribute
if (array_key_exists($name,$this->attributes))
return $this->attributes[$name];
if (array_key_exists($name,$this->attributes)) {
$value = $this->attributes[$name];
return $value;
}

// check relationships if no attribute
if (array_key_exists($name,$this->__relationships))
Expand Down Expand Up @@ -580,6 +607,52 @@ public function attributes()
return $this->attributes;
}

/**
* Returns a copy of the model's changed attributes hash with the
* attribute name and the original value.
*
* @return array A copy of the model's changed attribute data
*/
public function changed_attributes()
{
return $this->changed_attributes;
}

/**
* Returns a copy of the model's changed attributes as a hash
* in the form $attribute => array($original_value, $current_value)
*
* @return array A copy of the model's attribute changes
*/
public function changes()
{
$changes = array();
$attributes = array_intersect_key($this->attributes, $this->changed_attributes);
foreach($attributes as $name => $value) {
$changes[$name] = array($this->changed_attributes[$name],$value);
}
return $changes;
}

/**
* Returns a copy of the model's changed attributes before it was saved
*
* @return array A copy of the model's changed attribute before a save
*/
public function previous_changes()
{
return $this->previously_changed;
}

/**
* Returns the value of an attribute before it was changed
*
* @return string The original value of an attribute
*/
public function attribute_was($name) {
return isset($this->changed_attributes[$name]) ? $this->changed_attributes[$name] : null;
}

/**
* Retrieve the primary key name.
*
Expand Down Expand Up @@ -1304,9 +1377,11 @@ public function __clone()
*
* @see dirty_attributes
*/
public function reset_dirty()
public function reset_dirty($model_was_saved=false)
{
$this->__dirty = null;
$this->previously_changed = $model_was_saved ? $this->changes() : array();
$this->changed_attributes = array();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public function __construct($class_name)

$this->callback = new CallBack($class_name);
$this->callback->register('before_save', function(Model $model) { $model->set_timestamps(); }, array('prepend' => true));
$this->callback->register('after_save', function(Model $model) { $model->reset_dirty(); }, array('prepend' => true));
$this->callback->register('after_save', function(Model $model) { $model->reset_dirty(true); }, array('prepend' => true));
}

public function reestablish_connection($close=true)
Expand Down
96 changes: 96 additions & 0 deletions test/ActiveRecordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,102 @@ public function test_gh245_dirty_attribute_should_not_raise_php_notice_if_not_di
$this->assert_true($event->attribute_is_dirty('title'));
}

public function test_attribute_is_not_flagged_dirty_if_assigning_same_value() {
$event = Event::find(1);
$event->type = "Music";
$this->assert_false($event->attribute_is_dirty('type'));
}

public function test_changed_attributes() {
$event = Event::find(1);

$event->type = "Groovy Music";
$changed_attributes = $event->changed_attributes();
$this->assert_true(is_array($changed_attributes));
$this->assert_equals(1, count($changed_attributes));
$this->assert_true(isset($changed_attributes['type']));
$this->assert_equals("Music", $changed_attributes['type']);

$event->type = "Funky Music";
$changed_attributes = $event->changed_attributes();
$this->assert_true(is_array($changed_attributes));
$this->assert_equals(1, count($changed_attributes));
$this->assert_true(isset($changed_attributes['type']));
$this->assert_equals("Music", $changed_attributes['type']);
}

public function test_changes() {
$event = Event::find(1);

$event->type = "Groovy Music";
$changes = $event->changes();
$this->assert_true(is_array($changes));
$this->assert_equals(1, count($changes));
$this->assert_true(isset($changes['type']));
$this->assert_true(is_array($changes['type']));
$this->assert_equals("Music", $changes['type'][0]);
$this->assert_equals("Groovy Music", $changes['type'][1]);

$event->type = "Funky Music";
$changes = $event->changes();
$this->assert_true(is_array($changes));
$this->assert_equals(1, count($changes));
$this->assert_true(isset($changes['type']));
$this->assert_true(is_array($changes['type']));
$this->assert_equals("Music", $changes['type'][0]);
$this->assert_equals("Funky Music", $changes['type'][1]);
}

public function test_attribute_was() {
$event = Event::find(1);
$event->type = "Funky Music";
$this->assert_equals("Music", $event->attribute_was("type"));
$event->type = "Groovy Music";
$this->assert_equals("Music", $event->attribute_was("type"));
}

public function test_previous_changes() {
$event = Event::find(1);
$event->type = "Groovy Music";
$previous_changes = $event->previous_changes();
$this->assert_true(empty($previous_changes));
$event->save();
$previous_changes = $event->previous_changes();
$this->assert_true(is_array($previous_changes));
$this->assert_equals(1, count($previous_changes));
$this->assert_true(isset($previous_changes['type']));
$this->assert_true(is_array($previous_changes['type']));
$this->assert_equals("Music", $previous_changes['type'][0]);
$this->assert_equals("Groovy Music", $previous_changes['type'][1]);
}

public function test_save_resets_changed_attributes() {
$event = Event::find(1);
$event->type = "Groovy Music";
$event->save();
$changed_attributes = $event->changed_attributes();
$this->assert_true(empty($changed_attributes));
}

public function test_changing_datetime_attribute_tracks_change() {
$author = new Author();
$author->created_at = $original = new \DateTime("yesterday");
$author->created_at = $now = new \DateTime();
$changes = $author->changes();
$this->assert_true(isset($changes['created_at']));
$this->assert_datetime_equals($original, $changes['created_at'][0]);
$this->assert_datetime_equals($now, $changes['created_at'][1]);
}

public function test_changing_empty_attribute_value_tracks_change() {
$event = new Event();
$event->description = "The most fun";
$changes = $event->changes();
$this->assert_true(array_key_exists("description", $changes));
$this->assert_equals("", $changes['description'][0]);
$this->assert_equals("The most fun", $changes['description'][1]);
}

public function test_assigning_php_datetime_gets_converted_to_ar_datetime()
{
$author = new Author();
Expand Down

0 comments on commit de4166f

Please sign in to comment.