From e3f05b25f961e0169185acabd32566e2ae5198fe Mon Sep 17 00:00:00 2001 From: Thomas Lange Date: Tue, 10 Aug 2021 17:42:11 +0200 Subject: Add a better mechanism to detect Entity changes Implement and use a better mechanism to detect changes of attributes of the Entity objects by using a private variable which keeps track of the changed Entity attributes ("properties") via the "set" method. The "insert" and "update" method of the Repository now calls the method "getModifiedKeys" of the Entity class to get a list of properties which have been changed and builds the database query accordingly. This makes the use of "FALSE" as default value for the Entity attributes obsolete, so they have been set to the initial PHP default ("NULL"). --- core/namespace/ORM/Entities/Category.php | 10 ++--- core/namespace/ORM/Entities/Page.php | 10 ++--- core/namespace/ORM/Entities/Post.php | 12 +++--- core/namespace/ORM/Entities/User.php | 14 +++---- core/namespace/ORM/Entity.php | 16 +++++--- .../ORM/Repositories/CategoryRepository.php | 23 +++++------ core/namespace/ORM/Repository.php | 44 ++++++++++++---------- 7 files changed, 68 insertions(+), 61 deletions(-) (limited to 'core/namespace/ORM') diff --git a/core/namespace/ORM/Entities/Category.php b/core/namespace/ORM/Entities/Category.php index cf3ca5f..e8c4496 100644 --- a/core/namespace/ORM/Entities/Category.php +++ b/core/namespace/ORM/Entities/Category.php @@ -3,9 +3,9 @@ namespace ORM\Entities; use ORM\Entity; class Category extends Entity { - protected $parent = FALSE; - protected $slug = FALSE; - protected $name = FALSE; - protected $body = FALSE; - protected $argv = FALSE; + protected $parent; + protected $slug; + protected $name; + protected $body; + protected $argv; } diff --git a/core/namespace/ORM/Entities/Page.php b/core/namespace/ORM/Entities/Page.php index 96ff233..1a65c01 100644 --- a/core/namespace/ORM/Entities/Page.php +++ b/core/namespace/ORM/Entities/Page.php @@ -3,9 +3,9 @@ namespace ORM\Entities; use ORM\Entity; class Page extends Entity { - protected $user = FALSE; - protected $slug = FALSE; - protected $name = FALSE; - protected $body = FALSE; - protected $argv = FALSE; + protected $user; + protected $slug; + protected $name; + protected $body; + protected $argv; } diff --git a/core/namespace/ORM/Entities/Post.php b/core/namespace/ORM/Entities/Post.php index 6a52a1f..8403a65 100644 --- a/core/namespace/ORM/Entities/Post.php +++ b/core/namespace/ORM/Entities/Post.php @@ -3,10 +3,10 @@ namespace ORM\Entities; use ORM\Entity; class Post extends Entity { - protected $user = FALSE; - protected $category = FALSE; - protected $slug = FALSE; - protected $name = FALSE; - protected $body = FALSE; - protected $argv = FALSE; + protected $user; + protected $category; + protected $slug; + protected $name; + protected $body; + protected $argv; } diff --git a/core/namespace/ORM/Entities/User.php b/core/namespace/ORM/Entities/User.php index 65d5345..78e03db 100644 --- a/core/namespace/ORM/Entities/User.php +++ b/core/namespace/ORM/Entities/User.php @@ -3,11 +3,11 @@ namespace ORM\Entities; use ORM\Entity; class User extends Entity { - protected $slug = FALSE; - protected $username = FALSE; - protected $password = FALSE; - protected $fullname = FALSE; - protected $mailaddr = FALSE; - protected $body = FALSE; - protected $argv = FALSE; + protected $slug; + protected $username; + protected $password; + protected $fullname; + protected $mailaddr; + protected $body; + protected $argv; } diff --git a/core/namespace/ORM/Entity.php b/core/namespace/ORM/Entity.php index 9cc7755..a19b1f4 100644 --- a/core/namespace/ORM/Entity.php +++ b/core/namespace/ORM/Entity.php @@ -6,6 +6,9 @@ abstract class Entity implements EntityInterface { protected $time_insert; protected $time_update; + # Modified attributes + private $_modified = []; + #=============================================================================== # Get attribute #=============================================================================== @@ -17,6 +20,11 @@ abstract class Entity implements EntityInterface { # Set attribute #=============================================================================== public function set(string $attribute, $value) { + if($this->{$attribute} !== $value) { + !in_array($attribute, $this->_modified) && + array_push($this->_modified, $attribute); + } + return $this->{$attribute} = $value; } @@ -39,11 +47,9 @@ abstract class Entity implements EntityInterface { } #=============================================================================== - # Get array with all non-false attributes + # Get an array of modified attribute keys #=============================================================================== - public function getFilteredAttributes(): array { - return array_filter(get_object_vars($this), function($value) { - return $value !== FALSE; - }); + public function getModifiedKeys(): array { + return $this->_modified; } } diff --git a/core/namespace/ORM/Repositories/CategoryRepository.php b/core/namespace/ORM/Repositories/CategoryRepository.php index b62485a..8692079 100644 --- a/core/namespace/ORM/Repositories/CategoryRepository.php +++ b/core/namespace/ORM/Repositories/CategoryRepository.php @@ -96,36 +96,31 @@ class CategoryRepository extends Repository { # Update category (and check for parent/child circular loops) #=============================================================================== public function update(EntityInterface $Entity): bool { - # Entity parent might have changed *in memory*, so we re-fetch the original - # parent of the entity from the database and save it in a variable. - # TODO: Repository/Entity class should have a mechanism to detect changes! + # Skip circular loop check if parent is unchanged + if(!in_array('parent', $Entity->getModifiedKeys())) { + return parent::update($Entity); + } + $query = 'SELECT parent FROM %s WHERE id = ?'; $query = sprintf($query, static::getTableName()); $Statement = $this->Database->prepare($query); $Statement->execute([$Entity->getID()]); - $parent = $Statement->fetchColumn(); - - # If parent is unchanged, circular loop check is not needed. - if($parent === $Entity->get('parent')) { - return parent::update($Entity); - } - $_parent = $Entity->get('parent'); # Fetch the parent of the *new* parent category and let the while loop run through # the tree until either a parent of "NULL" was found or if the new parent category # is a *child* of the *current* category which would cause a circular loop. while($Statement->execute([$_parent]) && $_parent = $Statement->fetchColumn()) { - if($_parent == $Entity->get('id')) { + if($_parent == $Entity->getID()) { # Set parent of the *new* parent category to the *original* parent category # of the *current* category (one level up) to prevent a circular loop. - $query = 'UPDATE %s SET parent = ? WHERE id = ?'; - $query = sprintf($query, static::getTableName()); + $query = 'UPDATE %s SET parent = (SELECT parent FROM %s WHERE id = ?) WHERE id = ?'; + $query = sprintf($query, static::getTableName(), static::getTableName()); $UpdateStatement = $this->Database->prepare($query); - $UpdateStatement->execute([$parent, $Entity->get('parent')]); + $UpdateStatement->execute([$_parent, $Entity->get('parent')]); break; } else if($_parent === NULL) { break; diff --git a/core/namespace/ORM/Repository.php b/core/namespace/ORM/Repository.php index 423a286..e7750cb 100644 --- a/core/namespace/ORM/Repository.php +++ b/core/namespace/ORM/Repository.php @@ -76,40 +76,46 @@ abstract class Repository { # Insert entity #=========================================================================== public function insert(EntityInterface $Entity): bool { - $attributes = $Entity->getFilteredAttributes(); - - foreach($attributes as $field => $value) { - $fields[] = $field; + foreach($Entity->getModifiedKeys() as $attribute) { + $params[] = $Entity->get($attribute); + $fields[] = $attribute; $values[] = '?'; } - $fields = implode(', ', $fields ?? []); - $values = implode(', ', $values ?? []); + if(isset($params, $fields, $values)) { + $fields = implode(', ', $fields); + $values = implode(', ', $values); - $query = 'INSERT INTO %s (%s) VALUES(%s)'; - $query = sprintf($query, static::getTableName(), $fields, $values); + $query = sprintf('INSERT INTO %s (%s) VALUES(%s)', + static::getTableName(), $fields, $values); - $Statement = $this->Database->prepare($query); - return $Statement->execute(array_values($attributes)); + $Statement = $this->Database->prepare($query); + return $Statement->execute($params); + } + + return FALSE; } #=========================================================================== # Update entity #=========================================================================== public function update(EntityInterface $Entity): bool { - $attributes = $Entity->getFilteredAttributes(); - - foreach($attributes as $field => $value) { - $params[] = "$field = ?"; + foreach($Entity->getModifiedKeys() as $attribute) { + $params[] = $Entity->get($attribute); + $fields[] = "$attribute = ?"; } - $params = implode(', ', $params ?? []); + if(isset($params, $fields)) { + $fields = implode(', ', $fields); - $query = 'UPDATE %s SET %s WHERE id = '.intval($Entity->getID()); - $query = sprintf($query, static::getTableName(), $params); + $query = sprintf('UPDATE %s SET %s WHERE id = %d', + static::getTableName(), $fields, $Entity->getID()); - $Statement = $this->Database->prepare($query); - return $Statement->execute(array_values($attributes)); + $Statement = $this->Database->prepare($query); + return $Statement->execute($params); + } + + return FALSE; } #=========================================================================== -- cgit v1.2.3