Skip to content

Commit

Permalink
Implement hasOne() relationship ID binding. (#61)
Browse files Browse the repository at this point in the history
* Implement hasOne() relationship ID binding.

* Applied fixes from StyleCI

* allow hasOne()->addField('foo'); (instead of saying foo twice)

* Applied fixes from StyleCI
  • Loading branch information
romaninsh authored and DarkSide666 committed Jul 21, 2016
1 parent 94e938b commit 778cbc7
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 3 deletions.
49 changes: 49 additions & 0 deletions docs/relations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,52 @@ Loading model like that can produce a pretty sophisticated query
) `child_age`,`pp`.`id` `_i`
from `item` `pp`left join `item2` as `pp_i` on `pp_i`.`item_id` = `pp`.`id`
Relations with New Records
==========================

Agile Data takes extra care to help you link your new records with new related entities.
Consider the following two models::

class Model_User extends \atk4\data\Model {
public $table = 'user';
function init() {
parent::init();
$this->addField('name');

$this->hasOne('contact_id', new Model_Contact());
}
}

class Model_Contact extends \atk4\data\Model {
public $table = 'contact';
function init() {
parent::init();

$this->addField('address');
}
}

This is a classic one to one relation, but let's look what happens when you are working with
a new model::

$m = new Model_User($db);

$m['name'] = 'John';
$m->save();

In this scenario, a new record will be added into 'user' with 'contact_id' equal to null. The
next example will traverse into the contact to set it up::

$m = new Model_User($db);

$m['name'] = 'John';
$m->ref('address_id')->save(['address'=>'street']);
$m->save();

When entity which you have referenced through ref() is saved, it will automatically populate
$m['contact_id'] field and the final $m->save() will also store the reference.

ID setting is implemented through a basic hook. Related model will have afterSave
hook, which will update address_id field of the $m.


16 changes: 14 additions & 2 deletions src/Field_One.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,21 @@ public function ref($defaults = [])
$m = $this->getModel($defaults);
if ($this->owner->loaded()) {
if ($this->their_field) {
return $m->loadBy($this->their_field, $this->owner[$this->our_field]);
return $m->tryLoadBy($this->their_field, $this->owner[$this->our_field])
->addHook('afterSave', function ($m) {
$this->owner[$this->our_field] = $m[$this->their_field];
})
->addHook('afterDelete', function ($m) {
$this->owner[$this->our_field] = null;
});
} else {
return $m->load($this->owner[$this->our_field]);
return $m->tryLoad($this->owner[$this->our_field])
->addHook('afterSave', function ($m) {
$this->owner[$this->our_field] = $m->id;
})
->addHook('afterDelete', function ($m) {
$this->owner[$this->our_field] = null;
});
}
} else {
$m = clone $m; // we will be adding conditions!
Expand Down
6 changes: 5 additions & 1 deletion src/Field_SQL_One.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ class Field_SQL_One extends Field_One
*
* Returns Expression in case you want to do something else with it.
*/
public function addField($field, $their_field)
public function addField($field, $their_field = null)
{
if ($their_field === null) {
$their_field = $field;
}

return $this->owner->addExpression($field, function ($m) use ($their_field) {
return $m->refLink($this->link)->action('field', [$their_field]);
});
Expand Down
47 changes: 47 additions & 0 deletions tests/RelationSQLTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,51 @@ public function testAggregateHasMany()
$this->assertEquals($n * $vat, $i['total_vat']);
$this->assertEquals($n * ($vat + 1), $i['total_gross']);
}

public function testRelationHook()
{
$a = [
'user' => [
['name' => 'John', 'contact_id' => 2],
['name' => 'Peter', 'contact_id' => null],
['name' => 'Joe', 'contact_id' => 3],
], 'contact' => [
['address' => 'Sue contact'],
['address' => 'John contact'],
['address' => 'Joe contact'],
], ];

$this->setDB($a);

$db = new Persistence_SQL($this->db->connection);
$u = (new Model($db, 'user'))->addFields(['name']);
$c = (new Model($db, 'contact'))->addFields(['address']);

$u->hasOne('contact_id', $c)
->addField('address');


$u->load(1);
$this->assertEquals('John contact', $u['address']);
$this->assertEquals('John contact', $u->ref('contact_id')['address']);

$u->load(2);
$this->assertEquals(null, $u['address']);
$this->assertEquals(null, $u['contact_id']);
$this->assertEquals(null, $u->ref('contact_id')['address']);

$u->load(3);
$this->assertEquals('Joe contact', $u['address']);
$this->assertEquals('Joe contact', $u->ref('contact_id')['address']);

$u->load(2);
$u->ref('contact_id')->save(['address' => 'Peters new contact']);

$this->assertNotEquals(null, $u['contact_id']);
$this->assertEquals('Peters new contact', $u->ref('contact_id')['address']);

$u->save()->reload();
$this->assertEquals('Peters new contact', $u->ref('contact_id')['address']);
$this->assertEquals('Peters new contact', $u['address']);
}
}

0 comments on commit 778cbc7

Please sign in to comment.