Skip to content

Commit

Permalink
Fix ContainsOne model/entity hook (#999)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed May 30, 2022
1 parent bde5290 commit 9e4c71f
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 29 deletions.
15 changes: 7 additions & 8 deletions src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,6 @@ class Model implements \IteratorAggregate
* updating the model. This property is intended for UI and other code
* detecting read-only models and acting accordingly.
*
* SECURITY WARNING: If you are looking for a RELIABLE way to restrict access
* to model data, please check Secure Enclave extension.
*
* @var bool
*/
public $read_only = false;
Expand Down Expand Up @@ -259,13 +256,13 @@ class Model implements \IteratorAggregate
public $reload_after_save;

/**
* If this model is "contained into" another model by using containsOne
* or containsMany reference, then this property will contain reference
* to top most parent model.
* If this model is "contained into" another entity by using ContainsOne
* or ContainsMany reference, then this property will contain reference
* to owning entity.
*
* @var Model|null
*/
public $contained_in_root_model;
public $containedInEntity;

/** @var Reference Only for Reference class */
public $ownerReference;
Expand Down Expand Up @@ -318,7 +315,7 @@ public function assertIsModel(self $expectedModelInstance = null): void
if ($expectedModelInstance !== null && $expectedModelInstance !== $this) {
$expectedModelInstance->assertIsModel();

throw new Exception('Unexpected entity model instance');
throw new Exception('Model instance does not match');
}
}

Expand Down Expand Up @@ -388,6 +385,8 @@ protected function getModelOnlyProperties(): array

'ownerReference', // should be removed once references/joins are non-entity
'userActions', // should be removed once user actions are non-entity

'containedInEntity',
] as $name) {
unset($modelOnlyProperties[$name]);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Reference.php
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@ protected function getDefaultPersistence(Model $theirModel)
{
$ourModel = $this->getOurModel(null);

// this will be useful for containsOne/Many implementation in case when you have
// this is useful for ContainsOne/Many implementation in case when you have
// SQL_Model->containsOne()->hasOne() structure to get back to SQL persistence
// from Array persistence used in containsOne model
if ($ourModel->contained_in_root_model && $ourModel->contained_in_root_model->persistence) {
return $ourModel->contained_in_root_model->persistence;
// from Array persistence used in ContainsOne model
if ($ourModel->containedInEntity && $ourModel->containedInEntity->persistence) {
return $ourModel->containedInEntity->persistence;
}

return $ourModel->persistence ?: false;
Expand Down
11 changes: 6 additions & 5 deletions src/Reference/ContainsMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,20 @@ public function ref(Model $ourModel, array $defaults = []): Model

// get model
$theirModel = $this->createTheirModel(array_merge($defaults, [
'contained_in_root_model' => $ourModel->contained_in_root_model ?: $ourModel,
'containedInEntity' => $ourModel->isEntity() ? $ourModel : null,
'table' => $this->table_alias,
]));

// set some hooks for ref_model
foreach ([Model::HOOK_AFTER_SAVE, Model::HOOK_AFTER_DELETE] as $spot) {
$this->onHookToTheirModel($theirModel, $spot, function (Model $theirModel) use ($ourModel) {
$this->onHookToTheirModel($theirModel, $spot, function (Model $theirModel) {
$ourModel = $this->getOurModel($theirModel->containedInEntity);
$ourModel->assertIsEntity();

/** @var Persistence\Array_ */
$persistence = $theirModel->persistence;
$rows = $persistence->getRawDataByTable($theirModel, $this->table_alias);
$this->getOurModel($ourModel)->save([
$this->getOurFieldName() => $rows ?: null,
]);
$ourModel->save([$this->getOurFieldName() => $rows ?: null]);
});
}

Expand Down
9 changes: 6 additions & 3 deletions src/Reference/ContainsOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,20 @@ public function ref(Model $ourModel, array $defaults = []): Model
$ourModel = $this->getOurModel($ourModel);

$theirModel = $this->createTheirModel(array_merge($defaults, [
'contained_in_root_model' => $ourModel->contained_in_root_model ?: $ourModel,
'containedInEntity' => $ourModel->isEntity() ? $ourModel : null,
'table' => $this->table_alias,
]));

foreach ([Model::HOOK_AFTER_SAVE, Model::HOOK_AFTER_DELETE] as $spot) {
$this->onHookToTheirModel($theirModel, $spot, function (Model $theirModel) use ($ourModel) {
$this->onHookToTheirModel($theirModel, $spot, function (Model $theirModel) {
$ourModel = $this->getOurModel($theirModel->containedInEntity);
$ourModel->assertIsEntity();

/** @var Persistence\Array_ */
$persistence = $theirModel->persistence;
$row = $persistence->getRawDataByTable($theirModel, $this->table_alias);
$row = $row ? array_shift($row) : null; // get first and only one record from array persistence
$this->getOurModel($ourModel)->save([$this->getOurFieldName() => $row]);
$ourModel->save([$this->getOurFieldName() => $row]);
});
}

Expand Down
2 changes: 2 additions & 0 deletions tests/ContainsManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ public function testContainsMany(): void
$this->assertSameExportUnordered($rows, $i->lines->export());
$i->reload();
$this->assertSameExportUnordered($rows, $i->lines->export());
$i = $i->getModel()->load($i->getId());
$this->assertSameExportUnordered($rows, $i->lines->export());

// now let's delete line with id=2 and add one more line
$i->lines
Expand Down
19 changes: 10 additions & 9 deletions tests/ContainsOneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Atk4\Data\Schema\TestCase;
use Atk4\Data\Tests\ContainsOne\Address;
use Atk4\Data\Tests\ContainsOne\Country;
use Atk4\Data\Tests\ContainsOne\DoorCode;
use Atk4\Data\Tests\ContainsOne\Invoice;

/**
Expand Down Expand Up @@ -67,8 +66,7 @@ protected function setUp(): void
public function testModelCaption(): void
{
$i = new Invoice($this->db);
/** @var Address */
$a = $i->ref($i->fieldName()->addr);
$a = $i->addr;

// test caption of containsOne reference
$this->assertSame('Secret Code', $a->getField($a->fieldName()->door_code)->getCaption());
Expand All @@ -85,8 +83,8 @@ public function testContainsOne(): void

// check do we have address set
$this->assertNull($i->addr);
/** @var Address */
$a = $i->ref($i->fieldName()->addr);
$a = $i->getModel()->addr->createEntity();
$a->containedInEntity = $i;

// now store some address
$a->setMulti($row = [
Expand All @@ -103,14 +101,17 @@ public function testContainsOne(): void
$this->assertEquals($row, $i->addr->get());
$i->reload();
$this->assertEquals($row, $i->addr->get());
$i = $i->getModel()->load($i->getId());
$this->assertEquals($row, $i->addr->get());

// now try to change some field in address
$i->addr->set($i->addr->fieldName()->address, 'bar')->save();
$this->assertSame('bar', $i->addr->address);

// now add nested containsOne - DoorCode
/** @var DoorCode */
$c = $i->addr->ref($i->addr->fieldName()->door_code);
$iEntity = $i->addr;
$c = $iEntity->getModel()->door_code->createEntity();
$c->containedInEntity = $iEntity;
$c->setMulti($row = [
$c->fieldName()->id => 1,
$c->fieldName()->code => 'ABC',
Expand Down Expand Up @@ -175,8 +176,8 @@ public function testContainsOneWhenChangeModelFields(): void

// with address
$this->assertNull($i->addr);
/** @var Address */
$a = $i->ref($i->fieldName()->addr);
$a = $i->getModel()->addr->createEntity();
$a->containedInEntity = $i;
$a->setMulti($row = [
$a->fieldName()->id => 1,
$a->fieldName()->country_id => 1,
Expand Down

0 comments on commit 9e4c71f

Please sign in to comment.