SitePoint Sponsor |
|
User Tag List
Results 1 to 16 of 16
-
May 26, 2009, 14:26 #1
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Updating columns that have been deselected
In the loosely based ActiveRecord system I am building it is possible to deselect columns. If this is done then those columns will not be selected or included as properties of the returned objects.
Take for example a simple scenario where a Project is found with a primary key of 8 while deselecting the summary field.
PHP Code:$o = Project::find('one',array('id'=>8,'limit'=>1,'deselect'=>'summary'));
HTML Code:Project Object ( [_data:private] => ActiveRecordDataEntity Object ( [_data:private] => Array ( [id] => Array ( [0] => 8 ) [name] => Array ( [0] => Site Name ) [weight] => Array ( [0] => 8 ) [web_site] => Array ( [0] => http://www.site_name.com/ ) [created] => Array ( [0] => 2009-04-16 11:59:11 ) ) ) )
PHP Code:$o->summary = 'Changed this summary';
HTML Code:Project Object ( [_data:private] => ActiveRecordDataEntity Object ( [_data:private] => Array ( [id] => Array ( [0] => 8 ) [name] => Array ( [0] => Site Name ) [weight] => Array ( [0] => 8 ) [web_site] => Array ( [0] => http://www.site_name.com/ ) [created] => Array ( [0] => 2009-04-16 11:59:11 ) [summary] => Array ( [0] => Changed this summary ) ) ) )
PHP Code:$o->summary = '';
$o->summary = 'Changed this summary';
HTML Code:Project Object ( [_data:private] => ActiveRecordDataEntity Object ( [_data:private] => Array ( [id] => Array ( [0] => 8 ) [name] => Array ( [0] => Site Name ) [weight] => Array ( [0] => 8 ) [web_site] => Array ( [0] => http://www.site_name.com/ ) [created] => Array ( [0] => 2009-04-16 11:59:11 ) [summary] => Array ( [0] => Changed this summary [1] => ) ) ) )
However, I rather have the system know that something has changed without this hack. I'm not really sure how to approach this though since the has changed methodology is based on the idea that there is more then one value in memory for a specific property.
Any ideas or recommendations appreciated.
-
May 26, 2009, 16:48 #2
- Join Date
- Aug 2007
- Posts
- 365
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I have a seperate array for updates, and use references to point the $Data array to the update Array()
PHP Code:class ..
{
private $Data = array();
private $Updates = array();
public function Set( $Name, $Value)
{
$this->Updates[ $Name ] = $Value;
$this->Data[ $Name ] = & $this->Updates[ $Name ];
}
public funciton Save()
{
if( ! (count( $this->Updates) > 0))
return ;
$sql = 'Update table Set'
foreach( $this->Updates as $iName, $iVal)
$sql .= $iName . '=' . $iVal ;
$sql = ' Where id=' . $this->PrimaryKey;
DBUpdate( $sql);
}
}
-
May 26, 2009, 17:17 #3
- Join Date
- Dec 2002
- Location
- Ann Arbor, MI (USA)
- Posts
- 648
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Just some ideas:
1. Keep the original version of the object in an identity map. Compare with modified object, update difference.
2. Query for the original object, compare as above.
3. Keep a flag for each property that is set when modified. When updating, only update fields that have a modified flag.
These are all getting kinda tricky to be using with ActiveRecord though... might want to move up to a DataMapper.
-
May 26, 2009, 17:45 #4
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Originally Posted by allspiritseve
Originally Posted by allspiritseve
Originally Posted by allspiritseve
Originally Posted by allspiritseve
PHP Code:$blog = new Blog(39);
$comments = $blog->getComments(array('include'=>'user'));
Originally Posted by taliesinnz
PHP Code:$blog1 = new Blog(10)
$blog1->entry = 'changed title';
$blog2 = new Blog();
$blog2->entry = 'new blog title';
$save = new ActiveRecordSave();
$save->add($blog1)->add($blog2);
$save->query($db);
The save object will then ask the ActiveRecord(IActiveRecordDataEntity) through its hasChanged() interface method if a property has changed. If that method returns true for any given model property then the item will be added to the update linked list and handled appropriately.
-
May 26, 2009, 20:12 #5
- Join Date
- Aug 2007
- Posts
- 365
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
-
May 26, 2009, 20:25 #6
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Properties aren't interpreted as being updated until the save() method is called. At which point another class called ActiveRecordSave is responsible for separating updates, inserts, building queries and and executing the final SQL statements(s). None of this happens inside the data container which indirectly resides inside any ActiveRecord instance as a property. The data containers have no knowledge of what a "update" is. They just act like a repository for data. There is no concept of a update until there needs to be a separation for query building purposes.
-
May 26, 2009, 20:41 #7
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
This method is part of the ActiveRecordUpdate class and determines whether or not a record will added to the update list and which update list if any to add it to or create a new update list. The method recordHasChanged() determines whether or not a record has changed based on whether or not the ActiveRecord method hasChanged($field) returns true or not for any property native to the related model of the ActiveRecord.
PHP Code:public function add(ActiveRecord $pRecord,$changed=false) {
if($changed===false && $this->recordHasChanged($pRecord)===false) return;
if(is_null($this->record)) {
$this->record = $pRecord;
} else if($this->isCompatible($pRecord)===true) {
$this->records[] = $pRecord;
} else {
if($this->hasSibling()) {
$this->getSibling()->add($pRecord,true);
} else {
$this->setSibling(new ActiveRecordUpdate($pRecord));
}
}
}
PHP Code:public function collectData() {
$config = ActiveRecordModelConfig::getModelConfig(get_class($this->record));
if($config->hasFields()===true) {
foreach($config->getFields() as $field) {
if($this->record->hasChanged($field)===true) {
$this->collectFieldData($field);
}
}
}
}
-
May 27, 2009, 14:20 #8
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Maybe the solution could be something like this?
PHP Code:class Blog {
protected $suppress;
protected $changed = array();
public function suppress($bool) {
$this->suppress = $bool;
}
......
}
$record = new Blog();
$record->suppress(true);
$record['id'] = $row['t0_id'];
$record['title'] = $row['t0_title'];
$record['entry'] = $row['t0_entry'];
$record['created'] = $row['t0_created'];
$record['subtitle'] = $row['t0_subtitle'];
$record->suppress(false);
$record['status'] = 0;
The suppress property would determine whether or not to log a change. So when the record is filled with data from a result set changes are suppressed. However, after the record has been filled all changes are unsuppressed. So any alteration to a property is logged.
In the above case status would be the only property present in the changed array.
This seems like the simplest approach.
This would also make it easy to change associated records since the user property would be logged as changed.
PHP Code:$record['user'] = new User(7);
Just thinking if this will break anything I currently have working. Otherwise it seems like a good solution.
-
May 27, 2009, 15:46 #9
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
I'm having some trouble deciding which class the responsibility of logging changes and handling suppression falls under.
The ActiveRecord class and ActiveRecordDataEntity class both implement the interface IActiveRecordDataEntity. This interface is shown below. If something is a instance of IActiveRecordDataEntity the it can be hypothetically overloaded. IActiveRecordDataEntity essentially provides a common interface for objects that act as a dynamic repository for data.
PHP Code:interface IActiveRecordDataEntity extends IteratorAggregate {
public function getProperty($pName);
public function setProperty($pName,$pValue);
public function hasProperty($pName);
public function hasChanged($pName);
public function addRecord($pPropertyName,IActiveRecordDataEntity $pRecord,$pArrayByDefault=false);
public function getRecord($pPropertyName,$pPrimaryKey,$pField);
public function removeProperty($pPropertyName);
public function getData();
public function cast();
public function suppress($bool); // to keep everything compatible I'm thinking
}
With that said, is it the ActiveRecords responsibility to log changes to properties or would that more or less be the responsibility of the ActiveRecordDataEntity?
If this is the responsibility of the ActiveRecord then the suppress method will be empty for all ActiveRecordDataEntity instances. However, if it is the responsibility of the ActiveRecordDataEntity then the implementation of hasChanged() would need to be altered for the ActiveRecordDataEntity. Yet, by making this the ActiveRecords responsibility the hasChanged() method can be overridden and changes can be logged before pushing them through to the decorated object. This seems like the correct approach.
In this case the suppression functionality would only be available to instances of ActiveRecord even though ActiveRecordDataEntity has a suppress method which would just be empty to make sure the interface stays the same between instances of IActiveRecordDataEntity.
-
May 27, 2009, 16:06 #10
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
Maybe the solution that would work well for fetching result sets is to create a ActiveRecordDataEntity – load it with data – then create a ActiveRecord with that data. Where the active record adds the functionality of suppression and property change logging.
PHP Code:$records = new ActiveRecordCollection();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$entity = new ActiveRecordDataEntity();
$entity->setProperty('title',$row['t0_id']);
$entity->setProperty('title',$row['t0_title']);
$entity->setProperty('created',$row['t0_created']);
$records->add(new Blog($entity));
}
-
May 27, 2009, 18:25 #11
- Join Date
- Jun 2003
- Location
- Melbourne, Australia
- Posts
- 440
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
PDOStatement::fetchObject() works well here. The object is created by PDO, but the properties are set before the constructor is run. Therefore, if the constructor initialises the updates array (to empty), setting of those properties will not be logged because that constructor will "undo" anything the Set() method did previously. The same would happen if you used the magic __set() method: it is run for each field retrieved by PDOStatement::fetchObject() before the constructor.
I've played with this a lot. It works a treat!Zealotry is contingent upon 100 posts and addiction 200?
-
May 27, 2009, 18:48 #12
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
The previous method I posted is working well. It only required a change in the implementation not the interface. Exactly, what i was looking for.
-
May 27, 2009, 19:04 #13
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
fetchObject wouldn't work because a ActiveRecord instance isn't represented by a row. Each row could represents 1,2,3,4… separate ActiveRecord instances that are either parent or children of one another.
-
May 27, 2009, 19:12 #14
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
example of what needs to happen recursively to create the appropriate object hierarchy.
result set:
Code:t0_id | t0_title | t1_id | t1_name ---------------------------------------------------- 1 | 'a' | 2 | 'tom67' 3 | 'd' | 4 | 'mark45' 9 | 's' | 2 | 'tom67'
ActiveRecord
-- t0_id
-- t0_title
-- t1_id
-- t1_name
t1 is a child of t0 therefore, this needs to represented at the domain level. The native PHP objects and methods don't provide this type "hydrating" for the result set.
ActiveRecord
-- t0_id
-- t0_title
-- ActiveRecord
----- t1_id
----- t1_name
-
May 28, 2009, 03:29 #15
- Join Date
- Jun 2003
- Location
- Melbourne, Australia
- Posts
- 440
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Oh, I see.
It is possible to this with PDOStatement::fetchObject() if you use the magic __set() method. You'd need some code to evaluate the property name (such as "t1_id") to determine whether it's a native property of this created object or a "child" property. You can create the child as necessary.
I have done this too, but it does blow out the __set() method. You may consider it's not worthwhile especially since you seem to have found a solution.Last edited by auricle; May 28, 2009 at 03:31. Reason: typographical error
Zealotry is contingent upon 100 posts and addiction 200?
-
May 28, 2009, 03:55 #16
- Join Date
- Jul 2006
- Location
- Augusta, Georgia, United States
- Posts
- 4,194
- Mentioned
- 17 Post(s)
- Tagged
- 5 Thread(s)
I appreciate the advice and input. However, the goal at this point is to change the code as little as possible with each new enhancement added. I have about 35 classes and 10 interfaces to manage. Therefore, I'm trying to avoid any major changes unless they are warranted. The last thing I need to happen is for something to break that already works well.
Bookmarks