From b7a8a8a3ca6fc9fd6262fb41df04e54a00ce41e7 Mon Sep 17 00:00:00 2001 From: Michael Krecek <michael@krecek.net> Date: Thu, 25 Aug 2022 16:29:10 +0200 Subject: [PATCH 1/3] Add Model::saveWithoutScope --- src/Model.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Model.php b/src/Model.php index 755cfd4ad..ad0ee445c 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1384,6 +1384,34 @@ public function saveAndUnload(array $data = []) return $this; } + /** + * Store the data into database, but will never attempt to + * reload the data and permits that entity leaves scope after save. + * Additionally, any data will be unloaded. removeConditionFields will + * eliminate all conditions on those fields, set null to ignore all conditions. + * + * @return $this + */ + public function saveWithoutScope(array $data = [], array $removedConditionFields = null) + { + $scopeElementsOrig = $this->getModel()->scope()->elements; + try { + foreach ($this->getModel()->scope()->elements as $k => $v) { + if ($v instanceof Model\Scope\Condition && (!$removedConditionFields || in_array($v->key, $removedConditionFields))) { + unset($this->getModel()->scope()->elements[$k]); + } + } + + $this->saveAndUnload($data); + + } finally { + $this->getModel()->scope()->elements = $scopeElementsOrig; + } + + + return $this; + } + /** * Create new model from the same base class as $this. * From a45abe8247e628aa0442107d51913b6a424a73eb Mon Sep 17 00:00:00 2001 From: Michael Krecek <michael@krecek.net> Date: Thu, 25 Aug 2022 16:52:20 +0200 Subject: [PATCH 2/3] Optimized performance --- src/Model.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Model.php b/src/Model.php index ad0ee445c..a8e9b5a2c 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1396,11 +1396,13 @@ public function saveWithoutScope(array $data = [], array $removedConditionFields { $scopeElementsOrig = $this->getModel()->scope()->elements; try { - foreach ($this->getModel()->scope()->elements as $k => $v) { - if ($v instanceof Model\Scope\Condition && (!$removedConditionFields || in_array($v->key, $removedConditionFields))) { - unset($this->getModel()->scope()->elements[$k]); + if ($removedConditionFields) { + foreach ($this->getModel()->scope()->elements as $k => $v) { + if ($v instanceof Model\Scope\Condition && (!$removedConditionFields || in_array($v->key, $removedConditionFields))) { + unset($this->getModel()->scope()->elements[$k]); + } } - } + } else { $this->getModel()->scope()->elements = array(); } $this->saveAndUnload($data); From 6827c6c5498bc57f7ba7edb22054397f42869f02 Mon Sep 17 00:00:00 2001 From: Michael Krecek <michael@krecek.net> Date: Thu, 25 Aug 2022 20:10:09 +0200 Subject: [PATCH 3/3] First steps to introduce weak conditions, WIP, help needed on storing weak flag --- src/Model.php | 63 +++++++++++++++++++++++++++++++++-- src/Model/Scope.php | 2 +- src/Model/Scope/Condition.php | 3 ++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/Model.php b/src/Model.php index a8e9b5a2c..9f842d6a2 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1002,6 +1002,30 @@ public function addCondition($field, $operator = null, $value = null) return $this; } + /** + * Same as addCondition but condition will be dropped on save event and not be enforced. + * Therefore, it is valid to save an entity where the saved entity will not comply to the + * weak condition anymore. + * + * Example: + * + * $messsageModel->addWeakCondition('unread', true); + * $messageModel->save(['unread' => false]); + * + * @param mixed $field + * @param mixed $operator + * @param mixed $value + * + * @return $this + */ + public function addWeakCondition($field, $operator = null, $value = null) + { + + $this->scope()->addCondition(...func_get_args(), true); + + return $this; + } + /** * Adds WITH/CTE model. * @@ -1340,6 +1364,21 @@ public function reload() return $this; } + /** + * Try to reload model by taking its current ID, otherwise return null. + * + * @return $this + */ + public function tryReload() + { + $id = $this->getId(); + $this->unload(); + + $res = $this->_load(true, true, $id); + + return $this; + } + /** * Keeps the model data, but wipes out the ID so * when you save it next time, it ends up as a new @@ -1495,10 +1534,30 @@ public function tryLoadBy(string $fieldName, $value) return $this->_loadBy(true, $fieldName, $value); } + protected function invokeCallbackWithoutWeakConditions(Model $model, \Closure $callback) + { + $scopeElementsOrig = $model->scope()->elements; + try { + foreach ($model->scope()->elements as $k => $v) { + if ($v instanceof Model\Scope\Condition && $v->weak) { + unset($model->scope()->elements[$k]); + } + } + + return $callback(); + } finally { + $model->scope()->elements = $scopeElementsOrig; + } + } + protected function validateEntityScope(): void { if (!$this->getModel()->scope()->isEmpty()) { - $this->getPersistence()->load($this->getModel(), $this->getId()); + + $this->invokeCallbackWithoutWeakConditions($entity->getModel(), function () use ($entity): void { + $this->getPersistence()->load($this->getModel(), $this->getId()); + }); + } } @@ -1592,7 +1651,7 @@ public function save(array $data = []) if ($this->idField && $this->reloadAfterSave) { $d = $dirtyRef; $dirtyRef = []; - $this->reload(); + $this->tryReload(); $this->dirtyAfterReload = $dirtyRef; $dirtyRef = $d; } diff --git a/src/Model/Scope.php b/src/Model/Scope.php index 48256b330..c7dae28c1 100644 --- a/src/Model/Scope.php +++ b/src/Model/Scope.php @@ -74,7 +74,7 @@ public function __clone() * * @return $this */ - public function addCondition($field, $operator = null, $value = null) + public function addCondition($field, $operator = null, $value = null, $weak = false) { if (func_num_args() === 1 && $field instanceof Scope\AbstractScope) { $condition = $field; diff --git a/src/Model/Scope/Condition.php b/src/Model/Scope/Condition.php index 25e70a55a..41289d598 100644 --- a/src/Model/Scope/Condition.php +++ b/src/Model/Scope/Condition.php @@ -25,6 +25,9 @@ class Condition extends AbstractScope /** @var mixed */ public $value; + /** @var boolean */ + public $weak = false; + public const OPERATOR_EQUALS = '='; public const OPERATOR_DOESNOT_EQUAL = '!='; public const OPERATOR_GREATER = '>';