diff --git a/src/DataMapper/Cache/Apc.php b/src/DataMapper/Cache/Apc.php index 8b53b60..5bae411 100644 --- a/src/DataMapper/Cache/Apc.php +++ b/src/DataMapper/Cache/Apc.php @@ -1,32 +1,31 @@ getCacheKey($id); - $fn = $this->getFn('fetch'); + $fn = $this->getFn('fetch'); return $fn($key) ?: []; } - protected function deleteCache($id) + protected function deleteCache(array $id) { $key = $this->getCacheKey($id); - $fn = $this->getFn('delete'); + $fn = $this->getFn('delete'); return $fn($key); } - protected function saveCache($id, array $record, $ttl = null) + protected function saveCache(array $id, array $record, $ttl = null) { $key = $this->getCacheKey($id); $ttl = $ttl ?: $this->getCacheTTL(); - $fn = $this->getFn('store'); + $fn = $this->getFn('store'); return $fn($key, $record, $ttl); } @@ -45,6 +44,6 @@ private function getFn($method) } } - return $prefix.$method; + return $prefix . $method; } } diff --git a/src/DataMapper/Cache/Hooks.php b/src/DataMapper/Cache/Hooks.php index 0beda2f..0854f8f 100644 --- a/src/DataMapper/Cache/Hooks.php +++ b/src/DataMapper/Cache/Hooks.php @@ -1,5 +1,4 @@ getCachePolicy(); - - $id = $data->id(); + $id = $data->id(true); if ($policy['insert']) { $record = $this->unpack($data); - $record = $this->normalizeCacheRecord($record); + $record = $this->removeNullValues($record); $this->saveCache($id, $record); } elseif ($policy['not_found']) { @@ -94,15 +92,15 @@ protected function __afterInsert(\Owl\DataMapper\Data $data) protected function __afterUpdate(\Owl\DataMapper\Data $data) { $policy = $this->getCachePolicy(); + $id = $data->id(true); if ($policy['update']) { - $id = $data->id(); $record = $this->unpack($data); - $record = $this->normalizeCacheRecord($record); + $record = $this->removeNullValues($record); $this->saveCache($id, $record); } else { - $this->deleteCache($data->id()); + $this->deleteCache($id); } parent::__afterUpdate($data); @@ -115,7 +113,7 @@ protected function __afterUpdate(\Owl\DataMapper\Data $data) */ protected function __afterDelete(\Owl\DataMapper\Data $data) { - $this->deleteCache($data->id()); + $this->deleteCache($data->id(true)); parent::__afterDelete($data); } @@ -129,7 +127,7 @@ protected function __afterDelete(\Owl\DataMapper\Data $data) */ public function refresh(\Owl\DataMapper\Data $data) { - $this->deleteCache($data->id()); + $this->deleteCache($data->id(true)); return parent::refresh($data); } @@ -142,8 +140,8 @@ public function refresh(\Owl\DataMapper\Data $data) protected function getCachePolicy() { $defaults = [ - 'insert' => false, - 'update' => false, + 'insert' => false, + 'update' => false, 'not_found' => false, ]; @@ -155,32 +153,32 @@ protected function getCachePolicy() if (is_array($policy)) { return array_merge($defaults, $policy); - } else { - if (DEBUG) { - throw new \Exception('Invalid cache policy setting'); - } + } - return $defaults; + if (DEBUG) { + throw new \Exception('Invalid cache policy setting'); } + + return $defaults; } /** * return record from cache if cache is created, or save data into cache. * - * @param mixed $id + * @param array $id * @param \Owl\Service $service * @param string $collection * * @return array */ - protected function doFind($id, \Owl\Service $service = null, $collection = null) + protected function doFind(array $id, \Owl\Service $service = null, $collection = null) { if ($record = $this->getCache($id)) { return isset($record['__IS_NOT_FOUND__']) ? false : $record; } if ($record = parent::doFind($id, $service, $collection)) { - $record = $this->normalizeCacheRecord($record); + $record = $this->removeNullValues($record); $this->saveCache($id, $record); } else { $policy = $this->getCachePolicy(); @@ -194,25 +192,6 @@ protected function doFind($id, \Owl\Service $service = null, $collection = null) return $record; } - /** - * remove NULL value from record. - * - * @param array $record - * - * @return array - */ - protected function normalizeCacheRecord(array $record) - { - // 值为NULL的字段不用缓存 - foreach ($record as $key => $val) { - if ($val === null) { - unset($record[$key]); - } - } - - return $record; - } - /** * @param string $key * @@ -226,35 +205,31 @@ protected function getCacheService($key) } /** - * @param mixed $id + * @param array $id * * @return string */ - protected function getCacheKey($id) + protected function getCacheKey(array $id) { $prefix = $this->hasOption('cache_key') ? $this->getOption('cache_key') : sprintf('entity:%s', str_replace('\\', ':', trim($this->class, '\\'))); - $prefix = $prefix.'@'; + $prefix = $prefix . '@'; if ($this->hasOption('cache_key_prefix')) { - $prefix = $this->getOption('cache_key_prefix').':'.$prefix; + $prefix = $this->getOption('cache_key_prefix') . ':' . $prefix; } - if (is_array($id)) { - ksort($id); - - $kv = []; - foreach ($id as $k => $v) { - $kv[] = sprintf('%s:%s', $k, $v); - } + ksort($id); - $key = $prefix.implode(':', $kv); - } else { - $key = $prefix.$id; + $kv = []; + foreach ($id as $k => $v) { + $kv[] = sprintf('%s:%s', $k, $v); } + $key = $prefix . implode(':', $kv); + return strtolower($key); } @@ -265,4 +240,23 @@ protected function getCacheTTL() { return $this->hasOption('cache_ttl') ? $this->getOption('cache_ttl') : 300; } + + /** + * remove NULL value from record. + * + * @param array $record + * + * @return array + */ + private function removeNullValues(array $record) + { + // 值为NULL的字段不用缓存 + foreach ($record as $key => $val) { + if ($val === null) { + unset($record[$key]); + } + } + + return $record; + } } diff --git a/src/DataMapper/Cache/Memcached.php b/src/DataMapper/Cache/Memcached.php index e989d02..2480bbd 100644 --- a/src/DataMapper/Cache/Memcached.php +++ b/src/DataMapper/Cache/Memcached.php @@ -1,14 +1,13 @@ getCacheKey($id); + $key = $this->getCacheKey($id); $memcached = $this->getCacheService($key); try { @@ -26,19 +25,19 @@ protected function getCache($id) } } - protected function deleteCache($id) + protected function deleteCache(array $id) { - $key = $this->getCacheKey($id); + $key = $this->getCacheKey($id); $memcached = $this->getCacheService($key); return $memcached->delete($key); } - protected function saveCache($id, array $record, $ttl = null) + protected function saveCache(array $id, array $record, $ttl = null) { - $key = $this->getCacheKey($id); + $key = $this->getCacheKey($id); $memcached = $this->getCacheService($key); - $ttl = $ttl ?: $this->getCacheTTL(); + $ttl = $ttl ?: $this->getCacheTTL(); return $memcached->set($key, \Owl\safe_json_encode($record, JSON_UNESCAPED_UNICODE), $ttl); } diff --git a/src/DataMapper/Cache/Redis.php b/src/DataMapper/Cache/Redis.php index e85e13b..b50d426 100644 --- a/src/DataMapper/Cache/Redis.php +++ b/src/DataMapper/Cache/Redis.php @@ -1,14 +1,13 @@ getCacheKey($id); + $key = $this->getCacheKey($id); $redis = $this->getCacheService($key); try { @@ -26,19 +25,19 @@ protected function getCache($id) } } - protected function deleteCache($id) + protected function deleteCache(array $id) { - $key = $this->getCacheKey($id); + $key = $this->getCacheKey($id); $redis = $this->getCacheService($key); return $redis->delete($key); } - protected function saveCache($id, array $record, $ttl = null) + protected function saveCache(array $id, array $record, $ttl = null) { - $key = $this->getCacheKey($id); + $key = $this->getCacheKey($id); $redis = $this->getCacheService($key); - $ttl = $ttl ?: $this->getCacheTTL(); + $ttl = $ttl ?: $this->getCacheTTL(); return $redis->setex($key, $ttl, \Owl\safe_json_encode($record, JSON_UNESCAPED_UNICODE)); } diff --git a/src/DataMapper/DB/Mapper.php b/src/DataMapper/DB/Mapper.php index 0629734..3f46fc2 100644 --- a/src/DataMapper/DB/Mapper.php +++ b/src/DataMapper/DB/Mapper.php @@ -1,13 +1,12 @@ getService(); - $collection = $collection ?: $this->getCollection(); + $service = $service ?: $this->getService(); + $collection = $collection ?: $this->getCollection(); $primary_key = $this->getPrimaryKey(); // 只有一个主键,就可以返回以主键为key的数组结果 @@ -30,16 +29,16 @@ public function select(\Owl\Service $service = null, $collection = null) public function getBySQLAsIterator($sql, array $parameters = [], \Owl\Service $service = null) { $service = $service ?: $this->getService(); - $res = $service->execute($sql, $parameters); + $res = $service->execute($sql, $parameters); while ($record = $res->fetch()) { yield $this->pack($record); } } - protected function doFind($id, \Owl\Service $service = null, $collection = null) + protected function doFind(array $id, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); $select = $this->select($service, $collection); @@ -52,9 +51,9 @@ protected function doFind($id, \Owl\Service $service = null, $collection = null) protected function doInsert(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data); + $record = $this->unpack($data); if (!$service->insert($collection, $record)) { return false; @@ -75,47 +74,32 @@ protected function doInsert(\Owl\DataMapper\Data $data, \Owl\Service $service = protected function doUpdate(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data, ['dirty' => true]); + $record = $this->unpack($data, ['dirty' => true]); - list($where, $params) = $this->whereID($service, $data->id()); + list($where, $params) = $this->whereID($service, $data->id(true)); return $service->update($collection, $record, $where, $params); } protected function doDelete(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - list($where, $params) = $this->whereID($service, $data->id()); + list($where, $params) = $this->whereID($service, $data->id(true)); return $service->delete($collection, $where, $params); } - protected function whereID(\Owl\Service $service, $id) + protected function whereID(\Owl\Service $service, array $id) { + $where = $params = []; $primary_key = $this->getPrimaryKey(); - $key_count = count($primary_key); - if ($key_count === 1 && !is_array($id)) { - $key = $primary_key[0]; - $id = [$key => $id]; - } - - if (!is_array($id)) { - throw new \Exception("{$this->class}: Illegal id value"); - } - - $where = $params = []; foreach ($primary_key as $key) { - $where[] = $service->quoteIdentifier($key).' = ?'; - - if (!isset($id[$key])) { - throw new \Exception("{$this->class}: Illegal id value"); - } - + $where[] = $service->quoteIdentifier($key) . ' = ?'; $params[] = $id[$key]; } $where = implode(' AND ', $where); diff --git a/src/DataMapper/Data.php b/src/DataMapper/Data.php index bae5450..1266b07 100644 --- a/src/DataMapper/Data.php +++ b/src/DataMapper/Data.php @@ -53,27 +53,35 @@ abstract class Data implements \JsonSerializable public function __beforeSave() { } + public function __afterSave() { } + public function __beforeInsert() { } + public function __afterInsert() { } + public function __beforeUpdate() { } + public function __afterUpdate() { } + public function __beforeDelete() { } + public function __afterDelete() { } + /** * @param array [$values] * @param array [$options] @@ -761,21 +769,9 @@ public static function findOrCreate($id) return $data; } - if (!is_array($id)) { - $primary_keys = static::getMapper()->getPrimaryKey(); - - if (count($primary_keys) > 1) { - throw new \Owl\DataMapper\Exception\PropertyException(get_called_class() . ': Illegal id value'); - } - - $props = [ - $primary_keys[0] => $id, - ]; - } else { - $props = $id; - } + $id = static::getMapper()->normalizeID($id); - return new static($props); + return new static($id); } /** diff --git a/src/DataMapper/Mapper.php b/src/DataMapper/Mapper.php index bbb1896..11a03cc 100644 --- a/src/DataMapper/Mapper.php +++ b/src/DataMapper/Mapper.php @@ -20,13 +20,13 @@ abstract class Mapper /** * 根据主键值返回查询到的单条记录. * - * @param string|int|array $id 主键值 + * @param array $id 主键值 * @param Owl\Service [$service] 存储服务连接 * @param string [$collection] 存储集合名 * * @return array 数据结果 */ - abstract protected function doFind($id, \Owl\Service $service = null, $collection = null); + abstract protected function doFind(array $id, \Owl\Service $service = null, $collection = null); /** * 插入数据到存储服务 @@ -70,26 +70,42 @@ public function __construct($class) $this->options = array_merge($this->normalizeOptions($class::getOptions()), $this->options); } - protected function __beforeSave(\Owl\DataMapper\Data $data) {} - protected function __afterSave(\Owl\DataMapper\Data $data) {} - protected function __beforeInsert(\Owl\DataMapper\Data $data) {} - protected function __afterInsert(\Owl\DataMapper\Data $data) {} - protected function __beforeUpdate(\Owl\DataMapper\Data $data) {} - protected function __afterUpdate(\Owl\DataMapper\Data $data) {} - protected function __beforeDelete(\Owl\DataMapper\Data $data) {} - protected function __afterDelete(\Owl\DataMapper\Data $data) {} + protected function __beforeSave(\Owl\DataMapper\Data $data) + { + } + protected function __afterSave(\Owl\DataMapper\Data $data) + { + } + protected function __beforeInsert(\Owl\DataMapper\Data $data) + { + } + protected function __afterInsert(\Owl\DataMapper\Data $data) + { + } + protected function __beforeUpdate(\Owl\DataMapper\Data $data) + { + } + protected function __afterUpdate(\Owl\DataMapper\Data $data) + { + } + protected function __beforeDelete(\Owl\DataMapper\Data $data) + { + } + protected function __afterDelete(\Owl\DataMapper\Data $data) + { + } final private function __before($event, \Owl\DataMapper\Data $data) { $event = ucfirst($event); - call_user_func([$data, '__before'.$event]); - call_user_func([$this, '__before'.$event], $data); + call_user_func([$data, '__before' . $event]); + call_user_func([$this, '__before' . $event], $data); } final private function __after($event, \Owl\DataMapper\Data $data) { $event = ucfirst($event); - call_user_func([$data, '__after'.$event]); - call_user_func([$this, '__after'.$event], $data); + call_user_func([$data, '__after' . $event]); + call_user_func([$this, '__after' . $event], $data); } /** @@ -116,7 +132,7 @@ public function hasOption($key) public function getOption($key) { if (!isset($this->options[$key])) { - throw new \RuntimeException('Mapper: undefined option "'.$key.'"'); + throw new \RuntimeException('Mapper: undefined option "' . $key . '"'); } return $this->options[$key]; @@ -315,6 +331,7 @@ public function unpack(Data $data, array $options = null) */ public function find($id, Data $data = null) { + $id = $this->normalizeID($id); $registry = Registry::getInstance(); if (!$data) { @@ -346,7 +363,7 @@ public function refresh(Data $data) return $data; } - return $this->find($data->id(), $data); + return $this->find($data->id(true), $data); } /** @@ -359,7 +376,7 @@ public function refresh(Data $data) public function save(Data $data) { if ($this->isReadonly()) { - throw new \RuntimeException($this->class.' is readonly'); + throw new \RuntimeException($this->class . ' is readonly'); } $is_fresh = $data->isFresh(); @@ -371,7 +388,7 @@ public function save(Data $data) $result = $is_fresh ? $this->insert($data) : $this->update($data); if (!$result) { - throw new \RuntimeException($this->class.' save failed'); + throw new \RuntimeException($this->class . ' save failed'); } $this->__after('save', $data); @@ -389,7 +406,7 @@ public function save(Data $data) public function destroy(Data $data) { if ($this->isReadonly()) { - throw new \RuntimeException($this->class.' is readonly'); + throw new \RuntimeException($this->class . ' is readonly'); } if ($data->isFresh()) { @@ -399,16 +416,44 @@ public function destroy(Data $data) $this->__before('delete', $data); if (!$this->doDelete($data)) { - throw new \Exception($this->class.' destroy failed'); + throw new \Exception($this->class . ' destroy failed'); } $this->__after('delete', $data); - Registry::getInstance()->remove($this->class, $data->id()); + Registry::getInstance()->remove($this->class, $data->id(true)); return true; } + /** + * 把ID值格式化为数组形式. + * + * @param mixed $id + * + * @return array + */ + public function normalizeID($id) + { + $primary_keys = $this->getPrimaryKey(); + + if (!is_array($id)) { + $key = $primary_keys[0]; + $id = [$key => $id]; + } + + $result = []; + foreach ($primary_keys as $key) { + if (!isset($id[$key])) { + throw new Exception\UnexpectedPropertyValueException('Illegal id value'); + } + + $result[$key] = $id[$key]; + } + + return $result; + } + /** * 把新的Data数据插入到存储集合中. * diff --git a/src/DataMapper/Mongo/Mapper.php b/src/DataMapper/Mongo/Mapper.php index 06d2f32..c785aa0 100644 --- a/src/DataMapper/Mongo/Mapper.php +++ b/src/DataMapper/Mongo/Mapper.php @@ -1,5 +1,4 @@ getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - return $service->findOne($collection, ['_id' => $this->normalizeID($id)]); + return $service->findOne($collection, ['_id' => $this->normalizeIDValue($id)]); } protected function doInsert(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data); - $record['_id'] = $this->normalizeID($data->id()); + $record = $this->unpack($data); + $record['_id'] = $this->normalizeIDValue($data->id()); $service->insert($collection, $record); @@ -79,9 +78,9 @@ protected function doInsert(\Owl\DataMapper\Data $data, \Owl\Service $service = protected function doUpdate(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data, ['dirty' => true]); + $record = $this->unpack($data, ['dirty' => true]); $new = ['$set' => [], '$unset' => []]; foreach ($record as $key => $value) { @@ -100,15 +99,15 @@ protected function doUpdate(\Owl\DataMapper\Data $data, \Owl\Service $service = unset($new['$unset']); } - return $service->update($collection, ['_id' => $this->normalizeID($data)], $new); + return $service->update($collection, ['_id' => $this->normalizeIDValue($data)], $new); } protected function doDelete(\Owl\DataMapper\Data $data, \Owl\Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - return $service->remove($collection, ['_id' => $this->normalizeID($data)]); + return $service->remove($collection, ['_id' => $this->normalizeIDValue($data)]); } protected function normalizeOptions(array $options) @@ -124,7 +123,7 @@ protected function normalizeOptions(array $options) return $options; } - protected function normalizeID($data) + protected function normalizeIDValue($data) { $id = $data instanceof \Owl\DataMapper\Data ? $data->id() diff --git a/src/DataMapper/Registry.php b/src/DataMapper/Registry.php index 560fec6..59ca3a0 100644 --- a/src/DataMapper/Registry.php +++ b/src/DataMapper/Registry.php @@ -3,6 +3,8 @@ class Registry { + use \Owl\Traits\Singleton; + /** * 是否开启DataMapper的Data注册表功能. * @@ -59,7 +61,7 @@ public function set(Data $data) return false; } - if (!$id = $data->id()) { + if (!$id = $data->id(true)) { return false; } @@ -71,11 +73,11 @@ public function set(Data $data) * 根据类名和主键值,获得缓存结果. * * @param string $class - * @param string|int|array $id + * @param array $id * * @return Data|false */ - public function get($class, $id) + public function get($class, array $id) { if (!$this->isEnabled()) { return false; @@ -94,7 +96,7 @@ public function get($class, $id) * @param string $class * @param mixed $id */ - public function remove($class, $id) + public function remove($class, array $id) { if (!$this->isEnabled()) { return false; @@ -120,29 +122,19 @@ public function clear() * * @return string */ - private static function key($class, $id) + private static function key($class, array $id) { + $class = strtolower(ltrim($class, '\\')); + ksort($id); + $key = ''; - if (is_array($id)) { - ksort($id); - - foreach ($id as $prop => $val) { - if ($key) { - $key .= ';'; - } - $key .= "{$prop}:{$val}"; + foreach ($id as $prop => $val) { + if ($key) { + $key .= ';'; } - } else { - $key = $id; + $key .= "{$prop}:{$val}"; } - return $class.'@'.$key; - } - - private static $instance; - - public static function getInstance() - { - return self::$instance ?: (self::$instance = new self()); + return $class . '@' . $key; } } diff --git a/tests/DataMapper/Cache/HooksTest.php b/tests/DataMapper/Cache/HooksTest.php new file mode 100644 index 0000000..5652575 --- /dev/null +++ b/tests/DataMapper/Cache/HooksTest.php @@ -0,0 +1,70 @@ +class)::setMapper('\Tests\Mock\DataMapper\CacheMapper'); + } + + protected function tearDown() + { + ($this->class)::getMapper()->clearCachedData(); + ($this->class)::setMapper('\Tests\Mock\DataMapper\Mapper'); + } + + protected function setAttributes(array $attributes) + { + ($this->class)::getMapper()->setAttributes($attributes); + } + + public function test() + { + $this->setAttributes([ + 'id' => ['type' => 'uuid', 'primary_key' => true], + 'foo' => ['type' => 'string'], + ]); + + $class = $this->class; + $mapper = $class::getMapper(); + + // cache "NOT FOUND" + $id = '5fd55767-1e1d-4c6d-843a-b2a816970300'; + $class::find($id); + + $this->assertTrue($mapper->hasCached(['id' => $id])); + $this->assertSame(['__IS_NOT_FOUND__' => 1], $mapper->getCachedData(['id' => $id])); + + // cache insert + $data = new $class([ + 'id' => $id, + 'foo' => 'FOO', + ]); + $data->save(); + $this->assertSame( + [ + 'id' => $id, + 'foo' => 'FOO', + ], + $mapper->getCachedData(['id' => $id]) + ); + + // cache update + $data->foo = 'bar'; + $data->save(); + $this->assertSame( + [ + 'id' => $id, + 'foo' => 'bar', + ], + $mapper->getCachedData(['id' => $id]) + ); + + // remove cache after data destroy + $data->destroy(); + $this->assertFalse($mapper->hasCached(['id' => $id])); + } +} diff --git a/tests/DataMapper/MapperTest.php b/tests/DataMapper/MapperTest.php new file mode 100644 index 0000000..e9a7b46 --- /dev/null +++ b/tests/DataMapper/MapperTest.php @@ -0,0 +1,59 @@ +class)::getMapper()->setAttributes($attributes); + } + + public function testNormalizeID() + { + $this->setAttributes([ + 'id' => ['type' => 'uuid', 'primary_key' => true], + ]); + + $mapper = ($this->class)::getMapper(); + + $id = $mapper->normalizeID('f6c7339d-9d68-41b1-9f16-860f29ea5dee'); + $this->assertSame(['id' => 'f6c7339d-9d68-41b1-9f16-860f29ea5dee'], $id); + + $id = $mapper->normalizeID(['id' => '3ed0d7a8-e51e-44ee-a2a1-09d435d94cc5']); + $this->assertSame(['id' => '3ed0d7a8-e51e-44ee-a2a1-09d435d94cc5'], $id); + + //////////////////////////////////////////////////////////////////////// + $this->setAttributes([ + 'foo' => ['type' => 'uuid', 'primary_key' => true], + 'bar' => ['type' => 'uuid', 'primary_key' => true], + ]); + + $id = [ + 'foo' => 'cfd04278-6c98-4cf2-a26d-345c1ab729a2', + 'bar' => '7b76caa2-08fa-4e85-8cb8-744a1c5704ac', + 'baz' => 'ab8b9e5d-087a-458d-a595-29eda8ed38fb', + ]; + + $this->assertSame( + [ + 'foo' => 'cfd04278-6c98-4cf2-a26d-345c1ab729a2', + 'bar' => '7b76caa2-08fa-4e85-8cb8-744a1c5704ac', + ], + $mapper->normalizeID($id) + ); + + try { + $mapper->normalizeID('1f47f18c-40d4-47c8-954b-c2d8a6fe4c2a'); + $this->fail('test Mapper::normalizeID() failed'); + } catch (\Owl\DataMapper\Exception\UnexpectedPropertyValueException $ex) { + } + + try { + $mapper->normalizeID(['foo' => '1f47f18c-40d4-47c8-954b-c2d8a6fe4c2a']); + $this->fail('test Mapper::normalizeID() failed'); + } catch (\Owl\DataMapper\Exception\UnexpectedPropertyValueException $ex) { + } + } +} diff --git a/tests/DataMapper/RegistryTest.php b/tests/DataMapper/RegistryTest.php new file mode 100644 index 0000000..211a9e2 --- /dev/null +++ b/tests/DataMapper/RegistryTest.php @@ -0,0 +1,39 @@ +class)::getMapper()->setAttributes($attributes); + } + + public function test() + { + $this->setAttributes([ + 'id' => ['type' => 'uuid', 'primary_key' => true], + ]); + $id = '710c825e-20ea-4c98-b313-30d9eec2b2dc'; + + $class = $this->class; + $data = new $class([ + 'id' => $id, + ]); + + $registry = Registry::getInstance(); + + $this->assertFalse((bool) $registry->get($class, $data->id(true))); + $data->save(); + $this->assertFalse((bool) $registry->get($class, $data->id(true))); + + $data = $class::find($id); + $this->assertTrue((bool) $registry->get($class, $data->id(true))); + + $data->destroy(); + $this->assertFalse((bool) $registry->get($class, ['id' => $id])); + } +} diff --git a/tests/Mock/DataMapper/Cache/Memory.php b/tests/Mock/DataMapper/Cache/Memory.php new file mode 100644 index 0000000..91bf4fe --- /dev/null +++ b/tests/Mock/DataMapper/Cache/Memory.php @@ -0,0 +1,32 @@ +getCacheKey($id); + + return static::$__cache__[$key] ?? []; + } + + protected function deleteCache(array $id) + { + $key = $this->getCacheKey($id); + + unset(static::$__cache__[$key]); + } + + protected function saveCache(array $id, array $record, $ttl = null) + { + $key = $this->getCacheKey($id); + + static::$__cache__[$key] = $record; + + // var_dump(static::$__cache__); + } +} diff --git a/tests/Mock/DataMapper/CacheMapper.php b/tests/Mock/DataMapper/CacheMapper.php new file mode 100644 index 0000000..1d0ad0b --- /dev/null +++ b/tests/Mock/DataMapper/CacheMapper.php @@ -0,0 +1,35 @@ +getCacheKey($id); + + return isset(static::$__cache__[$key]); + } + + public function getCachedData(array $id) + { + $key = $this->getCacheKey($id); + + return static::$__cache__[$key] ?? false; + } + + public function clearCachedData() + { + static::$__cache__ = []; + } + + protected function getCachePolicy() + { + return [ + 'insert' => true, + 'update' => true, + 'not_found' => true, + ]; + } +} diff --git a/tests/Mock/DataMapper/Data.php b/tests/Mock/DataMapper/Data.php index c13c3ca..fd8333a 100644 --- a/tests/Mock/DataMapper/Data.php +++ b/tests/Mock/DataMapper/Data.php @@ -13,4 +13,9 @@ class Data extends \Owl\DataMapper\Data protected static $attributes = [ 'id' => ['type' => 'integer', 'primary_key' => true, 'auto_generate' => true], ]; + + public static function setMapper(string $mapper_class) + { + static::$mapper = $mapper_class; + } } diff --git a/tests/Mock/DataMapper/Mapper.php b/tests/Mock/DataMapper/Mapper.php index ba545ac..90d7510 100644 --- a/tests/Mock/DataMapper/Mapper.php +++ b/tests/Mock/DataMapper/Mapper.php @@ -1,5 +1,4 @@ getOptions(); + $options = $this->getOptions(); $options['attributes'] = $attributes; $this->options = $this->normalizeOptions($options); } - protected function doFind($id, Service $service = null, $collection = null) + protected function doFind(array $id, Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); return $service->find($collection, $id); @@ -24,15 +23,15 @@ protected function doFind($id, Service $service = null, $collection = null) protected function doInsert(\Owl\DataMapper\Data $data, Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data); + $record = $this->unpack($data); - if (!$service->insert($collection, $record, $data->id())) { + if (!$service->insert($collection, $record, $data->id(true))) { return false; } - $id = array(); + $id = []; foreach ($this->getPrimaryKey() as $key) { if (!isset($record[$key])) { if (!$last_id = $service->getLastId($collection, $key)) { @@ -47,18 +46,18 @@ protected function doInsert(\Owl\DataMapper\Data $data, Service $service = null, protected function doUpdate(\Owl\DataMapper\Data $data, Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - $record = $this->unpack($data, array('dirty' => true)); + $record = $this->unpack($data, ['dirty' => true]); - return $service->update($collection, $record, $data->id()); + return $service->update($collection, $record, $data->id(true)); } protected function doDelete(\Owl\DataMapper\Data $data, Service $service = null, $collection = null) { - $service = $service ?: $this->getService(); + $service = $service ?: $this->getService(); $collection = $collection ?: $this->getCollection(); - return $service->delete($collection, $data->id()); + return $service->delete($collection, $data->id(true)); } } diff --git a/tests/Mock/DataMapper/Service.php b/tests/Mock/DataMapper/Service.php index bfe4ebd..ce2ad05 100644 --- a/tests/Mock/DataMapper/Service.php +++ b/tests/Mock/DataMapper/Service.php @@ -13,7 +13,7 @@ public function disconnect() { } - public function find($table, $id) + public function find($table, array $id) { $key = $this->keyOfId($id); @@ -24,17 +24,11 @@ public function find($table, $id) return $this->data[$table][$key]; } - public function insert($table, array $row, $id = null) + public function insert($table, array $row, array $id = null) { - if (!$id) { - if (is_array($id)) { - foreach ($id as $k => $v) { - if (!$v) { - $id[$k] = StorageSequence::getInstance()->next(); - } - } - } else { - $id = StorageSequence::getInstance()->next(); + foreach ($id as $k => $v) { + if (!$v) { + $id[$k] = StorageSequence::getInstance()->next(); } } @@ -45,7 +39,7 @@ public function insert($table, array $row, $id = null) return $id; } - public function update($table, array $row, $id) + public function update($table, array $row, array $id) { if (!$this->find($table, $id)) { return false; @@ -57,7 +51,7 @@ public function update($table, array $row, $id) return true; } - public function delete($table, $id) + public function delete($table, array $id) { $key = $this->keyOfId($id); @@ -84,12 +78,8 @@ public function clear($table = null) } } - protected function keyOfId($id) + protected function keyOfId(array $id) { - if (!is_array($id)) { - return $id; - } - ksort($id); return md5(strtolower(json_encode($id)));