878 lines
31 KiB
PHP
878 lines
31 KiB
PHP
<?php
|
|
namespace DynCom\mysyde\common\traits;
|
|
use DynCom\mysyde\common\interfaces\CriteriaHelperInterface;
|
|
use DynCom\mysyde\common\interfaces\GenericCollectionInterface;
|
|
use DynCom\mysyde\common\interfaces\ModelDBConfigInterface;
|
|
|
|
/**
|
|
* Created by PhpStorm.
|
|
* User: Micha
|
|
* Date: 14.01.2015
|
|
* Time: 17:38
|
|
* @TODO: Rewrite from getByCriteria to end for SplObjectStorage
|
|
*/
|
|
trait genericCollectionTrait
|
|
{
|
|
|
|
protected $allowedComparisonOperations = array('IS NULL', 'IS NOT NULL', '=', '!=', '<', '>', '>=', '<=', 'IN', 'LIKE');
|
|
protected $criteriaValidationService;
|
|
/**
|
|
* @var ModelDBConfigInterface
|
|
*/
|
|
protected $config;
|
|
protected $elements;
|
|
protected $hashObjectMap = [];
|
|
protected $hashesByID = [];
|
|
protected $hashesByAltPrimary = [];
|
|
protected $hashList = [];
|
|
protected $fields;
|
|
protected $altPrimary;
|
|
protected $entryClassName;
|
|
protected $currIndex = '';
|
|
protected $currIndexPos = null;
|
|
|
|
/**
|
|
* @param ModelDBConfigInterface $config
|
|
* @param CriteriaHelperInterface $criteriaValidationService
|
|
*/
|
|
public function __construct(ModelDBConfigInterface $config, CriteriaHelperInterface $criteriaValidationService)
|
|
{
|
|
$this->elements = new \SplObjectStorage();
|
|
$this->config = $config;
|
|
$this->criteriaValidationService = $criteriaValidationService;
|
|
$this->entryClassName = $config->getModelClassName();
|
|
$this->altPrimary = $config->getAltPrimary();
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getBaseTableName()
|
|
{
|
|
return $this->config->getBaseTableName();
|
|
}
|
|
|
|
/**
|
|
* @return array Array of fields (optionally with DB-Type) of the class for entries in the collection
|
|
*/
|
|
public function getFieldsArray()
|
|
{
|
|
return $this->config->getMappedFields();
|
|
}
|
|
|
|
/**
|
|
* @return int Implements Countable interface
|
|
*/
|
|
public function count()
|
|
{
|
|
//return count($this->elements);
|
|
return count($this->hashObjectMap);
|
|
}
|
|
|
|
/**
|
|
* @return \IteratorIterator
|
|
*/
|
|
public function getIterator()
|
|
{
|
|
return new \IteratorIterator(new \ArrayIterator($this->hashObjectMap));
|
|
|
|
}
|
|
|
|
/**
|
|
* @param mixed $instance An instance of the collection-entry class
|
|
* @param bool $idCheck Flag whether comparisons should be id-only, default FALSE
|
|
*
|
|
* @return bool Success or failure
|
|
*/
|
|
public function remove($instance, $idCheck = false)
|
|
{
|
|
return $this->_remove($instance, $idCheck);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $instance An instance of the collection-entry class
|
|
* @param bool $idCheck Flag whether comparisons should be id-only, default FALSE
|
|
*
|
|
* @return bool Success or failure
|
|
*/
|
|
protected function _remove($instance, $idCheck = false)
|
|
{
|
|
$info = $this->getIndexInfo($instance);
|
|
if (
|
|
($idCheck && (empty($info['id']) || !array_key_exists((string)$info['id'], $this->hashesByID)))
|
|
|| (!array_key_exists($info['hash'], $this->hashObjectMap))
|
|
) {
|
|
return false;
|
|
}
|
|
$this->removeInstance($instance);
|
|
return true;
|
|
|
|
if (!$idCheck && $this->elements->contains($instance)) {
|
|
$this->elements->detach($instance);
|
|
return true;
|
|
}
|
|
$c = $this->elements;
|
|
$c->rewind();
|
|
$found = false;
|
|
while ($c->valid() && !$found) {
|
|
$object = $c->current();
|
|
$currID = $object->id;
|
|
if ($instance->id === $currID) {
|
|
$found = true;
|
|
$foundEntity = $object;
|
|
}
|
|
$c->next();
|
|
}
|
|
$c->rewind();
|
|
if ($found) {
|
|
$this->elements->detach($foundEntity);
|
|
return true;
|
|
}
|
|
$c->rewind();
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return array The entire collection as array
|
|
*/
|
|
public function getAllAsArray()
|
|
{
|
|
return array_values($this->hashObjectMap);
|
|
$newArray = array();
|
|
$this->elements->rewind();
|
|
while ($this->elements->valid()) {
|
|
$newArray[] = $this->elements->current();
|
|
$this->elements->next();
|
|
}
|
|
$this->elements->rewind();
|
|
return $newArray;
|
|
}
|
|
|
|
/**
|
|
* @param array $array Array containing instances of the class for entries in the collection
|
|
*
|
|
* @param bool $idCheck
|
|
* @return bool Success or failure
|
|
*/
|
|
protected function _resetEntriesFromArray(array $array, $idCheck = false)
|
|
{
|
|
$this->hashesByID = [];
|
|
$this->hashesByAltPrimary = [];
|
|
$this->hashObjectMap = [];
|
|
$className = $this->getEntryClassName();
|
|
//$newCollection = new SplObjectStorage();
|
|
foreach ($array as $instance) {
|
|
if ($instance instanceof $className) {
|
|
//$newCollection->attach($instance);
|
|
$this->add($instance, $idCheck);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
//$this->elements = $newCollection;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getEntryClassName()
|
|
{
|
|
return $this->entryClassName;
|
|
}
|
|
|
|
/**
|
|
* @param int $id
|
|
*
|
|
* @return mixed An instance of the collection-entry class (or Null-Object)
|
|
*/
|
|
public function getByID($id)
|
|
{
|
|
return $this->_getByID($id);
|
|
}
|
|
|
|
/**
|
|
* @param int $id
|
|
*
|
|
* @return mixed An instance of the collection-entry class
|
|
*/
|
|
protected function _getByID($id)
|
|
{
|
|
$obj = $this->getNullObject();
|
|
if (!$this->hasInstanceForID($id)) {
|
|
return $obj;
|
|
}
|
|
return $this->getInstanceByID($id);
|
|
foreach ($this->elements as $instance) {
|
|
if (isset($instance->id) && ($instance->id == (int)$id)) {
|
|
return $instance;
|
|
}
|
|
}
|
|
return $this->getNullObject();
|
|
}
|
|
|
|
/**
|
|
* @return mixed Empty (unset) instance of the class for entries in the collection
|
|
* !MUST! be overriden in classes with different constructor
|
|
*/
|
|
public function getNullObject()
|
|
{
|
|
return new $this->entryClassName($this->config);
|
|
}
|
|
|
|
/**
|
|
* @param array $uniqueKeyArray An array specifying an alternative primary key, of the form:
|
|
* array('keyField1Name'=>'keyField1Value','keyField2Name'=>'keyField2Value'...);
|
|
*
|
|
* @return mixed An instance of the collection-entry class
|
|
*/
|
|
public function getByAltPrimary(array $uniqueKeyArray)
|
|
{
|
|
return $this->getInstanceByAltPrimary($uniqueKeyArray);
|
|
return $this->_getByAltPrimary($uniqueKeyArray);
|
|
}
|
|
|
|
/**
|
|
* @param array $uniqueKeyArray
|
|
* @return mixed
|
|
* @throws \DomainException
|
|
*/
|
|
protected function _getByAltPrimary(array $uniqueKeyArray)
|
|
{
|
|
$altPrimaryValues = array();
|
|
foreach ($this->altPrimary as $name) {
|
|
if (isset($uniqueKeyArray[$name])) {
|
|
$altPrimaryValues[$name] = $uniqueKeyArray[$name];
|
|
} else {
|
|
throw new \DomainException('Paramter enthält keinen Wert für Schlüsselfeld \'' . $name . '\'');
|
|
return $this->getNullObject();
|
|
}
|
|
}
|
|
|
|
$all = $this->getAllAsArray();
|
|
foreach ($all as $instance) {
|
|
$instanceAltPrimary = array();
|
|
foreach ($this->altPrimary as $key => $value) {
|
|
$instanceAltPrimary[$key] = $instance->$key;
|
|
}
|
|
if ($instanceAltPrimary == $uniqueKeyArray) {
|
|
return $instance;
|
|
}
|
|
}
|
|
return $this->getNullObject();
|
|
}
|
|
|
|
/**
|
|
* @param array $criteria Filtering-criteria as array in disjunctive normal form:
|
|
* array(
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue')
|
|
* ...
|
|
* ),
|
|
* **OR**
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue')
|
|
* ...
|
|
* )
|
|
* ...
|
|
* )
|
|
*
|
|
* @return GenericCollectionInterface $collection Subset of the current collection as instance of own concrete
|
|
* collection class
|
|
*/
|
|
public function getByCriteria(array $criteria)
|
|
{
|
|
return $this->_getByCriteria($criteria);
|
|
}
|
|
|
|
/**
|
|
* @param array $criteria Filtering-criteria as array in disjunctive normal form:
|
|
* array(
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue')
|
|
* ...
|
|
* ),
|
|
* **OR**
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue')
|
|
* ...
|
|
* )
|
|
* ...
|
|
* )
|
|
*
|
|
* @return GenericCollectionInterface $collection Subset of the current collection as instance of own concrete
|
|
* collection class
|
|
*/
|
|
protected function _getByCriteria(array $criteria)
|
|
{
|
|
$resultCollection = new static($this->config, $this->criteriaValidationService);
|
|
|
|
if (!$this->criteriaValidationService->validateCriteria($this->config, $criteria)) {
|
|
return $resultCollection;
|
|
}
|
|
|
|
//foreach ($this->elements as $instance) {
|
|
foreach ($this->hashObjectMap as $instance) {
|
|
foreach ($criteria as $disjunctCriteria) {
|
|
$isValidDisjunct = true;
|
|
foreach ($disjunctCriteria as $conjunctCriterion) {
|
|
$fieldName = $conjunctCriterion[0];
|
|
$comparator = $conjunctCriterion[1];
|
|
$compareValue = isset($conjunctCriterion[2]) ? $conjunctCriterion[2] : '';
|
|
|
|
if ($comparator === 'IS NULL') {
|
|
if (!(is_null($instance->$fieldName))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === 'IS NOT NULL') {
|
|
if (is_null($instance->$fieldName)) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '=') {
|
|
if (!(isset($instance->$fieldName) && $instance->$fieldName == $compareValue)) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '!=') {
|
|
if (!($instance->$fieldName != $compareValue)) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '>') {
|
|
if (!(isset($instance->$fieldName) && ($instance->$fieldName > $compareValue))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '<') {
|
|
if (!(isset($instance->$fieldName) && ($instance->$fieldName < $compareValue))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '<=') {
|
|
if (!(isset($instance->$fieldName) && !($instance->$fieldName <= $compareValue))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === '>=') {
|
|
if (!(isset($instance->$fieldName) || ($instance->$fieldName >= $compareValue))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === 'IN') {
|
|
if (!(in_array($instance->$fieldName, $compareValue))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
} elseif ($comparator === 'LIKE') {
|
|
//SQL-Wildcard durch fnmatch-Wildcard ersetzen
|
|
$compareValue = str_replace('%', '*', $compareValue);
|
|
if (!(fnmatch($compareValue, $instance->$fieldName))) {
|
|
$isValidDisjunct = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ($isValidDisjunct) {
|
|
$resultCollection->add($instance, true);
|
|
}
|
|
}
|
|
}
|
|
return $resultCollection;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $instance An instance of the collection-entry class
|
|
* @param bool $idCheck Flag whether comparisons should be id-only, default FALSE
|
|
*
|
|
* @return bool Success or failure
|
|
*/
|
|
public function add($instance, $idCheck = false)
|
|
{
|
|
return $this->_add($instance, $idCheck);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $instance An instance of the collection-entry class
|
|
* @param bool $idCheck Flag whether comparisons should be id-only, default FALSE
|
|
*
|
|
* @return bool Success or failure
|
|
* @throws \DomainException
|
|
*/
|
|
protected function _add($instance, $idCheck = false)
|
|
{
|
|
$info = $this->getIndexInfo($instance);
|
|
|
|
if ((!$idCheck && array_key_exists($info['hash'],$this->hashObjectMap)) || $this->hasInstanceForID($info['id'])) {
|
|
$this->updateByID($instance);
|
|
} else {
|
|
$this->addNewInstance($instance);
|
|
}
|
|
return true;
|
|
|
|
|
|
|
|
$className = $this->getEntryClassName();
|
|
$id = null;
|
|
$hash = $this->elements->getHash($instance);
|
|
if (!($instance instanceof $className)) {
|
|
throw new \DomainException(
|
|
'Parameter \'instance\' is not an instance of class \'' . $className . '\', instead is ' . get_class(
|
|
$instance
|
|
)
|
|
);
|
|
}
|
|
if (property_exists($instance, 'id')) {
|
|
$idRefl = new \ReflectionProperty($instance, 'id');
|
|
$idRefl->setAccessible(true);
|
|
$id = $idRefl->getValue($instance);
|
|
}
|
|
if (!$idCheck) {
|
|
if (!($this->elements->offsetExists($instance))) {
|
|
$this->elements->attach($instance);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (count($this->elements) > 0) {
|
|
$this->elements->rewind();
|
|
|
|
if (!(property_exists($this->config->getModelClassName(), 'id') && isset($instance->id))) {
|
|
throw new \DomainException(
|
|
'idCheck is Set but either given instance or collection content doesn\'t have id-field'
|
|
);
|
|
}
|
|
$c = $this->elements;
|
|
$c->rewind();
|
|
$found = false;
|
|
while ($c->valid() && !$found) {
|
|
$object = $c->current();
|
|
$currID = $object->id;
|
|
if ($instance->id === $currID) {
|
|
$found = true;
|
|
$foundEntity = $object;
|
|
}
|
|
$c->next();
|
|
}
|
|
$c->rewind();
|
|
if ($found) {
|
|
$this->elements->detach($foundEntity);
|
|
$this->elements->attach($instance);
|
|
return true;
|
|
}
|
|
}
|
|
$this->elements->attach($instance);
|
|
$this->elements->rewind();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param array $criteria Filtering-criteria as array in disjunctive normal form:
|
|
* array(
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* ...
|
|
* ),
|
|
* **OR**
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* ...
|
|
* ),
|
|
* ...
|
|
* )
|
|
*
|
|
* @return array $collection Subset of the current collection as instance of own concrete
|
|
* collection class
|
|
*/
|
|
public function getByCriteriaAsArray(array $criteria)
|
|
{
|
|
return $this->_getByCriteriaAsArray($criteria);
|
|
}
|
|
|
|
/**
|
|
* @param array $criteria Filtering-criteria as array in disjunctive normal form:
|
|
* array(
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* ...
|
|
* ),
|
|
* **OR**
|
|
* array(
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* **AND**
|
|
* array('fieldName','comparator','fieldValue'),
|
|
* ...
|
|
* ),
|
|
* ...
|
|
* )
|
|
*
|
|
* @return array $collection Subset of the current collection as instance of own concrete
|
|
* collection class
|
|
*/
|
|
protected function _getByCriteriaAsArray(array $criteria)
|
|
{
|
|
return $this->_getByCriteria($criteria)->getAllAsArray();
|
|
}
|
|
|
|
/**
|
|
* @return static
|
|
*/
|
|
public function getEmptyCollection()
|
|
{
|
|
return new static($this->config, $this->criteriaValidationService);
|
|
}
|
|
|
|
/**
|
|
* @param array $arr
|
|
*
|
|
* @return genericCollectionTrait
|
|
* @throws \DomainException
|
|
*/
|
|
public function mapArrayToNewCollection(array $arr)
|
|
{
|
|
$newCollection = $this->getEmptyCollection();
|
|
foreach ($arr as $instanceRow) {
|
|
$newInstance = $this->getNullObject();
|
|
if (!$newInstance->isArrayPartiallyMappable($instanceRow)) {
|
|
throw new \DomainException(
|
|
'At least one entry in the array cannot be mapped to an object of the given instance.'
|
|
);
|
|
}
|
|
$newInstance->mapArray($instanceRow);
|
|
$newCollection->add($newInstance, true);
|
|
}
|
|
return $newCollection;
|
|
}
|
|
|
|
/**
|
|
* @param array $fields
|
|
* @return array
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public function getFieldData(array $fields)
|
|
{
|
|
$validFieldsArr = [];
|
|
foreach ($fields as $index => $fieldName) {
|
|
if (!is_string($fieldName)) {
|
|
throw new \InvalidArgumentException(
|
|
"Only strings are allowed as fieldsNames. Array-index $index is not a string."
|
|
);
|
|
}
|
|
if ($this->config->fieldExists($fieldName)) $validFieldsArr[] = $fieldName;
|
|
}
|
|
$dataArr = [];
|
|
$i = 0;
|
|
//for ($this->elements->rewind(); $this->elements->valid(); $this->elements->next()) {
|
|
// $el = $this->elements->current();
|
|
foreach ($this->hashObjectMap as $el) {
|
|
foreach ($validFieldsArr as $validFieldName) {
|
|
$dataArr[$i][$validFieldName] = $el->{$validFieldName};
|
|
}
|
|
++$i;
|
|
}
|
|
return $dataArr;
|
|
}
|
|
|
|
public function rewind()
|
|
{
|
|
if($this->count() > 0) {
|
|
$this->currIndex = array_keys($this->hashObjectMap)[0];
|
|
$this->currIndexPos = 0;
|
|
} else {
|
|
$this->currIndex = '';
|
|
$this->currIndexPos = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function valid()
|
|
{
|
|
return array_key_exists($this->currIndex,$this->hashObjectMap) && !empty($this->hashObjectMap[$this->currIndex]);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function current()
|
|
{
|
|
return $this->hashObjectMap[$this->currIndex];
|
|
}
|
|
|
|
public function next()
|
|
{
|
|
if ($this->count() > 0) {
|
|
$this->currIndexPos++;
|
|
if ($this->currIndexPos > (count($this->hashList) - 1)) {
|
|
$this->currIndexPos = null;
|
|
$this->currIndex = '';
|
|
} else {
|
|
$this->currIndex = $this->hashList[$this->currIndexPos];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return mixed|object
|
|
*/
|
|
public function getFirst()
|
|
{
|
|
$this->rewind();
|
|
if (!empty($this->hashObjectMap)) {
|
|
return $this->hashObjectMap[$this->hashList[0]];
|
|
}
|
|
return $this->getNullObject();
|
|
$this->elements->rewind();
|
|
return $this->elements->valid() ? $this->elements->current() : $this->getNullObject();
|
|
}
|
|
|
|
/**
|
|
* @return mixed|object
|
|
*/
|
|
public function getNext()
|
|
{
|
|
$this->next();
|
|
return $this->valid() ? $this->current() : $this->getNullObject();
|
|
$this->elements->next();
|
|
return $this->elements->valid() ? $this->elements->current() : $this->getNullObject();
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isCurrValid()
|
|
{
|
|
return $this->valid();
|
|
return $this->elements->valid();
|
|
}
|
|
|
|
/**
|
|
* @param $instance
|
|
*/
|
|
private function addNewInstance($instance)
|
|
{
|
|
$initEmpty = (0 === count($this->hashObjectMap));
|
|
$info = $this->getIndexInfo($instance);
|
|
if (!isset($info['id']) || $info['id'] === 0 || $info['id'] === '0') {
|
|
throw new \InvalidArgumentException('Cannot update by id if instance has no non-null, non-zero id-value');
|
|
}
|
|
if (array_key_exists((string)$info['id'], $this->hashesByID)) {
|
|
throw new \InvalidArgumentException(
|
|
'Cannot add. Instance with id ' . $info['id'] . ' already exists in collection.'
|
|
);
|
|
}
|
|
$hash = (string)$info['hash'];
|
|
$hashedAltPrimary = (string)$info['hashed_alt_primary'];
|
|
$this->hashObjectMap[$hash] = $instance;
|
|
$this->hashesByID[(string)$info['id']] = $info['hash'];
|
|
if ($info['hashed_alt_primary'] !== '') {
|
|
$this->hashesByAltPrimary[$hashedAltPrimary] = $info['hash'];
|
|
}
|
|
$this->hashList[] = $info['hash'];
|
|
if ($initEmpty) {
|
|
$this->currIndexPos = 0;
|
|
$this->currIndex = $info['hash'];
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param $instance
|
|
*/
|
|
private function updateByID($instance)
|
|
{
|
|
$info = $this->getIndexInfo($instance);
|
|
if (!isset($info['id']) || $info['id'] === 0 || $info['id'] === '0') {
|
|
throw new \InvalidArgumentException('Cannot update by id if instance has no non-null, non-zero id-value');
|
|
}
|
|
if (!array_key_exists((string)$info['id'], $this->hashesByID)) {
|
|
throw new \InvalidArgumentException(
|
|
'Cannot update by id. No instance with id ' . $info['id'] . ' in collection.'
|
|
);
|
|
}
|
|
$oldHash = $this->hashesByID[(string)$info['id']];
|
|
$newHash = (string)$info['hash'];
|
|
$hashedAltPrimary = (string)$info['hashed_alt_primary'];
|
|
if($oldHash !== $newHash) {
|
|
$pos = array_search($oldHash,$this->hashList,true);
|
|
$this->hashList[$pos] = $newHash;
|
|
if ($pos === $this->currIndexPos) {
|
|
$this->currIndex = $newHash;
|
|
}
|
|
unset($this->hashObjectMap[$oldHash]);
|
|
}
|
|
$this->hashObjectMap[$newHash] = $instance;
|
|
$this->hashesByID[(string)$info['id']] = $info['hash'];
|
|
if ($hashedAltPrimary !== '') {
|
|
$this->hashesByAltPrimary[$hashedAltPrimary] = $info['hash'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $id
|
|
*/
|
|
private function removeByID($id)
|
|
{
|
|
if (!array_key_exists((string)$id, $this->hashesByID)) {
|
|
throw new \InvalidArgumentException('Cannot remove by id. No instance with id ' . $id . ' in collection.');
|
|
}
|
|
$instance = $this->hashObjectMap[$this->hashesByID[$id]];
|
|
$this->removeInstance($instance);
|
|
}
|
|
|
|
/**
|
|
* @param array $altPrimary
|
|
*/
|
|
private function removeByAltPrimary(array $altPrimary)
|
|
{
|
|
if (!$this->hasInstanceForAltPrimary($altPrimary)) {
|
|
throw new \InvalidArgumentException(
|
|
'Cannot remove instance. No instance with given alt_primary in collection.'
|
|
);
|
|
}
|
|
$instance = $this->getInstanceByAltPrimary($altPrimary);
|
|
$this->removeInstance($instance);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $instance
|
|
*/
|
|
private function removeInstance($instance)
|
|
{
|
|
$info = $this->getIndexInfo($instance);
|
|
unset($this->hashObjectMap[(string)$info['hash']], $this->hashesByID[(string)$info['id']], $this->hashesByAltPrimary[(string)$info['hashed_alt_primary']]);
|
|
$hashListPos = array_search((string)$info['hash'],$this->hashList,true);
|
|
unset($this->hashList[$hashListPos]);
|
|
$this->hashList = array_values($this->hashList);
|
|
if ($this->currIndexPos >= $hashListPos) {
|
|
$this->currIndexPos--;
|
|
if ($this->currIndexPos < 0) {
|
|
$this->currIndexPos = 0;
|
|
}
|
|
}
|
|
if (array_key_exists($this->currIndexPos,$this->hashList) && !empty($this->hashList[$this->currIndexPos])) {
|
|
$this->currIndex = $this->hashList[$this->currIndexPos];
|
|
} else {
|
|
$this->currIndex = '';
|
|
$this->currIndexPos = null;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param $id
|
|
* @return bool
|
|
*/
|
|
private function hasInstanceForID($id)
|
|
{
|
|
return array_key_exists((string)$id, $this->hashesByID);
|
|
}
|
|
|
|
/**
|
|
* @param array $altPrimary
|
|
* @return bool
|
|
*/
|
|
private function hasInstanceForAltPrimary(array $altPrimary)
|
|
{
|
|
$altPrimaryHash = $this->hashAltPrimary($altPrimary);
|
|
return array_key_exists($altPrimaryHash, $this->hashesByAltPrimary);
|
|
}
|
|
|
|
/**
|
|
* @param $instance
|
|
* @return bool
|
|
*/
|
|
private function hasInstance($instance)
|
|
{
|
|
$info = $this->getIndexInfo($instance);
|
|
return array_key_exists($info['hash'], $this->hashObjectMap);
|
|
}
|
|
|
|
/**
|
|
* @param $id
|
|
* @return mixed
|
|
*/
|
|
private function getInstanceByID($id)
|
|
{
|
|
if (!$this->hasInstanceForID($id)) {
|
|
throw new \InvalidArgumentException('Cannot get Instance. No instance with id ' . $id . ' in collection.');
|
|
}
|
|
return $this->hashObjectMap[$this->hashesByID[(string)$id]];
|
|
}
|
|
|
|
/**
|
|
* @param array $altPrimary
|
|
* @return mixed
|
|
*/
|
|
private function getInstanceByAltPrimary(array $altPrimary)
|
|
{
|
|
if (!$this->hasInstanceForAltPrimary($altPrimary)) {
|
|
throw new \InvalidArgumentException(
|
|
'Cannot get Instance. No instance with given alt_primary in collection.'
|
|
);
|
|
}
|
|
$hashedAltPrimary = $this->hashAltPrimary($altPrimary);
|
|
return $this->hashObjectMap[$this->hashesByAltPrimary[$hashedAltPrimary]];
|
|
}
|
|
|
|
/**
|
|
* @param $instance
|
|
* @return array
|
|
*/
|
|
private function getIndexInfo($instance)
|
|
{
|
|
$hash = spl_object_hash($instance);
|
|
$id = null;
|
|
$altPrimary = [];
|
|
$hashedAltPrimary = '';
|
|
if (property_exists($instance, 'id')) {
|
|
$idRefl = new \ReflectionProperty($instance, 'id');
|
|
$idRefl->setAccessible(true);
|
|
$id = $idRefl->getValue($instance);
|
|
}
|
|
if (null === $id) {
|
|
$id = $hash;
|
|
}
|
|
if (is_array($this->altPrimary)) {
|
|
foreach ($this->altPrimary as $fieldName) {
|
|
if (property_exists($instance, $fieldName)) {
|
|
$propRefl = new \ReflectionProperty($instance, $fieldName);
|
|
$propRefl->setAccessible(true);
|
|
$altPrimary[$fieldName] = $propRefl->getValue($instance);
|
|
}
|
|
}
|
|
$hashedAltPrimary = $this->hashAltPrimary($altPrimary);
|
|
}
|
|
$info = ['hash' => $hash, 'id' => $id, 'alt_primary' => $altPrimary, 'hashed_alt_primary' => $hashedAltPrimary];
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* @param array $altPrimary
|
|
* @return string
|
|
*/
|
|
private function hashAltPrimary(array $altPrimary)
|
|
{
|
|
return md5(json_encode($altPrimary));
|
|
}
|
|
|
|
} |