diff --git a/system/src/Grav/Common/Object/AbstractObject.php b/system/src/Grav/Common/Object/AbstractObject.php index 781551b4d..82cee22c8 100644 --- a/system/src/Grav/Common/Object/AbstractObject.php +++ b/system/src/Grav/Common/Object/AbstractObject.php @@ -256,20 +256,31 @@ abstract class AbstractObject implements ObjectInterface * * @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 boolean True on success, false if the object doesn't exist. */ - public function load($keys = null) + public function load($keys = null, $getKey = true) { - if (is_scalar($keys)) { - $keys = ['id' => (string) $keys]; + 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($keys); + $this->items = $storage->load($key); $this->exists = !empty($this->items); // Append the keys and defaults if they were not set by load(). @@ -293,25 +304,38 @@ abstract class AbstractObject implements ObjectInterface * * @return boolean True on success. */ - public function save() + public function save($includeChildren = false) { // Check the object. - if ($this->readonly || !$this->check() || !$this->onBeforeSave()) { + if ($this->readonly || !$this->check($includeChildren) || !$this->onBeforeSave()) { return false; } - // Get storage. - $storage = $this->getStorage(); + try { + // 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); + } catch (\Exception $e) { + return false; + } - // Save the object. - $id = $storage->save($this); if (!$id) { return false; } // If item was created, load the object. if (!$this->exists) { - $this->load($id); + $this->load($id, false); + } + + if ($includeChildren) { + $this->saveChildren(); } $this->onAfterSave(); @@ -322,18 +346,23 @@ abstract class AbstractObject implements ObjectInterface /** * Method to delete the object from the database. * + * @param bool $includeChildren * @return boolean True on success. */ - public function delete() + 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)) { + if (!$storage->delete($this->getStorageKey())) { return false; } @@ -352,9 +381,29 @@ abstract class AbstractObject implements ObjectInterface * * @return boolean True if the instance is sane and able to be stored in the storage. */ - public function check() + public function check($includeChildren = false) { - return !empty($this->id); + $result = true; + + if ($includeChildren) { + foreach ($this->items as $field => $value) { + if (is_object($value) && method_exists($value, 'check')) { + $result = $result && $value->check(); + } + } + } + + return $result && !empty($this->items['id']); + } + + /** + * Implementes JsonSerializable interface. + * + * @return array + */ + public function jsonSerialize() + { + return $this->toArray(); } // Internal functions @@ -371,7 +420,7 @@ abstract class AbstractObject implements ObjectInterface } /** - * @return boolean + * @return bool */ protected function onBeforeSave() { @@ -383,7 +432,7 @@ abstract class AbstractObject implements ObjectInterface } /** - * @return boolean + * @return bool */ protected function onBeforeDelete() { @@ -394,6 +443,43 @@ abstract class AbstractObject implements ObjectInterface { } + protected function saveChildren() + { + foreach ($this->items as $field => $value) { + if (is_object($value) && method_exists($value, 'save')) { + $value->save(true); + } + } + } + + protected function deleteChildren() + { + foreach ($this->items 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 protected function getStorageKey(array $keys = null); + /** * @return StorageInterface */ diff --git a/system/src/Grav/Common/Object/ObjectInterface.php b/system/src/Grav/Common/Object/ObjectInterface.php index 2684f77d4..971ee1f57 100644 --- a/system/src/Grav/Common/Object/ObjectInterface.php +++ b/system/src/Grav/Common/Object/ObjectInterface.php @@ -1,7 +1,7 @@ path = $path; + $this->type = $type; + $this->extension = $extension; + } + + /** + * @param string $key * @return array */ - public function load(array $keys) + public function load($key) { - // TODO - return []; + if ($key === null) { + return []; + } + + $file = $this->getFile($key); + $content = (array)$file->content(); + $file->free(); + + return $content; } /** - * @param AbstractObject $object - * @return string|int Id + * @param string $key + * @param array $data + * @return string */ - public function save(AbstractObject $object) + public function save($key, array $data) { - // TODO - return 'xxx'; + $file = $this->getFile($key); + $file->save($data); + $file->free(); + + return $key; } /** - * @param AbstractObject $object + * @param string $key * @return bool */ - public function delete(AbstractObject $object) + public function delete($key) { - // TODO - return false; + $file = $this->getFile($key); + $result = $file->delete(); + $file->free(); + + return $result; } /** - * @param array|int[]|string[] $list + * @param string[] $list * @return array */ public function loadList(array $list) { - // TODO - return []; + $results = []; + foreach ($list as $id) { + $results[$id] = $this->load(['id' => $id]); + } + + return $results; } /** @@ -57,11 +104,32 @@ class FilesystemStorage implements StorageInterface /** * @param array $query - * @return array|int[]|string[] + * @return string[] */ - public function find(array $query) + public function find(array $query, $start = 0, $limit = 0) { // TODO return []; } + + /** + * @param string $key + * @return FileInterface + */ + protected function getFile($key) + { + if ($key === null) { + throw new \RuntimeException('Id 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/Common/Object/Storage/StorageInterface.php b/system/src/Grav/Common/Object/Storage/StorageInterface.php index f1889f8f7..c1f72124e 100644 --- a/system/src/Grav/Common/Object/Storage/StorageInterface.php +++ b/system/src/Grav/Common/Object/Storage/StorageInterface.php @@ -1,30 +1,29 @@