Skip to content

Commit

Permalink
Improve joined model update/delete. (#71)
Browse files Browse the repository at this point in the history
* Improve exception catching in Persistence_SQL

* Use single-table update and single-table delete with where id=X

$m->save() invokes update with a fixed ID. Also it updates joined
tables separately. Because the record has been loaded successfully
before and will be reloaded after updates (in some cases) at a risk of
failed transaction, we don't have to invoke action here.

Action is more suited for all-set update and if you use joins with
SQLite you're on your own, as it does not support join with update.

Same goes for deletion.

* Remove commented out code.

* Expanded documentation.
  • Loading branch information
romaninsh authored and DarkSide666 committed Jul 25, 2016
1 parent 80c8441 commit bfc16ee
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 19 deletions.
48 changes: 48 additions & 0 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,52 @@ You can still access the deleted records::

Calling delete() on the model with 'deleted_only' property will delete it permanently.

Creating Unique Field
=====================

Database can has UNIQUE constraint, but this does work if you use DataSet. For instance, you
may be only able to create one 'Category' with name 'Book', but what if there is a
soft-deleted record with same name or record that belongs to another user?

With Agile Data you can create controller that will ensure that certain fields inside
your model are unique::

class Controller_UniqueFields {
use \atk4\core\InitializerTrait {
init as _init;
}
use \atk4\core\TrackableTrait;

protected $fields = null;

function init() {
$this->_init();

// by default make 'name' unique
if (!$this->fields) {
$this->fields = [$this->owner->title_field];
}

$this->owner->addHook('beforeSave', $this);
}

function beforeSave($m)
{
foreach ($this->fields as $field) {
if ($m->dirty[$field]) {
$mm = clone $m;
$mm->addCondition($mm->id_field != $this->id);
$mm->tryLoadBy($field, $m[$field]);

if ($mm->loaded()) {
throw new \atk4\core\Exception(['Duplicate record exists', 'field'=>$field, 'value'=>$m[$field]]);
}
}
}
}
}

As expected - when you add a new model the new values are checked against existing records. You can also slightly modify the logic
to make addCondition additive if you are verifying for the combination of matched fields.


10 changes: 1 addition & 9 deletions src/Join_SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,9 @@ public function beforeUpdateQuery($model, $query)
return;
}

//if ($this->dsql->args['set']) {
$update = $this->dsql();
$update->where($this->foreign_field, $this->id);
$update->update();
//}
}

public function doDelete($model, $id)
Expand All @@ -211,13 +209,7 @@ public function doDelete($model, $id)
$delete
->where($this->foreign_field, $this->id);

//if ($this->delete_behaivour == 'cascade') {
$delete->delete()->execute();
//} elseif ($this->delete_behaivour == 'setnull') {
//$delete
//->set($this->foreign_field, null)
//->update();
//}
$delete->delete()->execute();
}

public function set($field, $value)
Expand Down
21 changes: 11 additions & 10 deletions src/Persistence_SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ public function load(Model $m, $id)
// execute action
try {
$data = $load->getRow();
} catch (\Exception $e) {
} catch (\PDOException $e) {
throw new Exception([
'Unable to load due to query error',
'query' => $load->getDebugQuery(false),
Expand Down Expand Up @@ -505,7 +505,7 @@ public function loadAny(Model $m)

if (!$data) {
throw new Exception([
'Unable to load record',
'Unable to load any record',
'model' => $m,
'query' => $load->getDebugQuery(false),
]);
Expand Down Expand Up @@ -581,7 +581,7 @@ public function insert(Model $m, $data)
try {
$m->hook('beforeInsertQuery', [$insert]);
$st = $insert->execute();
} catch (\Exception $e) {
} catch (\PDOException $e) {
throw new Exception([
'Unable to execute insert query',
'query' => $insert->getDebugQuery(false),
Expand Down Expand Up @@ -623,7 +623,7 @@ public function prepareIterator(Model $m)
$export = $this->action($m, 'select');

return $export->execute();
} catch (\Exception $e) {
} catch (\PDOException $e) {
throw new Exception([
'Unable to execute iteration query',
'query' => $export->getDebugQuery(false),
Expand All @@ -642,7 +642,8 @@ public function prepareIterator(Model $m)
*/
public function update(Model $m, $id, $data)
{
$update = $this->action($m, 'update');
$update = $this->initQuery($m);
$update->mode('update');

// only apply fields that has been modified
$cnt = 0;
Expand All @@ -662,7 +663,7 @@ public function update(Model $m, $id, $data)
try {
$m->hook('beforeUpdateQuery', [$update]);
$st = $update->execute();
} catch (\Exception $e) {
} catch (\PDOException $e) {
throw new Exception([
'Unable to update due to query error',
'query' => $update->getDebugQuery(false),
Expand All @@ -687,16 +688,16 @@ public function update(Model $m, $id, $data)
*/
public function delete(Model $m, $id)
{
$delete = $this->action($m, 'delete');
$delete->reset('where'); // because it could have join there..
$delete = $this->initQuery($m);
$delete->mode('delete');
$delete->where($m->id_field, $id);
$m->hook('beforeDeleteQuery', [$delete]);

try {
$delete->execute();
} catch (\Exception $e) {
} catch (\PDOException $e) {
throw new Exception([
'Unable to load due to query error',
'Unable to delete due to query error',
'query' => $delete->getDebugQuery(false),
'model' => $m,
'conditions' => $m->conditions,
Expand Down

0 comments on commit bfc16ee

Please sign in to comment.