From 703080a3293f9dca737e2bcb947ced5dc3d22226 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Tue, 7 Mar 2017 15:45:15 +0200 Subject: [PATCH] Start using StorageTrait for AbstractObject and AbstractObjectCollection --- .../src/Grav/Common/Object/AbstractObject.php | 426 +++--------------- .../Object/AbstractObjectCollection.php | 23 + .../Grav/Common/Object/ObjectCollection.php | 11 - .../Grav/Common/Object/ObjectInterface.php | 8 +- .../Grav/Common/Object/ObjectStorageTrait.php | 47 +- 5 files changed, 130 insertions(+), 385 deletions(-) create mode 100644 system/src/Grav/Common/Object/AbstractObjectCollection.php delete mode 100644 system/src/Grav/Common/Object/ObjectCollection.php diff --git a/system/src/Grav/Common/Object/AbstractObject.php b/system/src/Grav/Common/Object/AbstractObject.php index a2b07e848..9a462afbb 100644 --- a/system/src/Grav/Common/Object/AbstractObject.php +++ b/system/src/Grav/Common/Object/AbstractObject.php @@ -1,7 +1,6 @@ null + ]; + /** * Default properties for the object. * @var array */ - static protected $defaults = ['id' => null]; + static protected $defaults = []; /** * @var string */ - static protected $collectionClass = 'Grav\\Common\\Object\\ObjectCollection'; + static protected $collectionClass = 'Grav\\Common\\Object\\AbstractObjectCollection'; /** * Properties of the object. @@ -49,32 +47,6 @@ abstract class AbstractObject implements ObjectInterface */ protected $items; - /** - * Does object exist in storage? - * @var boolean - */ - protected $exists = false; - - /** - * Readonly object. - * @var bool - */ - protected $readonly = false; - - /** - * @var bool - */ - protected $initialized = false; - - - /** - * @param StorageInterface $storage - */ - static public function setStorage(StorageInterface $storage) - { - static::$storage = $storage; - } - /** * @param ObjectFinderInterface $finder */ @@ -83,48 +55,10 @@ abstract class AbstractObject implements ObjectInterface static::$finder = $finder; } - /** - * Returns the global instance to the object. - * - * Note that using array of fields will always make a query, but it's very useful feature if you want to search one - * item by using arbitrary set of matching fields. If there are more than one matching object, first one gets returned. - * - * @param string|array $keys An optional primary key value to load the object by, or an array of fields to match. - * @param boolean $reload Force object to reload. - * - * @return Object - */ - static public function instance($keys = null, $reload = false) - { - // If we are creating or loading a new item or we load instance by alternative keys, we need to create a new object. - if (!$keys || is_array($keys) || (is_scalar($keys) && !isset(static::$instances[$keys]))) { - $c = get_called_class(); - - /** @var ObjectStorageTrait $instance */ - $instance = new $c(); - $instance->load($keys); - - /** @var Object $instance */ - if (!$instance->exists()) return $instance; - - // Instance exists: make sure that we return the global instance. - $keys = $instance->id; - } - - // Return global instance from the identifier. - $instance = static::$instances[$keys]; - - if ($reload) { - $instance->load(); - } - - return $instance; - } - /** * @param array $ids List of primary Ids or null to return everything that has been loaded. * @param bool $readonly - * @return ObjectCollection + * @return AbstractObjectCollection */ static public function instances(array $ids = null, $readonly = true) { @@ -154,11 +88,10 @@ abstract class AbstractObject implements ObjectInterface foreach ($list as $keys) { /** @var static $instance */ $instance = new $c(); - $instance->set($keys); - $instance->exists(true); - $instance->initialize(); - $id = $instance->id; + $instance->doLoad($keys); + $id = $instance->getId(); if ($id && !isset(static::$instances[$id])) { + $instance->initialize(); static::$instances[$id] = $instance; } } @@ -173,6 +106,14 @@ abstract class AbstractObject implements ObjectInterface return new $collectionClass($results); } + /** + * @return string + */ + public function getId() + { + return implode('-', $this->getKeys()); + } + /** * @return ObjectFinderInterface */ @@ -181,186 +122,6 @@ abstract class AbstractObject implements ObjectInterface return static::$finder; } - /** - * Removes all or selected instances from the object cache. - * - * @param null|string|array $ids An optional primary key or list of keys. - */ - static public function freeInstances($ids = null) - { - if ($ids === null) { - $ids = array_keys(static::$instances); - } - $ids = (array) $ids; - - foreach ($ids as $id) { - unset(static::$instances[$id]); - } - } - - /** - * Override this function if you need to initialize object right after creating it. - * - * Can be used for example if the fields need to be converted from json strings to array. - * - * @return bool True if initialization was done, false if object was already initialized. - */ - public function initialize() - { - $initialized = $this->initialized; - $this->initialized = true; - - return !$initialized; - } - - /** - * Convert instance to a read only object. - * - * @return $this - */ - public function readonly() - { - $this->readonly = true; - - return $this; - } - - /** - * Returns true if the object exists. - * - * @param boolean $exists Internal parameter to change state. - * - * @return boolean True if object exists. - */ - public function exists($exists = null) - { - $return = $this->exists; - if ($exists !== null) { - $this->exists = (bool) $exists; - } - - return $return; - } - - /** - * Method to load object from the storage. - * - * @param mixed $keys An optional primary key value to load the object by, or an array of fields to match. If not - * set the instance key value is used. - * @param bool $getKey Internal parameter, please do not use. - * - * @return bool True on success, false if the object doesn't exist. - */ - public function load($keys = null, $getKey = true) - { - if ($getKey) { - if (is_scalar($keys)) { - $keys = ['id' => (string) $keys]; - } - - // Fetch internal key. - $key = $this->getStorageKey($keys); - - } else { - // Internal key was passed. - $key = $keys; - $keys = []; - } - - // Get storage. - $storage = $this->getStorage(); - - // Load the object based on the keys. - $items = $storage->load($key); - $this->exists = !empty($items); - - // Append the defaults and keys if they were not set by load(). - $this->items = array_merge(static::$defaults, $keys, $this->items, $items); - - $this->initialize(); - - if ($this->id) { - if (!isset(static::$instances[$this->id])) { - static::$instances[$this->id] = $this; - } - } - - return $this->exists; - } - - /** - * Method to save the object to the storage. - * - * Before saving the object, this method checks if object can be safely saved. - * - * @params bool $includeChildren - * @return bool True on success. - */ - public function save($includeChildren = false) - { - // Check the object. - if ($this->readonly || !$this->check($includeChildren) || !$this->onBeforeSave()) { - return false; - } - - // Get storage. - $storage = $this->getStorage(); - $key = $this->getStorageKey(); - - // Get data to be saved. - $data = $this->prepareSave($this->toArray()); - - // Save the object. - $id = $storage->save($key, $data); - - if (!$id) { - throw new \LogicException('No id specified'); - } - - // If item was created, load the object. - if (!$this->exists) { - $this->load($id, false); - } - - if ($includeChildren) { - $this->saveChildren(); - } - - $this->onAfterSave(); - - return true; - } - - /** - * Method to delete the object from the database. - * - * @param bool $includeChildren - * @return bool True on success. - */ - public function delete($includeChildren = false) - { - if ($this->readonly || !$this->exists || !$this->onBeforeDelete()) { - return false; - } - - if ($includeChildren) { - $this->deleteChildren(); - } - - // Get storage. - $storage = $this->getStorage(); - - if (!$storage->delete($this->getStorageKey())) { - return false; - } - - $this->exists = false; - - $this->onAfterDelete(); - - return true; - } - /** * Method to perform sanity checks on the instance properties to ensure they are safe to store in the storage. * @@ -371,17 +132,46 @@ abstract class AbstractObject implements ObjectInterface */ public function check($includeChildren = false) { - $result = true; + return $this->checkKeys() && $this->traitCheck($includeChildren); + } - if ($includeChildren) { - foreach ($this->items as $field => $value) { - if (is_object($value) && method_exists($value, 'check')) { - $result = $result && $value->check(); + /** + * @param array $keys + * @return array + */ + public function getKeys(array $keys = []) + { + foreach (static::$primaryKey as $key => $value) { + if (!isset($keys[$key])) { + if (isset($this->items[$key])) { + $keys[$key] = $this->items[$key]; + } else { + $keys[$key] = $value; } + } } - return $result && !empty($this->items['id']); + return $keys; + } + + /** + * @param array $keys + * @return bool + */ + public function checkKeys(array $keys = []) + { + if (!$keys) { + $keys = $this->getKeys(); + } + + foreach ($keys as $key => $value) { + if ($value === null) { + return false; + } + } + + return true; } /** @@ -394,94 +184,24 @@ abstract class AbstractObject implements ObjectInterface return $this->toArray(); } + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + // Internal functions /** * @param array $items - * @return $this + * @param array $keys */ - protected function set(array $items) + protected function doLoad(array $items, array $keys = []) { - $this->items = $items; - - return $this; - } - - /** - * @return bool - */ - protected function onBeforeSave() - { - return true; - } - - protected function onAfterSave() - { - } - - /** - * @return bool - */ - protected function onBeforeDelete() - { - return true; - } - - protected function onAfterDelete() - { - } - - protected function saveChildren() - { - foreach ($this->toArray() as $field => $value) { - if (is_object($value) && method_exists($value, 'save')) { - $value->save(true); - } - } - } - - protected function deleteChildren() - { - foreach ($this->toArray() as $field => $value) { - if (is_object($value) && method_exists($value, 'delete')) { - $value->delete(true); - } - } - } - - protected function prepareSave(array $data) - { - foreach ($data as $field => $value) { - if (is_object($value) && method_exists($value, 'save')) { - unset($data[$field]); - } - } - - return $data; - } - - /** - * Method to get the storage key for the object. - * - * @param array - * @return string - */ - abstract public function getStorageKey(array $keys = []); - - /** - * @return StorageInterface - */ - protected static function getStorage() - { - if (!static::$storage) { - static::loadStorage(); - } - - return static::$storage; - } - - protected static function loadStorage() - { - throw new \RuntimeException('Storage has not been set.'); + $this->items = array_replace(static::$defaults, $this->items, $this->getKeys($keys), $items); } } diff --git a/system/src/Grav/Common/Object/AbstractObjectCollection.php b/system/src/Grav/Common/Object/AbstractObjectCollection.php new file mode 100644 index 000000000..4cd61bff3 --- /dev/null +++ b/system/src/Grav/Common/Object/AbstractObjectCollection.php @@ -0,0 +1,23 @@ +id = $id; + } + + public function getId() + { + return $this->id; + } +} diff --git a/system/src/Grav/Common/Object/ObjectCollection.php b/system/src/Grav/Common/Object/ObjectCollection.php deleted file mode 100644 index c6d29176d..000000000 --- a/system/src/Grav/Common/Object/ObjectCollection.php +++ /dev/null @@ -1,11 +0,0 @@ - (string) $keys]; + } + $id = $keys ? static::getInstanceId($keys) : null; + // If we are creating or loading a new item or we load instance by alternative keys, we need to create a new object. - if (!$keys || is_array($keys) || (is_scalar($keys) && !isset(static::$instances[$keys]))) { + if (!$id || !isset(static::$instances[$id])) { $c = get_called_class(); /** @var ObjectStorageTrait|ObjectInterface $instance */ @@ -66,12 +71,12 @@ trait ObjectStorageTrait } // Instance exists in storage: make sure that we return the global instance. - $keys = $instance->getInstanceId(); + $id = $instance->getId(); $reload = false; } // Return global instance from the identifier. - $instance = static::$instances[$keys]; + $instance = static::$instances[$id]; if ($reload) { $instance->load(); @@ -151,7 +156,7 @@ trait ObjectStorageTrait } // Fetch internal key. - $key = $this->getStorageKey($keys); + $key = $keys ? $this->getStorageKey($keys) : null; } else { // Internal key was passed. @@ -159,15 +164,10 @@ trait ObjectStorageTrait $keys = []; } - // Get storage. - $storage = $this->getStorage(); - - // Load the object based on the keys. - $this->items = $storage->load($key); - + $this->doLoad($this->getStorage()->load($key), $keys); $this->initialize(); - $id = $this->getInstanceId(); + $id = $this->getId(); if ($id) { if (!isset(static::$instances[$id])) { static::$instances[$id] = $this; @@ -195,12 +195,12 @@ trait ObjectStorageTrait // Get storage. $storage = $this->getStorage(); $key = $this->getStorageKey(); - $exists = $this->getStorage()->exists($key); // Get data to be saved. $data = $this->prepareSave(); // Save the object. + $exists = $storage->exists($key); $id = $storage->save($key, $data); if (!$id) { @@ -262,7 +262,7 @@ trait ObjectStorageTrait $result = true; if ($includeChildren) { - foreach ($this as $field => $value) { + foreach ($this->toArray() as $field => $value) { if (is_object($value) && method_exists($value, 'check')) { $result = $result && $value->check(); } @@ -274,6 +274,8 @@ trait ObjectStorageTrait // Internal functions + abstract protected function doLoad(array $items, array $keys = []); + /** * @return bool */ @@ -300,7 +302,7 @@ trait ObjectStorageTrait protected function saveChildren() { - foreach ($this as $field => $value) { + foreach ($this->toArray() as $field => $value) { if (is_object($value) && method_exists($value, 'save')) { $value->save(true); } @@ -309,7 +311,7 @@ trait ObjectStorageTrait protected function deleteChildren() { - foreach ($this as $field => $value) { + foreach ($this->toArray() as $field => $value) { if (is_object($value) && method_exists($value, 'delete')) { $value->delete(true); } @@ -339,11 +341,24 @@ trait ObjectStorageTrait */ abstract public function getStorageKey(array $keys = []); - public function getInstanceId(array $keys = []) + /** + * @param array $keys + * @return string + */ + public function getInstanceId(array $keys) { return $this->getStorageKey($keys); } + /** + * @return string + */ + public function getId() + { + return $this->getStorageKey(); + } + + //abstract public function setStorageKey(array $keys = []); /**