From 77c2e47b9566263b6925aa046eb4c61ddc95c155 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 11 May 2017 13:20:01 +0300 Subject: [PATCH] Greatly simplify Object classes --- .../Grav/Framework/Object/AbstractObject.php | 185 --------- .../Object/AbstractObjectCollection.php | 35 -- system/src/Grav/Framework/Object/Object.php | 58 +++ .../Framework/Object/ObjectCollection.php | 50 +++ .../Object/ObjectCollectionInterface.php | 15 +- .../Object/ObjectCollectionTrait.php | 20 +- .../Grav/Framework/Object/ObjectInterface.php | 45 +- .../Framework/Object/ObjectStorageTrait.php | 385 ------------------ .../src/Grav/Framework/Object/ObjectTrait.php | 64 +++ .../Object/Storage/FilesystemStorage.php | 145 ------- .../Object/Storage/StorageInterface.php | 47 --- .../Object/StoredObjectInterface.php | 58 --- 12 files changed, 206 insertions(+), 901 deletions(-) delete mode 100644 system/src/Grav/Framework/Object/AbstractObject.php delete mode 100644 system/src/Grav/Framework/Object/AbstractObjectCollection.php create mode 100644 system/src/Grav/Framework/Object/Object.php create mode 100644 system/src/Grav/Framework/Object/ObjectCollection.php delete mode 100644 system/src/Grav/Framework/Object/ObjectStorageTrait.php create mode 100644 system/src/Grav/Framework/Object/ObjectTrait.php delete mode 100644 system/src/Grav/Framework/Object/Storage/FilesystemStorage.php delete mode 100644 system/src/Grav/Framework/Object/Storage/StorageInterface.php delete mode 100644 system/src/Grav/Framework/Object/StoredObjectInterface.php diff --git a/system/src/Grav/Framework/Object/AbstractObject.php b/system/src/Grav/Framework/Object/AbstractObject.php deleted file mode 100644 index 0508162e2..000000000 --- a/system/src/Grav/Framework/Object/AbstractObject.php +++ /dev/null @@ -1,185 +0,0 @@ - null - ]; - - /** - * Default properties for the object. - * @var array - */ - static protected $defaults = []; - - /** - * @var string - */ - static protected $collectionClass = 'Grav\\Framework\\Object\\AbstractObjectCollection'; - - /** - * Properties of the object. - * @var array - */ - protected $items; - - /** - * @param array $ids List of primary Ids or null to return everything that has been loaded. - * @param bool $readonly - * @return AbstractObjectCollection - */ - static public function instances(array $ids = null, $readonly = true) - { - $collectionClass = static::$collectionClass; - - if (is_null($ids)) { - return new $collectionClass(static::$instances); - } - - if (empty($ids)) { - return new $collectionClass([]); - } - - $results = []; - $list = []; - - foreach ($ids as $id) { - if (!isset(static::$instances[$id])) { - $list[] = $id; - } - } - - if ($list) { - $c = get_called_class(); - $storage = static::getStorage(); - $list = $storage->loadList($list); - foreach ($list as $keys) { - /** @var static $instance */ - $instance = new $c(); - $instance->doLoad($keys); - $id = $instance->getId(); - if ($id && !isset(static::$instances[$id])) { - $instance->initialize(); - static::$instances[$id] = $instance; - } - } - } - - foreach ($ids as $id) { - if (isset(static::$instances[$id])) { - $results[$id] = $readonly ? clone static::$instances[$id] : static::$instances[$id]; - } - } - - return new $collectionClass($results); - } - - /** - * 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) - { - return $this->checkKeys() && $this->traitCheck($includeChildren); - } - - /** - * @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 $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; - } - - /** - * Implementes JsonSerializable interface. - * - * @return array - */ - public function jsonSerialize() - { - 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 - * @param array $keys - */ - protected function doLoad(array $items, array $keys = []) - { - $this->items = array_replace(static::$defaults, $this->items, $this->getKeys($keys), $items); - } -} diff --git a/system/src/Grav/Framework/Object/AbstractObjectCollection.php b/system/src/Grav/Framework/Object/AbstractObjectCollection.php deleted file mode 100644 index ecad694d9..000000000 --- a/system/src/Grav/Framework/Object/AbstractObjectCollection.php +++ /dev/null @@ -1,35 +0,0 @@ -id = $id; - } - - public function getId() - { - return $this->id ?: $this->getParentId(); - } -} diff --git a/system/src/Grav/Framework/Object/Object.php b/system/src/Grav/Framework/Object/Object.php new file mode 100644 index 000000000..90ff4e667 --- /dev/null +++ b/system/src/Grav/Framework/Object/Object.php @@ -0,0 +1,58 @@ +items = $elements; + $this->key = $key !== null ? $key : $this->getKey(); + + if ($this->key === null) { + throw new \InvalidArgumentException('Object cannot be created without assigning a key'); + } + } + + /** + * Implements JsonSerializable interface. + * + * @return array + */ + public function jsonSerialize() + { + return ['key' => $this->getKey(), 'object' => $this->toArray()]; + } +} diff --git a/system/src/Grav/Framework/Object/ObjectCollection.php b/system/src/Grav/Framework/Object/ObjectCollection.php new file mode 100644 index 000000000..764b6ff13 --- /dev/null +++ b/system/src/Grav/Framework/Object/ObjectCollection.php @@ -0,0 +1,50 @@ +key = $key !== null ? $key : $this->getKey(); + + if ($this->key === null) { + throw new \InvalidArgumentException('Object cannot be created without assigning a key'); + } + } + + /** + * Implements JsonSerializable interface. + * + * @return array + */ + public function jsonSerialize() + { + return ['key' => $this->getKey(), 'objects' => $this->toArray()]; + } +} diff --git a/system/src/Grav/Framework/Object/ObjectCollectionInterface.php b/system/src/Grav/Framework/Object/ObjectCollectionInterface.php index ecacee917..2248417be 100644 --- a/system/src/Grav/Framework/Object/ObjectCollectionInterface.php +++ b/system/src/Grav/Framework/Object/ObjectCollectionInterface.php @@ -14,7 +14,7 @@ use Grav\Framework\Collection\CollectionInterface; * ObjectCollection Interface * @package Grav\Framework\Collection */ -interface ObjectCollectionInterface extends CollectionInterface +interface ObjectCollectionInterface extends CollectionInterface, ObjectInterface { /** * Create a copy from this collection by cloning all objects in the collection. @@ -23,6 +23,11 @@ interface ObjectCollectionInterface extends CollectionInterface */ public function copy(); + /** + * @return array + */ + public function getObjectKeys(); + /** * @param string $property Object property to be fetched. * @return array Values of the property. @@ -42,4 +47,12 @@ interface ObjectCollectionInterface extends CollectionInterface * @return array Return values. */ public function call($name, array $arguments); + + /** + * Group items in the collection by a field. + * + * @param string $property + * @return array + */ + public function group($property); } diff --git a/system/src/Grav/Framework/Object/ObjectCollectionTrait.php b/system/src/Grav/Framework/Object/ObjectCollectionTrait.php index bcfa41408..861ce2de1 100644 --- a/system/src/Grav/Framework/Object/ObjectCollectionTrait.php +++ b/system/src/Grav/Framework/Object/ObjectCollectionTrait.php @@ -14,6 +14,8 @@ namespace Grav\Framework\Object; */ trait ObjectCollectionTrait { + use ObjectTrait; + /** * Create a copy from this collection by cloning all objects in the collection. * @@ -26,12 +28,20 @@ trait ObjectCollectionTrait $list[$key] = is_object($value) ? clone $value : $value; } - if (method_exists($this, 'createFrom')) { - return $this->createFrom($list); - } else { - // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). + // TODO: remove when PHP 5.6 is minimum (with doctrine/collections v1.4). + if (!method_exists($this, 'createFrom')) { return new static($list); } + + return $this->createFrom($list); + } + + /** + * @return array + */ + public function getObjectKeys() + { + return $this->call('getKey'); } /** @@ -65,7 +75,7 @@ trait ObjectCollectionTrait * @param array $arguments List of arguments passed to the function. * @return array Return values. */ - public function call($method, array $arguments) + public function call($method, array $arguments = []) { $list = []; diff --git a/system/src/Grav/Framework/Object/ObjectInterface.php b/system/src/Grav/Framework/Object/ObjectInterface.php index b4ed09ef3..26c7a27b7 100644 --- a/system/src/Grav/Framework/Object/ObjectInterface.php +++ b/system/src/Grav/Framework/Object/ObjectInterface.php @@ -15,48 +15,13 @@ namespace Grav\Framework\Object; interface ObjectInterface extends \ArrayAccess, \JsonSerializable { /** - * 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 null|int|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 + * @param array $elements + * @param string $key */ - static public function instance($keys = null, $reload = false); + public function __construct(array $elements = [], $key = null); /** - * @param array $ids List of primary Ids or null to return everything that has been loaded. - * @param bool $readonly - * @return AbstractObjectCollection + * @return string */ - static public function instances(array $ids = null, $readonly = true); - - /** - * Removes all or selected instances from the object cache. - * - * @param null|int|string|array $ids An optional primary key or list of keys. - */ - static public function freeInstances($ids = null); - - /** - * 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(); - - /** - * 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 boolean True if the instance is sane and able to be stored in the storage. - */ - public function check(); + public function getKey(); } diff --git a/system/src/Grav/Framework/Object/ObjectStorageTrait.php b/system/src/Grav/Framework/Object/ObjectStorageTrait.php deleted file mode 100644 index 96ab28f74..000000000 --- a/system/src/Grav/Framework/Object/ObjectStorageTrait.php +++ /dev/null @@ -1,385 +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 (!$id || !isset(static::$instances[$id])) { - $c = get_called_class(); - - /** @var ObjectStorageTrait|ObjectInterface $instance */ - $instance = new $c(); - if (!$instance->load($keys)) { - return $instance; - } - - // Instance exists in storage: make sure that we return the global instance. - $id = $instance->getId(); - $reload = false; - } - - // Return global instance from the identifier. - $instance = static::$instances[$id]; - - 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 = $keys ? $this->getStorageKey($keys) : null; - - } else { - // Internal key was passed. - $key = $keys; - $keys = []; - } - - $this->doLoad($this->getStorage()->load($key), $keys); - $this->initialize(); - - $id = $this->getId(); - 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; - } - - // Get storage. - $storage = $this->getStorage(); - $key = $this->getStorageKey(); - - // Get data to be saved. - $data = $this->prepareSave(); - - // Save the object. - $exists = $storage->exists($key); - $id = $storage->save($key, $data); - - if (!$id) { - throw new \LogicException('No id specified'); - } - - // 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->toArray() as $field => $value) { - if (is_object($value) && method_exists($value, 'check')) { - $result = $result && $value->check(); - } - } - } - - return $result; - } - - // Internal functions - - abstract protected function doLoad(array $items, array $keys = []); - - /** - * @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 = 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 = []); - - /** - * @param array $keys - * @return string - */ - public function getInstanceId(array $keys) - { - return $this->getStorageKey($keys); - } - - /** - * @return string - */ - public function getId() - { - return $this->getStorageKey(); - } - - /** - * @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.'); - } -} diff --git a/system/src/Grav/Framework/Object/ObjectTrait.php b/system/src/Grav/Framework/Object/ObjectTrait.php new file mode 100644 index 000000000..c20a12aee --- /dev/null +++ b/system/src/Grav/Framework/Object/ObjectTrait.php @@ -0,0 +1,64 @@ +items = $elements; + $this->key = $key !== null ? $key : $this->getKey(); + + if ($this->key === null) { + throw new \InvalidArgumentException('Object cannot be created without assigning a key'); + } + } + + /** + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } +} diff --git a/system/src/Grav/Framework/Object/Storage/FilesystemStorage.php b/system/src/Grav/Framework/Object/Storage/FilesystemStorage.php deleted file mode 100644 index 671827acc..000000000 --- a/system/src/Grav/Framework/Object/Storage/FilesystemStorage.php +++ /dev/null @@ -1,145 +0,0 @@ -path = $path; - if ($type) { - $this->type = $type; - } - if ($extension) { - $this->extension = $extension; - } - } - - /** - * @param string $key - * @return bool - */ - public function exists($key) - { - if ($key === null) { - return false; - } - - $file = $this->getFile($key); - - return $file->exists(); - } - - /** - * @param string $key - * @return array - */ - public function load($key) - { - if ($key === null) { - return []; - } - - $file = $this->getFile($key); - $content = (array)$file->content(); - $file->free(); - - return $content; - } - - /** - * @param string $key - * @param array $data - * @return string - */ - public function save($key, array $data) - { - $file = $this->getFile($key); - $file->save($data); - $file->free(); - - return $key; - } - - /** - * @param string $key - * @return bool - */ - public function delete($key) - { - $file = $this->getFile($key); - $result = $file->delete(); - $file->free(); - - return $result; - } - - /** - * @param string[] $list - * @return array - */ - public function loadList(array $list) - { - $results = []; - foreach ($list as $id) { - $results[$id] = $this->load($id); - } - - return $results; - } - - /** - * @param string $key - * @return FileInterface - */ - protected function getFile($key) - { - if ($key === null) { - throw new \RuntimeException('Storage key not defined'); - } - - $filename = "{$this->path}/{$key}{$this->extension}"; - - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - - /** @var FileInterface $type */ - $type = $this->type; - - return $type::instance($locator->findResource($filename, true) ?: $locator->findResource($filename, true, true)); - } -} diff --git a/system/src/Grav/Framework/Object/Storage/StorageInterface.php b/system/src/Grav/Framework/Object/Storage/StorageInterface.php deleted file mode 100644 index e53cbcc8e..000000000 --- a/system/src/Grav/Framework/Object/Storage/StorageInterface.php +++ /dev/null @@ -1,47 +0,0 @@ -