From 9ba038b2b39fa388d30c66d024f7e0a10f450012 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Mon, 6 Mar 2017 18:58:55 +0200 Subject: [PATCH] Implement ObjectStorageTrait --- .../Grav/Common/Object/ObjectStorageTrait.php | 360 ++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 system/src/Grav/Common/Object/ObjectStorageTrait.php diff --git a/system/src/Grav/Common/Object/ObjectStorageTrait.php b/system/src/Grav/Common/Object/ObjectStorageTrait.php new file mode 100644 index 000000000..9d94de5c3 --- /dev/null +++ b/system/src/Grav/Common/Object/ObjectStorageTrait.php @@ -0,0 +1,360 @@ +load($keys)) { + return $instance; + } + + // Instance exists in storage: make sure that we return the global instance. + $keys = $instance->getInstanceId(); + $reload = false; + } + + // Return global instance from the identifier. + $instance = static::$instances[$keys]; + + if ($reload) { + $instance->load(); + } + + return $instance; + } + + /** + * 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 has been stored. + * + * @return boolean True if object exists in storage. + */ + public function isSaved() + { + return $this->getStorage()->exists($this->getStorageKey()); + } + + /** + * 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. + $this->items = $storage->load($key); + + $this->initialize(); + + $id = $this->getInstanceId(); + if ($id) { + if (!isset(static::$instances[$id])) { + static::$instances[$id] = $this; + } + } + + return $this->isSaved(); + } + + /** + * Method to save the object to the storage. + * + * Before saving the object, this method checks if object can be safely saved. + * + * @param 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; + } + + try { + // Get storage. + $storage = $this->getStorage(); + $key = $this->getStorageKey(); + $exists = $this->getStorage()->exists($key); + + // Get data to be saved. + $data = $this->prepareSave(); + + // Save the object. + $id = $storage->save($key, $data); + } catch (\Exception $e) { + return false; + } + + if (!$id) { + return false; + } + + // If item was created, load the object (making sure it has been properly initialized). + if (!$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->isSaved() || !$this->onBeforeDelete()) { + return false; + } + + if ($includeChildren) { + $this->deleteChildren(); + } + + // Get storage. + $storage = $this->getStorage(); + + if (!$storage->delete($this->getStorageKey())) { + return false; + } + + $this->onAfterDelete(); + + return true; + } + + /** + * Method to perform sanity checks on the instance properties to ensure they are safe to store in the storage. + * + * Child classes should override this method to make sure the data they are storing in the storage is safe and as + * expected before saving the object. + * + * @return bool True if the instance is sane and able to be stored in the storage. + */ + public function check($includeChildren = false) + { + $result = true; + + if ($includeChildren) { + foreach ($this as $field => $value) { + if (is_object($value) && method_exists($value, 'check')) { + $result = $result && $value->check(); + } + } + } + + return $result; + } + + // Internal functions + + /** + * @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 as $field => $value) { + if (is_object($value) && method_exists($value, 'save')) { + $value->save(true); + } + } + } + + protected function deleteChildren() + { + foreach ($this as $field => $value) { + if (is_object($value) && method_exists($value, 'delete')) { + $value->delete(true); + } + } + } + + protected function prepareSave(array $data = null) + { + if ($data === null) { + $data = $this->toArray(); + } + + 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 = []); + + public function getInstanceId(array $keys = []) + { + return $this->getStorageKey($keys); + } + + //abstract public function setStorageKey(array $keys = []); + + /** + * @return StorageInterface + */ + protected function getStorage() + { + return static::$storage; + } +}