From 9feaefb944faed5a3e4ca0508a624675e3ee6626 Mon Sep 17 00:00:00 2001 From: Rudi Servo Date: Thu, 10 Aug 2023 15:57:09 +0000 Subject: [PATCH] Fix #16343, DynamicUpdate is now enabled system wide --- CHANGELOG-5.0.md | 1 + config.json | 4 + phalcon/Mvc/Model.zep | 2 +- phalcon/Mvc/Model/Manager.zep | 8 + .../models/CustomersDymanicUpdate.php | 38 +++ .../database/Mvc/Model/DynamicUpdateCest.php | 219 ++++++++++++++++++ tests/unit/Mvc/GlobalsCest.php | 4 + 7 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tests/_data/fixtures/models/CustomersDymanicUpdate.php create mode 100644 tests/database/Mvc/Model/DynamicUpdateCest.php diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md index f65598c369b..375605e2c40 100644 --- a/CHANGELOG-5.0.md +++ b/CHANGELOG-5.0.md @@ -16,6 +16,7 @@ ### Fixed - Parse multipart/form-data from PUT request [#16271](https://github.com/phalcon/cphalcon/issues/16271) +- Set Dynamic Update by default system wide [#16343](https://github.com/phalcon/cphalcon/issues/16343) ## [5.2.3](https://github.com/phalcon/cphalcon/releases/tag/v5.2.3) (2023-07-26) diff --git a/config.json b/config.json index 68464ed9b0d..a8939dea647 100644 --- a/config.json +++ b/config.json @@ -149,6 +149,10 @@ "type": "bool", "default": true }, + "orm.dynamic_update": { + "type": "bool", + "default": true + }, "warning.enable": { "type": "bool", "default": true diff --git a/phalcon/Mvc/Model.zep b/phalcon/Mvc/Model.zep index 7a9c65e2d95..fa7096ebbad 100644 --- a/phalcon/Mvc/Model.zep +++ b/phalcon/Mvc/Model.zep @@ -3973,7 +3973,7 @@ abstract class Model extends AbstractInjectionAware implements EntityInterface, let columnMap = null; } - if useDynamicUpdate && typeof snapshot !== "array" { + if likely useDynamicUpdate && typeof snapshot === "array" { for field in nonPrimary { let changed = false; if typeof columnMap === "array" { diff --git a/phalcon/Mvc/Model/Manager.zep b/phalcon/Mvc/Model/Manager.zep index 6bfb969a177..99cee971aef 100644 --- a/phalcon/Mvc/Model/Manager.zep +++ b/phalcon/Mvc/Model/Manager.zep @@ -1848,6 +1848,10 @@ class Manager implements ManagerInterface, InjectionAwareInterface, EventsAwareI { var isKeeping; + if globals_get("orm.dynamic_update") { + return true; + } + if !fetch isKeeping, this->keepSnapshots[get_class_lower(model)] { return false; } @@ -1866,6 +1870,10 @@ class Manager implements ManagerInterface, InjectionAwareInterface, EventsAwareI { var isUsing; + if globals_get("orm.dynamic_update") { + return true; + } + if !fetch isUsing, this->dynamicUpdate[get_class_lower(model)] { return false; } diff --git a/tests/_data/fixtures/models/CustomersDymanicUpdate.php b/tests/_data/fixtures/models/CustomersDymanicUpdate.php new file mode 100644 index 00000000000..12106c1762d --- /dev/null +++ b/tests/_data/fixtures/models/CustomersDymanicUpdate.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the + * LICENSE.txt file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Models; + +use Phalcon\Mvc\Model; + +/** + * Class CustomersKeepSnapshots + * + * @property int $cst_id; + * @property int $cst_status_flag; + * @property string $cst_name_last; + * @property string $cst_name_first; + */ +class CustomersDymanicUpdate extends Model +{ + public $cst_id; + public $cst_status_flag; + public $cst_name_last; + public $cst_name_first; + + public function initialize() + { + $this->useDynamicUpdate(true); + $this->setSource('co_customers'); + } +} diff --git a/tests/database/Mvc/Model/DynamicUpdateCest.php b/tests/database/Mvc/Model/DynamicUpdateCest.php new file mode 100644 index 00000000000..aa108d6cfc2 --- /dev/null +++ b/tests/database/Mvc/Model/DynamicUpdateCest.php @@ -0,0 +1,219 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Phalcon\Tests\Integration\Mvc\Model; + +use DatabaseTester; +use Phalcon\Events\Event; +use Phalcon\Events\Manager; +use Phalcon\Support\Collection; +use Phalcon\Tests\Fixtures\Migrations\CustomersMigration; +use Phalcon\Tests\Fixtures\Traits\DiTrait; +use Phalcon\Tests\Models\Customers; +use Phalcon\Tests\Models\CustomersDymanicUpdate; + +class DynamicUpdateCest +{ + use DiTrait; + + public function _before(DatabaseTester $I) + { + $this->setNewFactoryDefault(); + $this->setDatabase($I); + } + + public function _after(DatabaseTester $I) + { + $this->container['db']->close(); + } + + /** + * Tests Phalcon\Mvc\Model :: save() With DynamicUpdate Enabled + * + * @author Phalcon Team + * @since 2023-08-11 + * + * @issue https://github.com/phalcon/cphalcon/issues/16343 + * + * @group mysql + * @group pgsql + * @group sqlite + */ + public function enableDynamicUpdate(DatabaseTester $I) + { + $I->wantToTest('Mvc\Model - DynamicUpdate System Wide Enabled'); + + $connection = $I->getConnection(); + + $customersMigration = new CustomersMigration($connection); + $customersMigration->insert(90, 1, null, null); + + /** + * Check system wide Dynamic update + */ + $actual = ini_get('phalcon.orm.dynamic_update'); + $I->assertEquals("1", $actual); + + $collection = new Collection(); + $connection = $this->container->get('db'); + $manager = new Manager(); + $modelsManager = $this->container->get('modelsManager'); + $manager->attach('db:beforeQuery', function (Event $event) use ($connection, $collection) { + $key = (string) $collection->count(); + $collection->set($key, $connection->getSQLVariables()); + }); + + $connection->setEventsManager($manager); + + /** + * New model + * @var Customer + */ + $customer = Customers::findFirst(['cst_id=:id:', 'bind' => ['id' => 90]]); + $customer->cst_name_first = 'enableDynamicUpdate'; + + $actual = $customer->save(); + $I->assertTrue($actual); + + $actual = $modelsManager->isUsingDynamicUpdate($customer); + $I->assertTrue($actual); + + $collection->clear(); + + $customer->cst_name_last = 'cst_test_lastName'; + + $actual = $customer->save(); + $I->assertTrue($actual); + + $expected = 2; + $actual = count($collection->get('0')); + $I->assertEquals($expected, $actual); + } + + /** + * Tests Phalcon\Mvc\Model :: save() with DynamicUpdate Disabled + * + * @author Phalcon Team + * @since 2023-08-11 + * + * @group mysql + * @group pgsql + * @group sqlite + */ + public function disableDynamicUpdate(DatabaseTester $I) + { + $I->wantToTest('Mvc\Model - DynamicUpdate Systeam Wide Disabled'); + $collection = new Collection(); + + $connection = $this->container->get('db'); + $manager = new Manager(); + $modelsManager = $this->container->get('modelsManager'); + $manager->attach('db:beforeQuery', function (Event $event) use ($connection, $collection) { + $key = (string) $collection->count(); + $collection->set($key, $connection->getSQLVariables()); + }); + + $connection->setEventsManager($manager); + + /** + * Disable system wide dynamic update + */ + ini_set('phalcon.orm.dynamic_update', "0"); + + /** + * Check system wide Dynamic update + */ + $actual = ini_get('phalcon.orm.dynamic_update'); + + $I->assertEquals("0", $actual); + + /** + * New model + * @var Customer + */ + $customer = Customers::findFirst(['cst_id=:id:', 'bind' => ['id' => 90]]); + $customer->cst_name_first = 'disableDynamicUpdate'; + $actual = $customer->save(); + + $I->assertTrue($actual); + $actual = $modelsManager->isUsingDynamicUpdate($customer); + $I->assertFalse($actual); + + $collection->clear(); + + $customer->cst_name_last = 'cst_test_lastName'; + + $actual = $customer->save(); + $I->assertTrue($actual); + + $expected = 4; + $actual = count($collection->get('0')); + $I->assertEquals($expected, $actual); + } + + /** + * Tests Phalcon\Mvc\Model :: save() with DynamicUpdate Disabled Cherry pick + * + * @author Phalcon Team + * @since 2023-08-11 + * + * @group mysql + * @group pgsql + * @group sqlite + */ + public function disabledCherryPickDynamicUpdate(DatabaseTester $I) + { + $I->wantToTest('Mvc\Model - DynamicUpdate Systeam Wide Disabled Cherry pick'); + + $collection = new Collection(); + $connection = $this->container->get('db'); + $manager = new Manager(); + $modelsManager = $this->container->get('modelsManager'); + $manager->attach('db:beforeQuery', function (Event $event) use ($connection, $collection) { + $key = (string) $collection->count(); + $collection->set($key, $connection->getSQLVariables()); + }); + + $connection->setEventsManager($manager); + + /** + * Disable system wide dynamic update + */ + ini_set('phalcon.orm.dynamic_update', "0"); + + /** + * Check system wide Dynamic update + */ + $actual = ini_get('phalcon.orm.dynamic_update'); + $I->assertEquals("0", $actual); + + /** + * New model + * @var CustomersDymanicUpdate + */ + $customer = CustomersDymanicUpdate::findFirst(['cst_id=:id:', 'bind' => ['id' => 90]]); + + $actual = $modelsManager->isUsingDynamicUpdate($customer); + $I->assertTrue($actual); + + $collection->clear(); + + $customer->cst_name_first = 'disabledCherryPickDynamicUpdate'; + $actual = $customer->save(); + $I->assertTrue($actual); + + $expected = 2; + $actual = count($collection->get('0')); + $I->assertEquals($expected, $actual); + } +} diff --git a/tests/unit/Mvc/GlobalsCest.php b/tests/unit/Mvc/GlobalsCest.php index 6b9269b987e..26192120ed0 100644 --- a/tests/unit/Mvc/GlobalsCest.php +++ b/tests/unit/Mvc/GlobalsCest.php @@ -117,6 +117,10 @@ private function getExamples(): array 'setting' => 'phalcon.orm.virtual_foreign_keys', 'value' => '1', ], + [ + 'setting' => 'phalcon.orm.dynamic_update', + 'value' => '1', + ], ]; } }