mirror of
https://github.com/getgrav/grav.git
synced 2025-10-26 07:56:07 +01:00
Add new collection and object classes
This commit is contained in:
59
system/src/Grav/Common/Collection/AbstractCollection.php
Normal file
59
system/src/Grav/Common/Collection/AbstractCollection.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
|
||||
class AbstractCollection implements CollectionInterface
|
||||
{
|
||||
use ArrayAccess, Countable;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $items = [];
|
||||
|
||||
/**
|
||||
* @param array $variables
|
||||
* @return static
|
||||
*/
|
||||
public static function __set_state(array $variables)
|
||||
{
|
||||
$instance = new static();
|
||||
$instance->items = $variables['items'];
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item to the list.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @param string $key
|
||||
* @return $this
|
||||
*/
|
||||
public function add($item, $key = null)
|
||||
{
|
||||
$this->offsetSet($key, $item);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param $key
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
$this->offsetUnset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
use Grav\Common\Collection\CollectionInterface;
|
||||
|
||||
interface ObjectCollectionInterface extends CollectionInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Create a clone from this collection.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function getClone();
|
||||
}
|
||||
29
system/src/Grav/Common/Collection/CloneCollectionTrait.php
Normal file
29
system/src/Grav/Common/Collection/CloneCollectionTrait.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
|
||||
trait CloneCollectionTrait
|
||||
{
|
||||
use ArrayAccess;
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
$this->offsetSet($key, clone $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a clone from this collection.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function getClone()
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
}
|
||||
21
system/src/Grav/Common/Collection/CollectionInterface.php
Normal file
21
system/src/Grav/Common/Collection/CollectionInterface.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
interface CollectionInterface extends \IteratorAggregate, \ArrayAccess, \Countable
|
||||
{
|
||||
/**
|
||||
* Add item to the list.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @param string $key
|
||||
* @return $this
|
||||
*/
|
||||
public function add($item, $key = null);
|
||||
|
||||
/**
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param $key
|
||||
*/
|
||||
public function remove($key);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
interface ObjectCollectionInterface extends CollectionInterface
|
||||
{
|
||||
/**
|
||||
* @param string $property Object property to be fetched.
|
||||
* @return array Values of the property.
|
||||
*/
|
||||
public function get($property);
|
||||
|
||||
/**
|
||||
* @param string $property Object property to be updated.
|
||||
* @param string $value New value.
|
||||
* @return $this
|
||||
*/
|
||||
public function set($property, $value);
|
||||
|
||||
/**
|
||||
* @param string $name Method name.
|
||||
* @param array $arguments List of arguments passed to the function.
|
||||
* @return array Return values.
|
||||
*/
|
||||
public function __call($name, array $arguments);
|
||||
}
|
||||
127
system/src/Grav/Common/Collection/ObjectCollectionTrait.php
Normal file
127
system/src/Grav/Common/Collection/ObjectCollectionTrait.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
|
||||
trait ObjectCollectionTrait
|
||||
{
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $keyProperty = null;
|
||||
|
||||
/**
|
||||
* Add item to the list.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $key
|
||||
* @return $this
|
||||
*/
|
||||
public function add($object, $key = null)
|
||||
{
|
||||
$objKey = $key ?: $this->getObjectKey($object);
|
||||
$this->offsetSet(is_null($objKey) ? $key : $objKey, $object);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param string|object $key
|
||||
* @return $this
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
if (is_object($key)) {
|
||||
$key = $this->getObjectKey($key);
|
||||
if (is_null($key)) {
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
$this->offsetUnset($key);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $property Object property to be fetched.
|
||||
* @return array Values of the property.
|
||||
*/
|
||||
public function get($property)
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($this as $id => $object) {
|
||||
$key = $this->getObjectKey($object);
|
||||
$list[is_null($key) ? $id : $key] = $this->getObjectValue($object, $property);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $property Object property to be updated.
|
||||
* @param string $value New value.
|
||||
* @return $this
|
||||
*/
|
||||
public function set($property, $value)
|
||||
{
|
||||
foreach ($this as $object) {
|
||||
$object->{$property} = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Method name.
|
||||
* @param array $arguments List of arguments passed to the function.
|
||||
* @return array Return values.
|
||||
*/
|
||||
public function __call($name, array $arguments)
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach ($this as $id => $object) {
|
||||
$key = $this->getObjectKey($object);
|
||||
$list[is_null($key) ? $id : $key] = $this->getObjectCallResult($object, $name, $arguments);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getObjectKey($object)
|
||||
{
|
||||
$keyProperty = $this->keyProperty;
|
||||
|
||||
return $keyProperty && isset($object->{$keyProperty}) ? (string) $object->{$keyProperty} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getObjectValue($object, $property)
|
||||
{
|
||||
return isset($object->{$property}) ? $object->{$property} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @param string $name;
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getObjectCallResult($object, $name, $arguments)
|
||||
{
|
||||
return method_exists($object, $name) ? call_user_func_array([$object, $name], $arguments) : null;
|
||||
}
|
||||
}
|
||||
84
system/src/Grav/Common/Collection/SortingCollectionTrait.php
Normal file
84
system/src/Grav/Common/Collection/SortingCollectionTrait.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace Grav\Common\Collection;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
|
||||
trait SortingCollectionTrait
|
||||
{
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* Reverse the order of the items.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$this->items = array_reverse($this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle items.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function shuffle()
|
||||
{
|
||||
$keys = array_keys($this->items);
|
||||
shuffle($keys);
|
||||
|
||||
$this->items = array_replace(array_flip($keys), $this->items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort collection by values using a user-defined comparison function.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function sort(callable $callback)
|
||||
{
|
||||
uasort($this->items, $callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sort collection by keys.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function ksort($sort_flags = SORT_REGULAR)
|
||||
{
|
||||
ksort($this->items, $sort_flags);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort collection by keys in reverse order.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function krsort($sort_flags = SORT_REGULAR)
|
||||
{
|
||||
krsort($this->items, $sort_flags);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort collection by keys using a user-defined comparison function.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function uksort(callable $key_compare_func)
|
||||
{
|
||||
uksort($this->items, $key_compare_func);
|
||||
|
||||
return $this;
|
||||
}}
|
||||
@@ -332,7 +332,8 @@ abstract class Folder
|
||||
*/
|
||||
public static function move($source, $target)
|
||||
{
|
||||
if (!is_dir($source)) {
|
||||
if (!file_exists($target) || !is_dir($source)) {
|
||||
// Rename fails if source folder does not exist.
|
||||
throw new \RuntimeException('Cannot move non-existing folder.');
|
||||
}
|
||||
|
||||
@@ -341,11 +342,24 @@ abstract class Folder
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_exists($target)) {
|
||||
// Rename fails if target folder exists.
|
||||
throw new \RuntimeException('Cannot move files to existing folder/file.');
|
||||
}
|
||||
|
||||
// Make sure that path to the target exists before moving.
|
||||
self::create(dirname($target));
|
||||
|
||||
$success = @rename($source, $target);
|
||||
if (!$success) {
|
||||
// Silence warnings (chmod failed etc).
|
||||
@rename($source, $target);
|
||||
|
||||
// Rename function can fail while still succeeding, so let's check if the folder exists.
|
||||
if (!file_exists($target) || !is_dir($target)) {
|
||||
// In some rare cases rename() creates file, not a folder. Get rid of it.
|
||||
if (file_exists($target)) {
|
||||
@unlink($target);
|
||||
}
|
||||
// Rename doesn't support moving folders across filesystems. Use copy instead.
|
||||
self::copy($source, $target);
|
||||
self::delete($source);
|
||||
}
|
||||
@@ -353,6 +367,7 @@ abstract class Folder
|
||||
// Make sure that the change will be detected when caching.
|
||||
@touch(dirname($source));
|
||||
@touch(dirname($target));
|
||||
@touch($target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
374
system/src/Grav/Common/Object/AbstractObject.php
Normal file
374
system/src/Grav/Common/Object/AbstractObject.php
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
use Grav\Common\Object\Storage\StorageInterface;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
|
||||
/**
|
||||
* Abstract base class for stored objects.
|
||||
*
|
||||
* @property string $id
|
||||
*/
|
||||
abstract class AbstractObject implements ObjectInterface
|
||||
{
|
||||
use ArrayAccessWithGetters, Export;
|
||||
|
||||
/**
|
||||
* If you don't have global instance ids, override this in extending class.
|
||||
* @var array
|
||||
*/
|
||||
static protected $instances = [];
|
||||
|
||||
/**
|
||||
* If you don't have global storage, override this in extending class.
|
||||
* @var StorageInterface
|
||||
*/
|
||||
static protected $storage;
|
||||
|
||||
/**
|
||||
* Default properties for the object.
|
||||
* @var array
|
||||
*/
|
||||
static protected $defaults = ['id' => null];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
static protected $collectionClass = 'Grav\\Common\\Object\\ObjectCollection';
|
||||
|
||||
/**
|
||||
* Properties of the object.
|
||||
* @var array
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* Does object exist in storage?
|
||||
* @var boolean
|
||||
*/
|
||||
protected $exists = false;
|
||||
|
||||
/**
|
||||
* Readonly object.
|
||||
* @var bool
|
||||
*/
|
||||
protected $readonly = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $initialized = false;
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
$instance = new $c($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
|
||||
*/
|
||||
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->set($keys);
|
||||
$instance->exists(true);
|
||||
$instance->initialize();
|
||||
$id = $instance->id;
|
||||
if ($id && !isset(static::$instances[$id])) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor, overridden in descendant classes.
|
||||
*
|
||||
* @param string|array $identifier Identifier.
|
||||
*/
|
||||
public function __construct($identifier = null)
|
||||
{
|
||||
if ($identifier) {
|
||||
$this->load($identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return boolean True on success, false if the object doesn't exist.
|
||||
*/
|
||||
public function load($keys = null)
|
||||
{
|
||||
if (is_scalar($keys)) {
|
||||
$keys = ['id' => (string) $keys];
|
||||
}
|
||||
|
||||
// Get storage.
|
||||
$storage = $this->getStorage();
|
||||
|
||||
// Load the object based on the keys.
|
||||
$this->items = $storage->load($keys);
|
||||
$this->exists = !empty($this->items);
|
||||
|
||||
// Append the keys and defaults if they were not set by load().
|
||||
$this->items += $keys + static::$defaults;
|
||||
|
||||
$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.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
// Check the object.
|
||||
if ($this->readonly || !$this->check() || !$this->onBeforeSave()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get storage.
|
||||
$storage = $this->getStorage();
|
||||
|
||||
// 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->onAfterSave();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to delete the object from the database.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if ($this->readonly || !$this->exists || !$this->onBeforeDelete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get storage.
|
||||
$storage = $this->getStorage();
|
||||
|
||||
if (!$storage->delete($this)) {
|
||||
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.
|
||||
*
|
||||
* 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()
|
||||
{
|
||||
return !empty($this->id);
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @return $this
|
||||
*/
|
||||
protected function set(array $items)
|
||||
{
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
protected function onBeforeSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function onAfterSave()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
protected function onBeforeDelete()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function onAfterDelete()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StorageInterface
|
||||
*/
|
||||
static protected function getStorage()
|
||||
{
|
||||
return static::$storage;
|
||||
}
|
||||
}
|
||||
279
system/src/Grav/Common/Object/AbstractObjectFinder.php
Normal file
279
system/src/Grav/Common/Object/AbstractObjectFinder.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
use Grav\Common\Object\Storage\StorageInterface;
|
||||
|
||||
abstract class AbstractObjectFinder implements ObjectFinderInterface
|
||||
{
|
||||
/**
|
||||
* Storage associated with the model.
|
||||
*
|
||||
* @var StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $start = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $limit = 0;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $skip = false;
|
||||
|
||||
/**
|
||||
* List of query rules.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $query = [];
|
||||
|
||||
/**
|
||||
* Finder constructor.
|
||||
*
|
||||
* @param array $options Query options.
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (!$this->storage) {
|
||||
throw new \DomainException('Storage missing from ' . get_class($this));
|
||||
}
|
||||
|
||||
if ($options) {
|
||||
$this->parse($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse query options.
|
||||
*
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function parse(array $options)
|
||||
{
|
||||
foreach ($options as $func => $params) {
|
||||
if (method_exists($this, $func)) {
|
||||
call_user_func_array([$this, $func], (array) $params);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set start position for the query.
|
||||
*
|
||||
* @param int $start
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function start($start = null)
|
||||
{
|
||||
if (!is_null($start)) {
|
||||
if ((string)(int)$start === (string)$start && $start>=0) {
|
||||
throw new \RuntimeException('$start needs to be zero or a positive integer');
|
||||
}
|
||||
|
||||
$this->start = (int)$start;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set limit to the query.
|
||||
*
|
||||
* If this function isn't used, RokClub will use threads per page configuration setting.
|
||||
*
|
||||
* @param int $limit
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function limit($limit = null)
|
||||
{
|
||||
if (!is_null($limit))
|
||||
{
|
||||
if ((string)(int)$limit === (string)$limit && $limit>=0) {
|
||||
throw new \RuntimeException('$limit needs to be zero or a positive integer');
|
||||
}
|
||||
|
||||
$this->limit = (int)$limit;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set order by field and direction.
|
||||
*
|
||||
* This function can be used more than once to chain order by.
|
||||
*
|
||||
* @param string $field
|
||||
* @param int $direction
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order($field, $direction = 1)
|
||||
{
|
||||
if (!is_string($field)) {
|
||||
throw new \RuntimeException('$field needs to be a string');
|
||||
}
|
||||
if ((string)(int)$direction !== (string)$direction || $direction <= -1 || $direction >= 1) {
|
||||
throw new \RuntimeException('$direction needs to be 1 (ascending), 0 (undefined) or -1 (descending)');
|
||||
}
|
||||
|
||||
if ($direction) {
|
||||
$this->query[] = ['order', $field, (int)$direction];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by field.
|
||||
*
|
||||
* @param string $field Field name.
|
||||
* @param string $operation Operation (>|>=|<|<=|==|!=|BETWEEN|NOT BETWEEN|IN|NOT IN)
|
||||
* @param mixed $value Value.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where($field, $operation, $value = null)
|
||||
{
|
||||
if (!is_string($field)) {
|
||||
throw new \RuntimeException('$field needs to be a string');
|
||||
}
|
||||
|
||||
$operation = strtoupper((string)$operation);
|
||||
|
||||
switch ($operation)
|
||||
{
|
||||
case '>':
|
||||
case '>=':
|
||||
case '<':
|
||||
case '<=':
|
||||
$this->checkOperator($value);
|
||||
$this->query[] = ['where', $field, $operation, $value];
|
||||
break;
|
||||
case '==':
|
||||
case '!=':
|
||||
$this->checkEqOperator($value);
|
||||
$this->query[] = ['where', $field, $operation, $value];
|
||||
break;
|
||||
case 'BETWEEN':
|
||||
case 'NOT BETWEEN':
|
||||
$this->checkBetweenOperator($value);
|
||||
$this->query[] = ['where', $field, $operation, array_values($value)];
|
||||
break;
|
||||
case 'IN':
|
||||
if (is_null($value) || (is_array($value) && empty($value))) {
|
||||
// IN (nothing): optimized to empty collection.
|
||||
$this->skip = true;
|
||||
} else {
|
||||
$this->checkInOperator($value);
|
||||
$this->query[] = ['where', $field, $operation, array_values((array)$value)];
|
||||
}
|
||||
break;
|
||||
case 'NOT IN':
|
||||
if (is_null($value) || (is_array($value) && empty($value))) {
|
||||
// NOT IN (nothing): optimized away.
|
||||
} else {
|
||||
$this->checkInOperator($value);
|
||||
$this->query[] = ['where', $field, $operation, array_values((array)$value)];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException('$operation needs to be one of: > , >= , < , <= , == , != , BETWEEN , NOT BETWEEN , IN , NOT IN');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
* Derived classes should generally override this function to return correct objects.
|
||||
*
|
||||
* @param int $start
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function find($start = null, $limit = null)
|
||||
{
|
||||
if ($this->skip)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$query = $this->query;
|
||||
$this->start($start)->limit($limit);
|
||||
$this->prepare();
|
||||
|
||||
$results = (array) $this->storage->find($this->query);
|
||||
|
||||
$this->query = $query;
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count items.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
$query = $this->query;
|
||||
$this->prepare();
|
||||
|
||||
$count = (int) $this->storage->count($this->query);
|
||||
|
||||
$this->query = $query;
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to include common rules.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
}
|
||||
|
||||
protected function checkOperator($value)
|
||||
{
|
||||
if (!is_scalar($value)) {
|
||||
throw new \RuntimeException('$value needs to be a scalar');
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkEqOperator($value)
|
||||
{
|
||||
if (!is_null($value) || !is_scalar($value)) {
|
||||
throw new \RuntimeException('$value needs to be a scalar or null');
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkBetweenOperator($value)
|
||||
{
|
||||
if (!is_array($value) || count($value) !== 2) {
|
||||
throw new \RuntimeException('$value needs to be a list with two values in it');
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkInOperator($value)
|
||||
{
|
||||
if (!is_scalar($value) || !is_array($value)) {
|
||||
throw new \RuntimeException('$value needs to be a single value or list of values');
|
||||
}
|
||||
}
|
||||
}
|
||||
20
system/src/Grav/Common/Object/ObjectCollection.php
Normal file
20
system/src/Grav/Common/Object/ObjectCollection.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
use Grav\Common\Collection\AbstractCollection;
|
||||
use Grav\Common\Collection\ObjectCollectionInterface;
|
||||
use Grav\Common\Collection\ObjectCollectionTrait;
|
||||
|
||||
class ObjectCollection extends AbstractCollection implements ObjectCollectionInterface
|
||||
{
|
||||
use ObjectCollectionTrait;
|
||||
|
||||
/**
|
||||
* Collection constructor.
|
||||
* @param array|AbstractObject[] $items
|
||||
*/
|
||||
public function __construct(array $items)
|
||||
{
|
||||
$this->items = $items;
|
||||
}
|
||||
}
|
||||
80
system/src/Grav/Common/Object/ObjectFinderInterface.php
Normal file
80
system/src/Grav/Common/Object/ObjectFinderInterface.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
interface ObjectFinderInterface
|
||||
{
|
||||
/**
|
||||
* Finder constructor.
|
||||
*
|
||||
* @param array $options Query options.
|
||||
*/
|
||||
public function __construct(array $options = []);
|
||||
|
||||
/**
|
||||
* Parse query options.
|
||||
*
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function parse(array $options);
|
||||
|
||||
/**
|
||||
* Set start position for the query.
|
||||
*
|
||||
* @param int $start
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function start($start = null);
|
||||
|
||||
/**
|
||||
* Set limit to the query.
|
||||
*
|
||||
* If this function isn't used, RokClub will use threads per page configuration setting.
|
||||
*
|
||||
* @param int $limit
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function limit($limit = null);
|
||||
|
||||
/**
|
||||
* Set order by field and direction.
|
||||
*
|
||||
* This function can be used more than once to chain order by.
|
||||
*
|
||||
* @param string $field
|
||||
* @param int $direction
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order($field, $direction = 1);
|
||||
/**
|
||||
* Filter by field.
|
||||
*
|
||||
* @param string $field Field name.
|
||||
* @param string $operation Operation (>|>=|<|<=|==|!=|BETWEEN|NOT BETWEEN|IN|NOT IN)
|
||||
* @param mixed $value Value.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where($field, $operation, $value = null);
|
||||
|
||||
/**
|
||||
* Get items.
|
||||
*
|
||||
* Derived classes should generally override this function to return correct objects.
|
||||
*
|
||||
* @param int $start
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function find($start = null, $limit = null);
|
||||
|
||||
/**
|
||||
* Count items.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count();
|
||||
}
|
||||
100
system/src/Grav/Common/Object/ObjectInterface.php
Normal file
100
system/src/Grav/Common/Object/ObjectInterface.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object;
|
||||
|
||||
interface ObjectInterface extends \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* 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 int|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);
|
||||
|
||||
/**
|
||||
* @param array $ids List of primary Ids or null to return everything that has been loaded.
|
||||
* @param bool $readonly
|
||||
* @return ObjectCollection
|
||||
*/
|
||||
static public function instances(array $ids = null, $readonly = true);
|
||||
|
||||
/**
|
||||
* Removes all or selected instances from the object cache.
|
||||
*
|
||||
* @param null|int|array $ids An optional primary key or list of keys.
|
||||
*/
|
||||
static public function freeInstances($ids = null);
|
||||
|
||||
/**
|
||||
* Class constructor, overridden in descendant classes.
|
||||
*
|
||||
* @param string|array $identifier Identifier.
|
||||
*/
|
||||
public function __construct($identifier = 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();
|
||||
|
||||
/**
|
||||
* Convert instance to a read only object.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function readonly();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return boolean True on success, false if the object doesn't exist.
|
||||
*/
|
||||
public function load($keys = null);
|
||||
|
||||
/**
|
||||
* Method to save the object to the storage.
|
||||
*
|
||||
* Before saving the object, this method checks if object can be safely saved.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*/
|
||||
public function save();
|
||||
|
||||
/**
|
||||
* Method to delete the object from the database.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
67
system/src/Grav/Common/Object/Storage/FilesystemStorage.php
Normal file
67
system/src/Grav/Common/Object/Storage/FilesystemStorage.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object\Storage;
|
||||
|
||||
use Grav\Common\Object\AbstractObject;
|
||||
|
||||
class FilesystemStorage implements StorageInterface
|
||||
{
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return array
|
||||
*/
|
||||
public function load(array $keys)
|
||||
{
|
||||
// TODO
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractObject $object
|
||||
* @return string|int Id
|
||||
*/
|
||||
public function save(AbstractObject $object)
|
||||
{
|
||||
// TODO
|
||||
return 'xxx';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractObject $object
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(AbstractObject $object)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|int[]|string[] $list
|
||||
* @return array
|
||||
*/
|
||||
public function loadList(array $list)
|
||||
{
|
||||
// TODO
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @return int
|
||||
*/
|
||||
public function count(array $query)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @return array|int[]|string[]
|
||||
*/
|
||||
public function find(array $query)
|
||||
{
|
||||
// TODO
|
||||
return [];
|
||||
}
|
||||
}
|
||||
43
system/src/Grav/Common/Object/Storage/StorageInterface.php
Normal file
43
system/src/Grav/Common/Object/Storage/StorageInterface.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
namespace Grav\Common\Object\Storage;
|
||||
|
||||
use Grav\Common\Object\AbstractObject;
|
||||
|
||||
interface StorageInterface
|
||||
{
|
||||
/**
|
||||
* @param array $keys
|
||||
* @return array
|
||||
*/
|
||||
public function load(array $keys);
|
||||
|
||||
/**
|
||||
* @param AbstractObject $object
|
||||
* @return string|int Id
|
||||
*/
|
||||
public function save(AbstractObject $object);
|
||||
|
||||
/**
|
||||
* @param AbstractObject $object
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(AbstractObject $object);
|
||||
|
||||
/**
|
||||
* @param array|int[]|string[] $list
|
||||
* @return array
|
||||
*/
|
||||
public function loadList(array $list);
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @return int
|
||||
*/
|
||||
public function count(array $query);
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @return array|int[]|string[]
|
||||
*/
|
||||
public function find(array $query);
|
||||
}
|
||||
Reference in New Issue
Block a user