Skip to content
Snippets Groups Projects
Commit 11322766 authored by Jared Hancock's avatar Jared Hancock
Browse files

orm: Implement lazy saving of related objects

When associating objects via the ::set() method or by adding them to an
InstrumentedList, avoid saving the object immediately. Instead, lazily
update the primary and foreign key of the associated objects.
parent 3d736b4c
Branches
Tags
No related merge requests found
...@@ -166,8 +166,7 @@ class VerySimpleModel { ...@@ -166,8 +166,7 @@ class VerySimpleModel {
static::_inspect(); static::_inspect();
$j = static::$meta['joins'][$field]; $j = static::$meta['joins'][$field];
// Support instrumented lists and such // Support instrumented lists and such
if (isset($this->ht[$j['local']]) if (isset($j['list']) && $j['list']) {
&& isset($j['list']) && $j['list']) {
$fkey = $j['fkey']; $fkey = $j['fkey'];
$v = $this->ht[$field] = new InstrumentedList( $v = $this->ht[$field] = new InstrumentedList(
// Send Model, Foriegn-Field, Local-Id // Send Model, Foriegn-Field, Local-Id
...@@ -252,11 +251,13 @@ class VerySimpleModel { ...@@ -252,11 +251,13 @@ class VerySimpleModel {
// Pass. Set local field to NULL in logic below // Pass. Set local field to NULL in logic below
} }
elseif ($value instanceof $j['fkey'][0]) { elseif ($value instanceof $j['fkey'][0]) {
if ($value->__new__)
$value->save();
// Capture the object under the object's field name // Capture the object under the object's field name
$this->ht[$field] = $value; $this->ht[$field] = $value;
$value = $value->get($j['fkey'][1]); if ($value->__new__)
// save() will be performed when saving this object
$value = null;
else
$value = $value->get($j['fkey'][1]);
// Fall through to the standard logic below // Fall through to the standard logic below
} }
else else
...@@ -373,17 +374,38 @@ class VerySimpleModel { ...@@ -373,17 +374,38 @@ class VerySimpleModel {
} }
function save($refetch=false) { function save($refetch=false) {
if ($this->__deleted__)
throw new OrmException('Trying to update a deleted object');
$pk = static::$meta['pk'];
$wasnew = $this->__new__;
// First, if any foreign properties of this object are connected to
// another *new* object, then save those objects first and set the
// local foreign key field values
foreach (static::$meta['joins'] as $prop => $j) {
if (isset($this->ht[$prop])
&& ($foreign = $this->ht[$prop])
&& $foreign instanceof VerySimpleModel
&& !in_array($j['local'], $pk)
&& null === $this->get($j['local'])
) {
if ($foreign->__new__ && !$foreign->save())
return false;
$this->set($j['local'], $foreign->get($j['fkey'][1]));
}
}
// If there's nothing in the model to be saved, then we're done
if (count($this->dirty) === 0) if (count($this->dirty) === 0)
return true; return true;
elseif ($this->__deleted__)
throw new OrmException('Trying to update a deleted object');
$ex = DbEngine::save($this); $ex = DbEngine::save($this);
try { try {
$ex->execute(); $ex->execute();
if ($ex->affected_rows() != 1) { if ($ex->affected_rows() != 1) {
// This doesn't really signify an error. It just means that // This doesn't really signify an error. It just means that
// the database believe that the row did not change. For // the database believes that the row did not change. For
// inserts though, it's a deal breaker // inserts though, it's a deal breaker
if ($this->__new__) if ($this->__new__)
return false; return false;
...@@ -397,10 +419,7 @@ class VerySimpleModel { ...@@ -397,10 +419,7 @@ class VerySimpleModel {
return false; return false;
} }
$pk = static::$meta['pk']; if ($wasnew) {
$wasnew = $this->__new__;
if ($this->__new__) {
if (count($pk) == 1) if (count($pk) == 1)
// XXX: Ensure AUTO_INCREMENT is set for the field // XXX: Ensure AUTO_INCREMENT is set for the field
$this->ht[$pk[0]] = $ex->insert_id(); $this->ht[$pk[0]] = $ex->insert_id();
...@@ -412,19 +431,38 @@ class VerySimpleModel { ...@@ -412,19 +431,38 @@ class VerySimpleModel {
Signal::send('model.updated', $this, $data); Signal::send('model.updated', $this, $data);
} }
# Refetch row from database # Refetch row from database
# XXX: Too much voodoo
if ($refetch) { if ($refetch) {
// Uncache so that the lookup will not be short-cirtuited to // Preserve non database information such as list relationships
// return this object // across the refetch
ModelInstanceManager::uncache($this); $this->ht =
$self = call_user_func_array(array(get_class($this), 'lookup'), static::objects()->filter($this->getPk())->values()->one()
$this->get('pk')); + $this->ht;
$this->ht = $self->ht; }
} if ($wasnew) {
if ($wasnew) // Attempt to update foreign, unsaved objects with the PK of
// this newly created object
foreach (static::$meta['joins'] as $prop => $j) {
if (isset($this->ht[$prop])
&& ($foreign = $this->ht[$prop])
&& in_array($j['local'], $pk)
) {
if ($foreign instanceof VerySimpleModel
&& null === $foreign->get($j['fkey'][1])
) {
$foreign->set($j['fkey'][1], $this->get($j['local']));
}
elseif ($foreign instanceof InstrumentedList) {
foreach ($foreign as $item) {
if (null === $item->get($j['fkey'][1]))
$item->set($j['fkey'][1], $this->get($j['local']));
}
}
}
}
$this->__onload(); $this->__onload();
}
$this->dirty = array(); $this->dirty = array();
return $this->get($pk[0]); return true;
} }
static function create($ht=false) { static function create($ht=false) {
...@@ -944,7 +982,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl ...@@ -944,7 +982,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
$query = clone $this; $query = clone $this;
if (!$query->ordering && isset($model::$meta['ordering'])) if (!$query->ordering && isset($model::$meta['ordering']))
$query->ordering = $model::$meta['ordering']; $query->ordering = $model::$meta['ordering'];
if (!$query->related && !$query->values && $model::$meta['select_related']) if (false !== $query->related && !$query->values && $model::$meta['select_related'])
$query->related = $model::$meta['select_related']; $query->related = $model::$meta['select_related'];
if (!$query->defer && $model::$meta['defer']) if (!$query->defer && $model::$meta['defer'])
$query->defer = $model::$meta['defer']; $query->defer = $model::$meta['defer'];
...@@ -1275,15 +1313,17 @@ class InstrumentedList extends ModelInstanceManager { ...@@ -1275,15 +1313,17 @@ class InstrumentedList extends ModelInstanceManager {
throw new Exception(__('Attempting to add invalid object to list')); throw new Exception(__('Attempting to add invalid object to list'));
$object->set($this->key, $this->id); $object->set($this->key, $this->id);
$object->save();
if ($at !== false) if ($at !== false)
$this->cache[$at] = $object; $this->cache[$at] = $object;
else else
$this->cache[] = $object; $this->cache[] = $object;
} }
function remove($object) { function remove($object, $delete=true) {
$object->delete(); if ($delete)
$object->delete();
else
$object->set($this->key, null);
} }
function reset() { function reset() {
...@@ -2344,11 +2384,11 @@ class Q implements Serializable { ...@@ -2344,11 +2384,11 @@ class Q implements Serializable {
return $this; return $this;
} }
static function not(array $constraints) { static function not($constraints) {
return new static($constraints, self::NEGATED); return new static($constraints, self::NEGATED);
} }
static function any(array $constraints) { static function any($constraints) {
return new static($constraints, self::ANY); return new static($constraints, self::ANY);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment