mirror of
https://github.com/doctrine/KeyValueStore.git
synced 2026-03-24 16:52:17 +01:00
Compare commits
49 Commits
enhancemen
...
missing-ra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2117eef3e3 | ||
|
|
ff58187a3b | ||
|
|
e878a42d81 | ||
|
|
55f0840706 | ||
|
|
ac4c317a7e | ||
|
|
705d5fc1d1 | ||
|
|
465bd365f7 | ||
|
|
9fb5e498ed | ||
|
|
573723ab3c | ||
|
|
8ba3db4ce7 | ||
|
|
bb1ba64453 | ||
|
|
903f6a8c97 | ||
|
|
0a5917a2a9 | ||
|
|
b7daeb0d19 | ||
|
|
06e11dae33 | ||
|
|
4584d99529 | ||
|
|
54e05f9acd | ||
|
|
4d4a17aca9 | ||
|
|
b2a4e320b0 | ||
|
|
b2906e209e | ||
|
|
f29622194e | ||
|
|
2517bb04c5 | ||
|
|
a0d85c1c27 | ||
|
|
0d9c10edf2 | ||
|
|
606ca8b29d | ||
|
|
1ffab2e77d | ||
|
|
e0878ca97a | ||
|
|
7877050062 | ||
|
|
cc3e478d10 | ||
|
|
79bee991a2 | ||
|
|
f61ce21d4b | ||
|
|
58f98f8a94 | ||
|
|
902da8f693 | ||
|
|
84d5db7073 | ||
|
|
ef425578fb | ||
|
|
518f4211b0 | ||
|
|
bad3b56f4b | ||
|
|
2836bc0cfd | ||
|
|
6000d76099 | ||
|
|
b095170bdc | ||
|
|
fe7195fc2d | ||
|
|
e282c1d9f6 | ||
|
|
e2f7ea54f7 | ||
|
|
03a2413e99 | ||
|
|
2c99b71d19 | ||
|
|
fe1e155e69 | ||
|
|
ec14ab554a | ||
|
|
3c7ba8415e | ||
|
|
69832f5423 |
1
.php_cs
1
.php_cs
@@ -61,7 +61,6 @@ $config = Symfony\CS\Config\Config::create()
|
||||
'align_equals',
|
||||
'concat_with_spaces',
|
||||
'header_comment',
|
||||
'logical_not_operators_with_spaces',
|
||||
'logical_not_operators_with_successor_space',
|
||||
'multiline_spaces_before_semicolon',
|
||||
'newline_after_open_tag',
|
||||
|
||||
15
.travis.yml
15
.travis.yml
@@ -1,17 +1,26 @@
|
||||
language: php
|
||||
|
||||
services:
|
||||
- cassandra
|
||||
- couchdb
|
||||
- mongodb
|
||||
- redis-server
|
||||
|
||||
matrix:
|
||||
include:
|
||||
-
|
||||
dist: precise
|
||||
php: 5.5
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_PHP_VERSION != "hhvm" && $TRAVIS_PHP_VERSION != "7.0" ]]; then sh ./tests/travis.sh; fi
|
||||
- if [[ ${TRAVIS_PHP_VERSION:0:1} != "7" ]]; then sh ./tests/travis.sh; fi
|
||||
- composer self-update
|
||||
|
||||
install:
|
||||
|
||||
@@ -23,7 +23,7 @@ Following vendors are targeted:
|
||||
* Doctrine\Common\Cache provider (Implemented)
|
||||
* RDBMS (Implemented)
|
||||
* Couchbase (Implemented)
|
||||
* Amazon DynamoDB
|
||||
* Amazon DynamoDB (Implemented)
|
||||
* CouchDB (Implemented)
|
||||
* Cassandra
|
||||
* MongoDB (Implemented)
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
{
|
||||
"name": "doctrine/key-value-store",
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"doctrine/common": "^2.4",
|
||||
"doctrine/couchdb": "^1.0.0-beta4"
|
||||
"php": "^5.5|^7.0",
|
||||
"doctrine/common": "^2.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"datastax/php-driver": "^1.0",
|
||||
"phpunit/phpunit": "^4.8",
|
||||
"doctrine/couchdb": "^2.0@alpha",
|
||||
"phpunit/phpunit": "^4.8|^5.0",
|
||||
"aws/aws-sdk-php": "^3.8",
|
||||
"riak/riak-client": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"riak/riak-client": "to use the Riak storage",
|
||||
"aws/aws-sdk-php": "to use the DynamoDB storage",
|
||||
"doctrine/couchdb": "to use the CouchDB storage",
|
||||
"ext-couchbase": "to use the Couchbase storage",
|
||||
"aws/aws-sdk-php": "to use the DynamoDB storage"
|
||||
"riak/riak-client": "to use the Riak storage"
|
||||
},
|
||||
"description": "Simple Key-Value Store Abstraction Layer that maps to PHP objects, allowing for many backends.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -52,6 +52,7 @@ database backends, you have to configure the actual storage you want to use.
|
||||
This configuration is obviously specific to all the different storage drivers.
|
||||
So far the following drivers exist (and are documented here):
|
||||
|
||||
* PHP Array
|
||||
* Doctrine Cache Backend
|
||||
* SQL Backend with Doctrine DBAL
|
||||
* Microsoft Windows Azure Table
|
||||
@@ -63,6 +64,19 @@ So far the following drivers exist (and are documented here):
|
||||
Also all those storage backends obviously have different dependencies in terms
|
||||
of PHP libraries or PHP PECL extensions.
|
||||
|
||||
PHP Array
|
||||
---------
|
||||
|
||||
PHP array is used mainly for development and teesting purposes.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Doctrine\KeyValueStore\Storage\ArrayStorage;
|
||||
|
||||
$storage = new ArrayStorage();
|
||||
|
||||
Doctrine Cache Backend
|
||||
----------------------
|
||||
|
||||
@@ -131,6 +145,26 @@ as a storage layer through the Windows Azure PHP SDK:
|
||||
|
||||
$storage = new AzureSdkTableStorage($client);
|
||||
|
||||
Cassandra
|
||||
---------
|
||||
|
||||
Cassandra is supported through the `PECL extension <https://pecl.php.net/package/cassandra>`_
|
||||
and the `DataStax PHP driver <https://github.com/datastax/php-driver>`_:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
use Cassandra;
|
||||
use Cassandra\SimpleStatement;
|
||||
use Doctrine\KeyValueStore\Storage\CassandraStorage;
|
||||
|
||||
$cluster = Cassandra::cluster()->build();
|
||||
$session = $cluster->connect();
|
||||
$session->execute(new SimpleStatement('USE doctrine'));
|
||||
|
||||
$storage = new CassandraStorage($session);
|
||||
|
||||
Couchbase
|
||||
---------
|
||||
|
||||
@@ -164,6 +198,29 @@ CouchDB storage setup based on `doctrine/couchdb-client <https://github.com/doct
|
||||
|
||||
$storage = new CouchDbStorage($client);
|
||||
|
||||
DynamoDb
|
||||
---------
|
||||
|
||||
DynamoDb is supported through the `AWS SDK for PHP <https://aws.amazon.com/sdk-for-php/>`_
|
||||
Create your tables via the AWS DynamoDb console or using the `PHP based API <http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelPHPTableOperationsExample.html>`_
|
||||
See the `AWS docs <http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/UsingPHP.html#PHPSDKCredentialsSet>`_ for more information on configuring credentials for the client.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$sdk = new \Aws\Sdk([...]);
|
||||
$client = $sdk->createDynamoDb();
|
||||
|
||||
$storage = new DynamoDbStorage(
|
||||
$client,
|
||||
// Optional key name, defaults to Id.
|
||||
null,
|
||||
// Optional table name/ key name pairs.
|
||||
// This example uses a table called Awesome keyed by MyKey.
|
||||
['storage_keys' => ['Awesome' => 'MyKey']]
|
||||
);
|
||||
|
||||
MongoDB
|
||||
-------
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class Configuration
|
||||
*/
|
||||
public function getMappingDriverImpl()
|
||||
{
|
||||
if ( ! isset($this->config['mappingDriver'])) {
|
||||
if (! isset($this->config['mappingDriver'])) {
|
||||
throw KeyValueStoreException::mappingDriverMissing();
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class Configuration
|
||||
*/
|
||||
public function getMetadataCache()
|
||||
{
|
||||
if ( ! isset($this->config['metadataCache'])) {
|
||||
if (! isset($this->config['metadataCache'])) {
|
||||
$this->config['metadataCache'] = new ArrayCache();
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class Configuration
|
||||
*/
|
||||
public function getIdConverterStrategy()
|
||||
{
|
||||
if ( ! isset($this->config['idConverter'])) {
|
||||
if (! isset($this->config['idConverter'])) {
|
||||
$this->config['idConverter'] = new NullIdConverter();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class EntityManager
|
||||
*/
|
||||
public function find($className, $key)
|
||||
{
|
||||
return $this->unitOfWork->reconsititute($className, $key);
|
||||
return $this->unitOfWork->reconstititute($className, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,7 +71,7 @@ class Response
|
||||
*/
|
||||
public function getHeader($name)
|
||||
{
|
||||
if ( ! isset($this->headers[$name])) {
|
||||
if (! isset($this->headers[$name])) {
|
||||
return;
|
||||
}
|
||||
return $this->headers[$name];
|
||||
|
||||
@@ -157,7 +157,7 @@ class SocketClient implements Client
|
||||
|
||||
// Remove leading newlines, should not accur at all, actually.
|
||||
while (true) {
|
||||
if ( ! (($line = fgets($this->connection)) !== false) || ! (($lineContent = rtrim($line)) === '')) {
|
||||
if (! (($line = fgets($this->connection)) !== false) || ! (($lineContent = rtrim($line)) === '')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,7 @@ class SocketClient implements Client
|
||||
|
||||
// Read response body
|
||||
$body = '';
|
||||
if ( ! isset($headers['transfer-encoding']) ||
|
||||
if (! isset($headers['transfer-encoding']) ||
|
||||
($headers['transfer-encoding'] !== 'chunked')) {
|
||||
// HTTP 1.1 supports chunked transfer encoding, if the according
|
||||
// header is not set, just read the specified amount of bytes.
|
||||
|
||||
@@ -26,14 +26,14 @@ class CompositeIdHandler implements IdHandlingStrategy
|
||||
{
|
||||
public function normalizeId(ClassMetadata $metadata, $key)
|
||||
{
|
||||
if ( ! $metadata->isCompositeKey && ! is_array($key)) {
|
||||
if (! $metadata->isCompositeKey && ! is_array($key)) {
|
||||
$id = [$metadata->identifier[0] => $key];
|
||||
} elseif ( ! is_array($key)) {
|
||||
} elseif (! is_array($key)) {
|
||||
throw new \InvalidArgumentException('Array of identifier key-value pairs is expected!');
|
||||
} else {
|
||||
$id = [];
|
||||
foreach ($metadata->identifier as $field) {
|
||||
if ( ! isset($key[$field])) {
|
||||
if (! isset($key[$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Missing identifier field $field in request for the primary key."
|
||||
);
|
||||
|
||||
48
lib/Doctrine/KeyValueStore/InvalidArgumentException.php
Normal file
48
lib/Doctrine/KeyValueStore/InvalidArgumentException.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\KeyValueStore;
|
||||
|
||||
class InvalidArgumentException extends KeyValueStoreException
|
||||
{
|
||||
public static function invalidType($name, $expectedType, &$actual)
|
||||
{
|
||||
return new static(
|
||||
sprintf('The %s must be a %s, got "%s" instead.', $name, $expectedType, gettype($actual)),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalidLength($name, $min, $max)
|
||||
{
|
||||
return new static(
|
||||
sprintf('The %s must be at least %d but no more than %d chars.', $name, $min, $max),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalidTableName($name)
|
||||
{
|
||||
return new static(
|
||||
sprintf('Invalid table name: %s', $name),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -52,14 +52,14 @@ class AnnotationDriver implements MappingDriver
|
||||
public function loadMetadataForClass($className, ClassMetadata $metadata)
|
||||
{
|
||||
$class = $metadata->getReflectionClass();
|
||||
if ( ! $class) {
|
||||
if (! $class) {
|
||||
// this happens when running annotation driver in combination with
|
||||
// static reflection services. This is not the nicest fix
|
||||
$class = new \ReflectionClass($metadata->name);
|
||||
}
|
||||
|
||||
$entityAnnot = $this->reader->getClassAnnotation($class, 'Doctrine\KeyValueStore\Mapping\Annotations\Entity');
|
||||
if ( ! $entityAnnot) {
|
||||
if (! $entityAnnot) {
|
||||
throw new \InvalidArgumentException($metadata->name . ' is not a valid key-value-store entity.');
|
||||
}
|
||||
$metadata->storageName = $entityAnnot->storageName;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
namespace Doctrine\KeyValueStore\Mapping;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata as BaseClassMetadata;
|
||||
use ReflectionClass;
|
||||
|
||||
class ClassMetadata implements BaseClassMetadata
|
||||
{
|
||||
@@ -50,7 +51,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
|
||||
public function mapField($mapping)
|
||||
{
|
||||
if ( ! isset($this->transientFields[$mapping['fieldName']])) {
|
||||
if (! isset($this->transientFields[$mapping['fieldName']])) {
|
||||
$this->fields[$mapping['fieldName']] = $mapping;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +94,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fully-qualified class name of this persistent class.
|
||||
*
|
||||
@@ -100,6 +102,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,6 +114,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,6 +124,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function getReflectionClass()
|
||||
{
|
||||
return new ReflectionClass($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +136,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function isIdentifier($fieldName)
|
||||
{
|
||||
return in_array($fieldName, $this->identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +148,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function hasField($fieldName)
|
||||
{
|
||||
return isset($this->fields[$fieldName]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,6 +160,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function hasAssociation($fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,6 +172,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function isSingleValuedAssociation($fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +184,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function isCollectionValuedAssociation($fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,6 +196,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function getFieldNames()
|
||||
{
|
||||
return array_column($this->fields, 'fieldName');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,6 +206,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function getIdentifierFieldNames()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,6 +254,7 @@ class ClassMetadata implements BaseClassMetadata
|
||||
*/
|
||||
public function isAssociationInverseSide($assocName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,12 +58,12 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
$class->storageName = $parent->storageName;
|
||||
}
|
||||
|
||||
if ( ! $class->storageName) {
|
||||
if (! $class->storageName) {
|
||||
$parts = explode('\\', $class->name);
|
||||
$class->storageName = end($parts);
|
||||
}
|
||||
|
||||
if ( ! $class->identifier) {
|
||||
if (! $class->identifier) {
|
||||
throw new \InvalidArgumentException('Class ' . $class->name . ' has no identifier.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,8 @@ namespace Doctrine\KeyValueStore;
|
||||
|
||||
class NotFoundException extends KeyValueStoreException
|
||||
{
|
||||
public static function notFoundByKey($key)
|
||||
{
|
||||
return new static(sprintf('Could not find an item with key: %s', $key), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,15 +202,15 @@ class RangeQuery
|
||||
/**
|
||||
* Execute query and return a result iterator.
|
||||
*
|
||||
* @return ResultIterator
|
||||
* @return array
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$storage = $this->em->unwrap();
|
||||
|
||||
if ( ! ($storage instanceof RangeQueryStorage)) {
|
||||
if (! $storage instanceof RangeQueryStorage) {
|
||||
throw new \RuntimeException(
|
||||
'The storage backend ' . $this->storage->getName() . ' does not support range queries.'
|
||||
'The storage backend ' . $storage->getName() . ' does not support range queries.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,14 @@ namespace Doctrine\KeyValueStore\Query;
|
||||
interface RangeQueryStorage
|
||||
{
|
||||
/**
|
||||
* Execute the range query and return a ResultIterator
|
||||
* Execute the range query and return an array
|
||||
*
|
||||
* @param RangeQuery $query
|
||||
* @param string $storageName
|
||||
* @param array $key
|
||||
* @param Closure $hydrateRow
|
||||
*
|
||||
* @return ResultIterator
|
||||
* @return array
|
||||
*/
|
||||
public function executeRangeQuery(RangeQuery $query, $storageName, $key, \Closure $hydrateRow = null);
|
||||
}
|
||||
|
||||
136
lib/Doctrine/KeyValueStore/Storage/ArrayStorage.php
Normal file
136
lib/Doctrine/KeyValueStore/Storage/ArrayStorage.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\KeyValueStore\Storage;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\KeyValueStore\NotFoundException;
|
||||
|
||||
/**
|
||||
* Array storage, mainly used for development purposes.
|
||||
*
|
||||
* @author Emanuele Minotto <minottoemanuele@gmail.com>
|
||||
*/
|
||||
class ArrayStorage implements Storage
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $data = [];
|
||||
|
||||
public function supportsPartialUpdates()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this storage support composite primary keys?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportsCompositePrimaryKeys()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this storage require composite primary keys?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function requiresCompositePrimaryKeys()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert data into the storage key specified.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param array $data
|
||||
*/
|
||||
public function insert($storageName, $key, array $data)
|
||||
{
|
||||
$this->update($storageName, $key, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data into the given key.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param array $data
|
||||
*/
|
||||
public function update($storageName, $key, array $data)
|
||||
{
|
||||
if (!isset($this->data[$storageName])) {
|
||||
$this->data[$storageName] = [];
|
||||
}
|
||||
|
||||
$this->data[$storageName][serialize($key)] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete data at key
|
||||
*
|
||||
* @param array|string $key
|
||||
*/
|
||||
public function delete($storageName, $key)
|
||||
{
|
||||
if (!isset($this->data[$storageName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->data[$storageName][serialize($key)])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->data[$storageName][serialize($key)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find data at key
|
||||
*
|
||||
* @param array|string $key
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function find($storageName, $key)
|
||||
{
|
||||
if (!isset($this->data[$storageName])) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!isset($this->data[$storageName][serialize($key)])) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
unset($this->data[$storageName][serialize($key)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a name of the underlying storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'array';
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ class AzureSdkTableStorage implements Storage, RangeQueryStorage
|
||||
$filters = ['PartitionKey eq ' . $this->quoteFilterValue($query->getPartitionKey())];
|
||||
|
||||
foreach ($query->getConditions() as $condition) {
|
||||
if ( ! in_array($condition[0], ['eq', 'neq', 'le', 'lt', 'ge', 'gt'])) {
|
||||
if (! in_array($condition[0], ['eq', 'neq', 'le', 'lt', 'ge', 'gt'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Windows Azure Table only supports eq, neq, le, lt, ge, gt as conditions.'
|
||||
);
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace Doctrine\KeyValueStore\Storage;
|
||||
use Cassandra\ExecutionOptions;
|
||||
use Cassandra\Session;
|
||||
use Doctrine\KeyValueStore\NotFoundException;
|
||||
use Doctrine\KeyValueStore\Query\RangeQuery;
|
||||
use Doctrine\KeyValueStore\Query\RangeQueryStorage;
|
||||
|
||||
/**
|
||||
* Cassandra Storage Engine for KeyValueStore.
|
||||
@@ -31,7 +33,7 @@ use Doctrine\KeyValueStore\NotFoundException;
|
||||
*
|
||||
* @uses https://github.com/datastax/php-driver
|
||||
*/
|
||||
class CassandraStorage implements Storage
|
||||
class CassandraStorage implements Storage, RangeQueryStorage
|
||||
{
|
||||
/**
|
||||
* @var \Cassandra\Session
|
||||
@@ -160,7 +162,7 @@ class CassandraStorage implements Storage
|
||||
$result = $this->session->execute($stmt, $options);
|
||||
$rows = iterator_to_array($result);
|
||||
|
||||
if ( ! isset($rows[0])) {
|
||||
if (! isset($rows[0])) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
@@ -176,6 +178,13 @@ class CassandraStorage implements Storage
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function executeRangeQuery(RangeQuery $query, $storageName, $key, \Closure $hydrateRow = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
namespace Doctrine\KeyValueStore\Storage;
|
||||
|
||||
use Doctrine\CouchDB\CouchDBClient;
|
||||
use Doctrine\CouchDB\Mango\MangoQuery;
|
||||
use Doctrine\KeyValueStore\Query\RangeQuery;
|
||||
use Doctrine\KeyValueStore\Query\RangeQueryStorage;
|
||||
|
||||
/**
|
||||
* Key-Value-Storage using a Doctrine CouchDB Client library as backend.
|
||||
@@ -30,7 +33,7 @@ use Doctrine\CouchDB\CouchDBClient;
|
||||
*
|
||||
* @author Emanuele Minotto <minottoemanuele@gmail.com>
|
||||
*/
|
||||
final class CouchDbStorage implements Storage
|
||||
final class CouchDbStorage implements Storage, RangeQueryStorage
|
||||
{
|
||||
/**
|
||||
* @var CouchDBClient
|
||||
@@ -116,7 +119,7 @@ final class CouchDbStorage implements Storage
|
||||
/**
|
||||
* @param string $storageName
|
||||
* @param array|string $key
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function flattenKey($storageName, $key)
|
||||
@@ -127,7 +130,7 @@ final class CouchDbStorage implements Storage
|
||||
return $finalKey . $key;
|
||||
}
|
||||
|
||||
if ( ! is_array($key)) {
|
||||
if (! is_array($key)) {
|
||||
throw new \InvalidArgumentException('The key should be a string or a flat array.');
|
||||
}
|
||||
|
||||
@@ -137,4 +140,72 @@ final class CouchDbStorage implements Storage
|
||||
|
||||
return $finalKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function executeRangeQuery(RangeQuery $query, $storageName, $key, \Closure $hydrateRow = null)
|
||||
{
|
||||
$mangoQuery = new MangoQuery();
|
||||
$partitionKey = $query->getPartitionKey();
|
||||
|
||||
$conditions = [];
|
||||
foreach ($query->getConditions() as $condition) {
|
||||
switch ($condition[0]) {
|
||||
case RangeQuery::CONDITION_LE:
|
||||
$conditions[] = [
|
||||
$partitionKey => [
|
||||
'$lte' => $condition[1],
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
||||
case RangeQuery::CONDITION_GE:
|
||||
$conditions[] = [
|
||||
$partitionKey => [
|
||||
'$gte' => $condition[1],
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
||||
case RangeQuery::CONDITION_NEQ:
|
||||
$conditions[] = [
|
||||
$partitionKey => [
|
||||
'$ne' => $condition[1],
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
||||
case RangeQuery::CONDITION_STARTSWITH:
|
||||
$conditions[] = [
|
||||
$partitionKey => [
|
||||
'$regex' => '^'.$condition[1],
|
||||
],
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$conditions[] = [
|
||||
$partitionKey => [
|
||||
'$'.$condition[0] => $condition[1],
|
||||
],
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$mangoQuery
|
||||
->select(['_id', $key])
|
||||
->where(['$and' => $conditions])
|
||||
->limit($query->getLimit());
|
||||
|
||||
$results = [];
|
||||
$mangoResults = $this->client->find($query);
|
||||
|
||||
foreach ($mangoResults as $mangoResult) {
|
||||
$results[] = $hydrateRow($mangoResult);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class DBALStorage implements Storage
|
||||
|
||||
$data = $stmt->fetchColumn();
|
||||
|
||||
if ( ! $data) {
|
||||
if (! $data) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class DoctrineCacheStorage implements Storage
|
||||
|
||||
private function flattenKey($storageName, $key)
|
||||
{
|
||||
if ( ! $this->supportsCompositeKeys) {
|
||||
if (! $this->supportsCompositeKeys) {
|
||||
return $storageName . '-' . $key;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
namespace Doctrine\KeyValueStore\Storage;
|
||||
|
||||
use Aws\DynamoDb\DynamoDbClient;
|
||||
use Aws\DynamoDb\Exception\ResourceNotFoundException;
|
||||
use Aws\DynamoDb\Iterator\ItemIterator;
|
||||
use Aws\DynamoDb\Marshaler;
|
||||
use Doctrine\KeyValueStore\InvalidArgumentException;
|
||||
use Doctrine\KeyValueStore\NotFoundException;
|
||||
|
||||
/**
|
||||
@@ -32,19 +32,185 @@ use Doctrine\KeyValueStore\NotFoundException;
|
||||
*/
|
||||
class DynamoDbStorage implements Storage
|
||||
{
|
||||
/**
|
||||
* The key that DynamoDb uses to indicate the name of the table.
|
||||
*/
|
||||
const TABLE_NAME_KEY = 'TableName';
|
||||
|
||||
/**
|
||||
* The key that DynamoDb uses to indicate whether or not to do a consistent read.
|
||||
*/
|
||||
const CONSISTENT_READ_KEY = 'ConsistentRead';
|
||||
|
||||
/**
|
||||
* The key that is used to refer to the DynamoDb table key.
|
||||
*/
|
||||
const TABLE_KEY = 'Key';
|
||||
|
||||
/**
|
||||
* The key that is used to refer to the marshaled item for DynamoDb table.
|
||||
*/
|
||||
const TABLE_ITEM_KEY = 'Item';
|
||||
|
||||
/**
|
||||
* @var \Aws\DynamoDb\DynamoDbClient
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Aws\DynamoDb\DynamoDbClient $client
|
||||
* @var Marshaler
|
||||
*/
|
||||
public function __construct(DynamoDbClient $client)
|
||||
private $marshaler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $defaultKeyName = 'Id';
|
||||
|
||||
/**
|
||||
* A associative array where the key is the table name and the value is the name of the key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tableKeys = [];
|
||||
|
||||
/**
|
||||
* @param DynamoDbClient $client The client for connecting to AWS DynamoDB.
|
||||
* @param Marshaler|null $marshaler (optional) Marshaller for converting data to/from DynamoDB format.
|
||||
* @param string $defaultKeyName (optional) Default name to use for keys.
|
||||
* @param array $tableKeys $tableKeys (optional) An associative array for keys representing table names and values
|
||||
* representing key names for those tables.
|
||||
*/
|
||||
public function __construct(
|
||||
DynamoDbClient $client,
|
||||
Marshaler $marshaler = null,
|
||||
$defaultKeyName = null,
|
||||
array $tableKeys = []
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->marshaler = $marshaler ?: new Marshaler();
|
||||
|
||||
if ($defaultKeyName !== null) {
|
||||
$this->setDefaultKeyName($defaultKeyName);
|
||||
}
|
||||
|
||||
foreach ($tableKeys as $table => $keyName) {
|
||||
$this->setKeyForTable($table, $keyName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a DynamoDB key name.
|
||||
*
|
||||
* @param $name mixed The name to validate.
|
||||
*
|
||||
* @throws InvalidArgumentException When the key name is invalid.
|
||||
*/
|
||||
private function validateKeyName($name)
|
||||
{
|
||||
$this->client = $client;
|
||||
if (! is_string($name)) {
|
||||
throw InvalidArgumentException::invalidType('key', 'string', $name);
|
||||
}
|
||||
|
||||
$len = strlen($name);
|
||||
if ($len > 255 || $len < 1) {
|
||||
throw InvalidArgumentException::invalidLength('name', 1, 255);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a DynamoDB table name.
|
||||
*
|
||||
* @see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html
|
||||
*
|
||||
* @param $name string The table name to validate.
|
||||
*
|
||||
* @throws InvalidArgumentException When the name is invalid.
|
||||
*/
|
||||
private function validateTableName($name)
|
||||
{
|
||||
if (! is_string($name)) {
|
||||
throw InvalidArgumentException::invalidType('key', 'string', $name);
|
||||
}
|
||||
|
||||
if (! preg_match('/^[a-z0-9_.-]{3,255}$/i', $name)) {
|
||||
throw InvalidArgumentException::invalidTableName($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default key name for storage tables.
|
||||
*
|
||||
* @param $name string The default name to use for the key.
|
||||
*
|
||||
* @throws InvalidArgumentException When the key name is invalid.
|
||||
*/
|
||||
private function setDefaultKeyName($name)
|
||||
{
|
||||
$this->validateKeyName($name);
|
||||
$this->defaultKeyName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default key name.
|
||||
*
|
||||
* @return string The default key name.
|
||||
*/
|
||||
public function getDefaultKeyName()
|
||||
{
|
||||
return $this->defaultKeyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a key name for a specific table.
|
||||
*
|
||||
* @param $table string The name of the table.
|
||||
* @param $key string The name of the string.
|
||||
*
|
||||
* @throws InvalidArgumentException When the key or table name is invalid.
|
||||
*/
|
||||
private function setKeyForTable($table, $key)
|
||||
{
|
||||
$this->validateTableName($table);
|
||||
$this->validateKeyName($key);
|
||||
$this->tableKeys[$table] = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific name for a key for a given table. The default is returned if this table does not have
|
||||
* an actual override.
|
||||
*
|
||||
* @param string $tableName The name of the table.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getKeyNameForTable($tableName)
|
||||
{
|
||||
return isset($this->tableKeys[$tableName]) ?
|
||||
$this->tableKeys[$tableName] :
|
||||
$this->defaultKeyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a key to be in a valid format for lookups for DynamoDB. If passing an array, that means that the key
|
||||
* is the name of the key and the value is the actual value for the lookup.
|
||||
*
|
||||
* @param string $storageName Table name.
|
||||
* @param string $key Key name.
|
||||
*
|
||||
* @return array The key in DynamoDB format.
|
||||
*/
|
||||
private function prepareKey($storageName, $key)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
$keyValue = reset($key);
|
||||
$keyName = key($key);
|
||||
} else {
|
||||
$keyValue = $key;
|
||||
$keyName = $this->getKeyNameForTable($storageName);
|
||||
}
|
||||
|
||||
return $this->marshaler->marshalItem([$keyName => $keyValue]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,14 +242,9 @@ class DynamoDbStorage implements Storage
|
||||
*/
|
||||
public function insert($storageName, $key, array $data)
|
||||
{
|
||||
$this->createTable($storageName);
|
||||
|
||||
$this->prepareData($key, $data);
|
||||
|
||||
$result = $this->client->putItem([
|
||||
'TableName' => $storageName,
|
||||
'Item' => $this->client->formatAttributes($data),
|
||||
'ReturnConsumedCapacity' => 'TOTAL',
|
||||
$this->client->putItem([
|
||||
self::TABLE_NAME_KEY => $storageName,
|
||||
self::TABLE_ITEM_KEY => $this->prepareKey($storageName, $key) + $this->marshaler->marshalItem($this->prepareData($data)),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -92,23 +253,9 @@ class DynamoDbStorage implements Storage
|
||||
*/
|
||||
public function update($storageName, $key, array $data)
|
||||
{
|
||||
$this->prepareData($key, $data);
|
||||
|
||||
unset($data['id']);
|
||||
|
||||
foreach ($data as $k => $v) {
|
||||
$data[$k] = [
|
||||
'Value' => $this->client->formatValue($v),
|
||||
];
|
||||
}
|
||||
|
||||
$result = $this->client->updateItem([
|
||||
'TableName' => $storageName,
|
||||
'Key' => [
|
||||
'id' => ['S' => $key],
|
||||
],
|
||||
'AttributeUpdates' => $data,
|
||||
]);
|
||||
//We are using PUT so we just replace the original item, if the key
|
||||
//does not exist, it will be created.
|
||||
$this->insert($storageName, $key, $this->prepareData($data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,11 +263,9 @@ class DynamoDbStorage implements Storage
|
||||
*/
|
||||
public function delete($storageName, $key)
|
||||
{
|
||||
$result = $this->client->deleteItem([
|
||||
'TableName' => $storageName,
|
||||
'Key' => [
|
||||
'id' => ['S' => $key],
|
||||
],
|
||||
$this->client->deleteItem([
|
||||
self::TABLE_NAME_KEY => $storageName,
|
||||
self::TABLE_KEY => $this->prepareKey($storageName, $key),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -129,20 +274,19 @@ class DynamoDbStorage implements Storage
|
||||
*/
|
||||
public function find($storageName, $key)
|
||||
{
|
||||
$iterator = new ItemIterator($this->client->getScanIterator([
|
||||
'TableName' => $storageName,
|
||||
'Key' => [
|
||||
'Id' => ['S' => $key],
|
||||
],
|
||||
]));
|
||||
$item = $this->client->getItem([
|
||||
self::TABLE_NAME_KEY => $storageName,
|
||||
self::CONSISTENT_READ_KEY => true,
|
||||
self::TABLE_KEY => $this->prepareKey($storageName, $key),
|
||||
]);
|
||||
|
||||
$results = $iterator->toArray();
|
||||
|
||||
if (count($results)) {
|
||||
return array_shift($results);
|
||||
if (! $item) {
|
||||
throw NotFoundException::notFoundByKey($key);
|
||||
}
|
||||
|
||||
throw new NotFoundException();
|
||||
$item = $item->get(self::TABLE_ITEM_KEY);
|
||||
|
||||
return $this->marshaler->unmarshalItem($item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,51 +300,23 @@ class DynamoDbStorage implements Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tableName
|
||||
* Prepare data by removing empty item attributes.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function createTable($tableName)
|
||||
protected function prepareData($data)
|
||||
{
|
||||
try {
|
||||
$this->client->describeTable([
|
||||
'TableName' => $tableName,
|
||||
]);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
$this->client->createTable([
|
||||
'AttributeDefinitions' => [
|
||||
[
|
||||
'AttributeName' => 'id',
|
||||
'AttributeType' => 'S',
|
||||
],
|
||||
],
|
||||
'TableName' => $tableName,
|
||||
'KeySchema' => [
|
||||
[
|
||||
'AttributeName' => 'id',
|
||||
'KeyType' => 'HASH',
|
||||
],
|
||||
],
|
||||
'ProvisionedThroughput' => [
|
||||
'ReadCapacityUnits' => 1,
|
||||
'WriteCapacityUnits' => 1,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
$callback = function ($value) {
|
||||
return $value !== null && $value !== [] && $value !== '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param array $data
|
||||
*/
|
||||
protected function prepareData($key, &$data)
|
||||
{
|
||||
$data = array_merge($data, [
|
||||
'id' => $key,
|
||||
]);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value === null || $value === [] || $value === '') {
|
||||
unset($data[$key]);
|
||||
foreach ($data as &$value) {
|
||||
if (is_array($value)) {
|
||||
$value = $this->prepareData($value);
|
||||
}
|
||||
}
|
||||
return array_filter($data, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class RedisStorage implements Storage
|
||||
{
|
||||
$key = $this->getKeyName($key);
|
||||
|
||||
if ( ! $this->client->exists($key)) {
|
||||
if (! $this->client->exists($key)) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class RiakStorage implements Storage
|
||||
/** @var $object \Riak\Object */
|
||||
$object = $bucket->get($key);
|
||||
|
||||
if ( ! $object->exists()) {
|
||||
if (! $object->exists()) {
|
||||
// object does not exist, do nothing
|
||||
return;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class RiakStorage implements Storage
|
||||
/** @var $object \Riak\Object */
|
||||
$object = $bucket->get($key);
|
||||
|
||||
if ( ! $object->exists()) {
|
||||
if (! $object->exists()) {
|
||||
throw new NotFoundException;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class WindowsAzureTableStorage implements Storage, RangeQueryStorage
|
||||
</entry>';
|
||||
|
||||
const XML_TEMPLATE_TABLE = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
|
||||
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
|
||||
<title />
|
||||
<updated></updated>
|
||||
<author>
|
||||
@@ -338,7 +338,7 @@ class WindowsAzureTableStorage implements Storage, RangeQueryStorage
|
||||
|
||||
$filters = ['PartitionKey eq ' . $this->quoteFilterValue($query->getPartitionKey())];
|
||||
foreach ($query->getConditions() as $condition) {
|
||||
if ( ! in_array($condition[0], ['eq', 'neq', 'le', 'lt', 'ge', 'gt'])) {
|
||||
if (! in_array($condition[0], ['eq', 'neq', 'le', 'lt', 'ge', 'gt'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Windows Azure Table only supports eq, neq, le, lt, ge, gt as conditions.'
|
||||
);
|
||||
|
||||
@@ -85,13 +85,13 @@ class UnitOfWork
|
||||
return;
|
||||
}
|
||||
|
||||
public function reconsititute($className, $key)
|
||||
public function reconstititute($className, $key)
|
||||
{
|
||||
$class = $this->cmf->getMetadataFor($className);
|
||||
$id = $this->idHandler->normalizeId($class, $key);
|
||||
$data = $this->storageDriver->find($class->storageName, $id);
|
||||
|
||||
if ( ! $data) {
|
||||
if (! $data) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class UnitOfWork
|
||||
$originalData = $this->originalData[spl_object_hash($object)];
|
||||
|
||||
foreach ($snapshot as $field => $value) {
|
||||
if ( ! isset($originalData[$field]) || $originalData[$field] !== $value) {
|
||||
if (! isset($originalData[$field]) || $originalData[$field] !== $value) {
|
||||
$changeSet[$field] = $value;
|
||||
}
|
||||
}
|
||||
@@ -159,13 +159,13 @@ class UnitOfWork
|
||||
$data = [];
|
||||
|
||||
foreach ($class->reflFields as $fieldName => $reflProperty) {
|
||||
if ( ! isset($class->fields[$fieldName]['id'])) {
|
||||
if (! isset($class->fields[$fieldName]['id'])) {
|
||||
$data[$fieldName] = $reflProperty->getValue($object);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (get_object_vars($object) as $property => $value) {
|
||||
if ( ! isset($data[$property])) {
|
||||
if (! isset($data[$property])) {
|
||||
$data[$property] = $value;
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ class UnitOfWork
|
||||
$class = $this->cmf->getMetadataFor(get_class($object));
|
||||
$id = $this->idHandler->getIdentifier($class, $object);
|
||||
|
||||
if ( ! $id) {
|
||||
if (! $id) {
|
||||
throw new \RuntimeException('Trying to persist entity that has no id.');
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ class UnitOfWork
|
||||
public function scheduleForDelete($object)
|
||||
{
|
||||
$oid = spl_object_hash($object);
|
||||
if ( ! isset($this->identifiers[$oid])) {
|
||||
if (! isset($this->identifiers[$oid])) {
|
||||
throw new \RuntimeException(
|
||||
'Object scheduled for deletion is not managed. Only managed objects can be deleted.'
|
||||
);
|
||||
@@ -242,7 +242,7 @@ class UnitOfWork
|
||||
$id = $this->idHandler->getIdentifier($class, $object);
|
||||
$id = $this->idConverter->serialize($class, $id);
|
||||
|
||||
if ( ! $id) {
|
||||
if (! $id) {
|
||||
throw new \RuntimeException('Trying to persist entity that has no id.');
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class CassandraTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$cluster = Cassandra::cluster()->build();
|
||||
$cluster = Cassandra::cluster()->build();
|
||||
$this->session = $cluster->connect();
|
||||
|
||||
try {
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
namespace Doctrine\Tests\KeyValueStore\Mapping;
|
||||
|
||||
use Doctrine\KeyValueStore\Mapping\ClassMetadata;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Doctrine\KeyValueStore\Mapping\ClassMetadata
|
||||
*/
|
||||
class ClassMetadataTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $metadata;
|
||||
@@ -68,4 +72,118 @@ class ClassMetadataTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->assertEquals([], $this->metadata->fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getIdentifier
|
||||
*/
|
||||
public function testGetIdentifier()
|
||||
{
|
||||
$identifier = $this->metadata->getIdentifier();
|
||||
|
||||
$this->assertInternalType('array', $identifier);
|
||||
|
||||
foreach ($identifier as $key => $value) {
|
||||
$this->assertInternalType('integer', $key);
|
||||
$this->assertInternalType('string', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getReflectionClass
|
||||
*/
|
||||
public function testGetReflectionClass()
|
||||
{
|
||||
$reflectionClass = $this->metadata->getReflectionClass();
|
||||
|
||||
$this->assertInstanceOf(ReflectionClass::class, $reflectionClass);
|
||||
$this->assertSame(__CLASS__, $reflectionClass->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isIdentifier
|
||||
*/
|
||||
public function testIsIdentifier()
|
||||
{
|
||||
$this->metadata->mapIdentifier('id');
|
||||
|
||||
$this->assertTrue($this->metadata->isIdentifier('id'));
|
||||
$this->assertFalse($this->metadata->isIdentifier('test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::hasField
|
||||
*/
|
||||
public function testHasField()
|
||||
{
|
||||
$this->metadata->mapField(['fieldName' => 'foo']);
|
||||
|
||||
$this->assertTrue($this->metadata->hasField('foo'));
|
||||
$this->assertFalse($this->metadata->hasField('bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::hasAssociation
|
||||
*/
|
||||
public function testHasAssociation()
|
||||
{
|
||||
$this->assertFalse($this->metadata->hasAssociation(sha1(rand())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isSingleValuedAssociation
|
||||
*/
|
||||
public function testIsSingleValuedAssociation()
|
||||
{
|
||||
$this->assertFalse($this->metadata->isSingleValuedAssociation(sha1(rand())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isCollectionValuedAssociation
|
||||
*/
|
||||
public function testIsCollectionValuedAssociation()
|
||||
{
|
||||
$this->assertFalse($this->metadata->isCollectionValuedAssociation(sha1(rand())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getFieldNames
|
||||
*/
|
||||
public function testGetFieldNames()
|
||||
{
|
||||
$this->metadata->mapField(['fieldName' => 'foo']);
|
||||
|
||||
$fieldNames = $this->metadata->getFieldNames();
|
||||
|
||||
$this->assertInternalType('array', $fieldNames);
|
||||
|
||||
foreach ($fieldNames as $key => $value) {
|
||||
$this->assertInternalType('integer', $key);
|
||||
$this->assertInternalType('string', $value);
|
||||
}
|
||||
|
||||
$this->assertSame(['foo'], $fieldNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getIdentifierFieldNames
|
||||
*/
|
||||
public function testGetIdentifierFieldNames()
|
||||
{
|
||||
$identifierFieldNames = $this->metadata->getIdentifierFieldNames();
|
||||
|
||||
$this->assertInternalType('array', $identifierFieldNames);
|
||||
|
||||
foreach ($identifierFieldNames as $key => $value) {
|
||||
$this->assertInternalType('integer', $key);
|
||||
$this->assertInternalType('string', $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::isAssociationInverseSide
|
||||
*/
|
||||
public function testIsAssociationInverseSide()
|
||||
{
|
||||
$this->assertFalse($this->metadata->isAssociationInverseSide(sha1(rand())));
|
||||
}
|
||||
}
|
||||
|
||||
250
tests/Doctrine/Tests/KeyValueStore/Query/RangeQueryTest.php
Normal file
250
tests/Doctrine/Tests/KeyValueStore/Query/RangeQueryTest.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\KeyValueStore\Query;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\KeyValueStore\EntityManager;
|
||||
use Doctrine\KeyValueStore\Query\RangeQuery;
|
||||
use Doctrine\KeyValueStore\Storage\DoctrineCacheStorage;
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Doctrine\KeyValueStore\Query\RangeQuery
|
||||
*/
|
||||
class RangeQueryTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $partitionKey;
|
||||
|
||||
/**
|
||||
* @var RangeQuery
|
||||
*/
|
||||
protected $object;
|
||||
|
||||
/**
|
||||
* Sets up the fixture, for example, opens a network connection.
|
||||
* This method is called before a test is executed.
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$this->entityManager = $this
|
||||
->getMockBuilder(EntityManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->className = sha1(rand());
|
||||
$this->partitionKey = sha1(rand());
|
||||
|
||||
$this->object = new RangeQuery(
|
||||
$this->entityManager,
|
||||
$this->className,
|
||||
$this->partitionKey
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::setLimit
|
||||
* @covers ::getLimit
|
||||
*/
|
||||
public function testLimit()
|
||||
{
|
||||
$limit = rand();
|
||||
|
||||
$setterOutput = $this->object->setLimit($limit);
|
||||
|
||||
$this->assertInstanceOf(RangeQuery::class, $setterOutput);
|
||||
$this->assertSame($limit, $this->object->getLimit());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getClassName
|
||||
*/
|
||||
public function testGetClassName()
|
||||
{
|
||||
$this->assertSame(
|
||||
$this->className,
|
||||
$this->object->getClassName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getPartitionKey
|
||||
*/
|
||||
public function testGetPartitionKey()
|
||||
{
|
||||
$this->assertSame(
|
||||
$this->partitionKey,
|
||||
$this->object->getPartitionKey()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testGetConditions()
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($this->object);
|
||||
$constants = $reflectionClass->getConstants();
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
|
||||
$this->assertInternalType('array', $conditions);
|
||||
|
||||
foreach ($conditions as $condition) {
|
||||
$this->assertArrayHasKey($condition[0], $constants);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeEquals
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeEquals()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeEquals($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_EQ, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeNotEquals
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeNotEquals()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeNotEquals($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_NEQ, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeLessThan
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeLessThan()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeLessThan($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_LT, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeLessThanEquals
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeLessThanEquals()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeLessThanEquals($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_LE, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeGreaterThan
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeGreaterThan()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeGreaterThan($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_GT, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::rangeGreaterThanEquals
|
||||
* @depends testGetConditions
|
||||
*/
|
||||
public function testRangeGreaterThanEquals()
|
||||
{
|
||||
$value = 'test';
|
||||
|
||||
$output = $this->object->rangeGreaterThanEquals($value);
|
||||
$this->assertInstanceOf(RangeQuery::class, $output);
|
||||
|
||||
$conditions = $this->object->getConditions();
|
||||
$this->assertArraySubset(
|
||||
[[RangeQuery::CONDITION_GE, $value]],
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::execute
|
||||
*/
|
||||
public function testWrongExecute()
|
||||
{
|
||||
$this->entityManager
|
||||
->method('unwrap')
|
||||
->willReturn(new DoctrineCacheStorage(new ArrayCache));
|
||||
|
||||
$this->setExpectedException(RuntimeException::class);
|
||||
$this->object->execute();
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ abstract class AbstractStorageTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testInsertCompositeKey()
|
||||
{
|
||||
if ( ! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
if (! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
$this->markTestSkipped('Composite keys need to be supported for this test to run.');
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ abstract class AbstractStorageTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testUpdateCompositeKey()
|
||||
{
|
||||
if ( ! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
if (! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
$this->markTestSkipped('Composite keys need to be supported for this test to run.');
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ abstract class AbstractStorageTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testDeleteCompositeKey()
|
||||
{
|
||||
if ( ! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
if (! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
$this->markTestSkipped('Composite keys need to be supported for this test to run.');
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ abstract class AbstractStorageTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testFindCompositeKey()
|
||||
{
|
||||
if ( ! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
if (! $this->storage->supportsCompositePrimaryKeys()) {
|
||||
$this->markTestSkipped('Composite keys need to be supported for this test to run.');
|
||||
}
|
||||
|
||||
|
||||
102
tests/Doctrine/Tests/KeyValueStore/Storage/ArrayStorageTest.php
Normal file
102
tests/Doctrine/Tests/KeyValueStore/Storage/ArrayStorageTest.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||
|
||||
use Doctrine\KeyValueStore\Storage\ArrayStorage;
|
||||
use ReflectionProperty;
|
||||
|
||||
/**
|
||||
* @author Emanuele Minotto <minottoemanuele@gmail.com>
|
||||
*/
|
||||
class ArrayStorageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var ArrayStorage
|
||||
*/
|
||||
private $storage;
|
||||
|
||||
protected function setup()
|
||||
{
|
||||
$this->storage = new ArrayStorage();
|
||||
}
|
||||
|
||||
public function testSupportsPartialUpdates()
|
||||
{
|
||||
$this->assertFalse($this->storage->supportsPartialUpdates());
|
||||
}
|
||||
|
||||
public function testSupportsCompositePrimaryKeys()
|
||||
{
|
||||
$this->assertFalse($this->storage->supportsCompositePrimaryKeys());
|
||||
}
|
||||
|
||||
public function testRequiresCompositePrimaryKeys()
|
||||
{
|
||||
$this->assertFalse($this->storage->requiresCompositePrimaryKeys());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider methodsProvider
|
||||
*/
|
||||
public function testInsert($method)
|
||||
{
|
||||
$data = [
|
||||
'author' => 'John Doe',
|
||||
'title' => 'example book',
|
||||
];
|
||||
|
||||
$this->storage->$method('foo', 'bar', $data);
|
||||
|
||||
$reflector = new ReflectionProperty(ArrayStorage::class, 'data');
|
||||
$reflector->setAccessible(true);
|
||||
|
||||
$storedValue = $reflector->getValue($this->storage);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => [
|
||||
serialize('bar') => $data,
|
||||
],
|
||||
],
|
||||
$storedValue
|
||||
);
|
||||
|
||||
$this->storage->$method('foo', 'bar', $data);
|
||||
$this->assertCount(1, $storedValue);
|
||||
$this->assertCount(1, $storedValue['foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function methodsProvider()
|
||||
{
|
||||
return [
|
||||
['insert'],
|
||||
['update'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetName()
|
||||
{
|
||||
$this->assertEquals('array', $this->storage->getName());
|
||||
}
|
||||
}
|
||||
346
tests/Doctrine/Tests/KeyValueStore/Storage/DynamoDbTest.php
Normal file
346
tests/Doctrine/Tests/KeyValueStore/Storage/DynamoDbTest.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||
|
||||
use Doctrine\KeyValueStore\Storage\DynamoDbStorage;
|
||||
|
||||
/**
|
||||
* @covers \Doctrine\KeyValueStore\Storage\DynamoDbStorage
|
||||
*/
|
||||
class DynamoDbTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private function getDynamoDbMock($methods = [])
|
||||
{
|
||||
$client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient')->disableOriginalConstructor();
|
||||
|
||||
if (count($methods)) {
|
||||
$client->setMethods($methods);
|
||||
}
|
||||
|
||||
return $client->getMock();
|
||||
}
|
||||
|
||||
private function getDynamoDbResultMock($methods = [])
|
||||
{
|
||||
$result = $this->getMockBuilder('Aws\Result')->disableOriginalConstructor();
|
||||
|
||||
if (count($methods)) {
|
||||
$result->setMethods($methods);
|
||||
}
|
||||
|
||||
return $result->getMock();
|
||||
}
|
||||
|
||||
public function testTheStorageName()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->assertSame('dynamodb', $storage->getName());
|
||||
}
|
||||
|
||||
public function testDefaultKeyName()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->assertAttributeSame('Id', 'defaultKeyName', $storage);
|
||||
}
|
||||
|
||||
public function testThatTableKeysInitiallyEmpty()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->assertAttributeSame([], 'tableKeys', $storage);
|
||||
}
|
||||
|
||||
public function testDefaultKeyCannotBeSomethingOtherThanString()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$this->setExpectedException(
|
||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
||||
'The key must be a string, got "array" instead.'
|
||||
);
|
||||
new DynamoDbStorage($client, null, []);
|
||||
}
|
||||
|
||||
public function testTableKeysMustAllBeStringsOrElse()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$this->setExpectedException(
|
||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
||||
'The key must be a string, got "object" instead.'
|
||||
);
|
||||
new DynamoDbStorage($client, null, null, ['mytable' => 'hello', 'yourtable' => new \stdClass()]);
|
||||
}
|
||||
|
||||
public function testKeyNameMustBeUnder255Bytes()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$this->setExpectedException(
|
||||
'\Doctrine\KeyValueStore\KeyValueStoreException',
|
||||
'The name must be at least 1 but no more than 255 chars.'
|
||||
);
|
||||
new DynamoDbStorage($client, null, str_repeat('a', 256));
|
||||
}
|
||||
|
||||
public function invalidTableNames()
|
||||
{
|
||||
return [
|
||||
['a2'],
|
||||
['yo%'],
|
||||
['что'],
|
||||
['h@llo'],
|
||||
];
|
||||
}
|
||||
|
||||
public function validTableNames()
|
||||
{
|
||||
return [
|
||||
['MyTable'],
|
||||
['This_is0k-...'],
|
||||
['hello_world'],
|
||||
['...........00....'],
|
||||
];
|
||||
}
|
||||
|
||||
private function invokeMethod($methodName, $obj, array $args = null)
|
||||
{
|
||||
$relf = new \ReflectionObject($obj);
|
||||
$method = $relf->getMethod($methodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
if ($args) {
|
||||
return $method->invokeArgs($obj, $args);
|
||||
}
|
||||
|
||||
return $method->invoke($obj);
|
||||
}
|
||||
|
||||
public function testTableNameMustBeAString()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->setExpectedException('\Doctrine\KeyValueStore\InvalidArgumentException');
|
||||
$this->invokeMethod('setKeyForTable', $storage, [[], 'Id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider invalidTableNames
|
||||
*/
|
||||
public function testTableNameValidatesAgainstInvalidTableNames($tableName)
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->setExpectedException('\Doctrine\KeyValueStore\KeyValueStoreException');
|
||||
$this->invokeMethod('setKeyForTable', $storage, [$tableName, 'Id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validTableNames
|
||||
*/
|
||||
public function testTableNameValidatesAgainstValidTableNames($tableName)
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->invokeMethod('setKeyForTable', $storage, [$tableName, 'Id']);
|
||||
|
||||
$this->assertAttributeSame([$tableName => 'Id'], 'tableKeys', $storage);
|
||||
}
|
||||
|
||||
public function testThatYouCanHaveMultipleTablesWithOverrides()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->invokeMethod('setKeyForTable', $storage, ['Aaa', '2']);
|
||||
$this->invokeMethod('setKeyForTable', $storage, ['Bbb', '1']);
|
||||
|
||||
$this->assertAttributeSame(['Aaa' => '2', 'Bbb' => '1'], 'tableKeys', $storage);
|
||||
}
|
||||
|
||||
public function testGetterForDefaultKeyName()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client, null, 'CustomKey');
|
||||
$this->assertSame('CustomKey', $storage->getDefaultKeyName());
|
||||
}
|
||||
|
||||
public function testGetWillReturnDefaultKeyForUnrecognizedTableName()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client, null, 'CustomKey');
|
||||
$this->assertSame('CustomKey', $this->invokeMethod('getKeyNameForTable', $storage, ['whatever_this_is']));
|
||||
}
|
||||
|
||||
public function testGetWillReturnCorrectKeyForRecognizedTableName()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
$storage = new DynamoDbStorage($client, null, 'CustomKey', ['MyTable' => 'Yesss']);
|
||||
$this->assertSame('Yesss', $this->invokeMethod('getKeyNameForTable', $storage, ['MyTable']));
|
||||
}
|
||||
|
||||
public function testThatSomeStorageHasDifferentKey()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
|
||||
$storage = new DynamoDbStorage($client, null, 'sauce', ['this' => 'that', 'yolo' => 'now']);
|
||||
|
||||
$this->assertSame(['that' => ['N' => '111']], $this->invokeMethod('prepareKey', $storage, ['this', 111]));
|
||||
}
|
||||
|
||||
public function testThatSomeStorageUsesDefaultKey()
|
||||
{
|
||||
$client = $this->getDynamoDbMock();
|
||||
|
||||
$storage = new DynamoDbStorage($client, null, 'sauce', ['this' => 'that', 'yolo' => 'now']);
|
||||
|
||||
$this->assertSame(['sauce' => ['S' => 'hello']], $this->invokeMethod('prepareKey', $storage, ['MyTable', 'hello']));
|
||||
}
|
||||
|
||||
public function testInsertingCallsAPutItem()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['putItem']);
|
||||
|
||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Item' => [
|
||||
'Id' => ['S' => 'stuff'],
|
||||
'hi' => ['S' => 'there'],
|
||||
'yo' => ['BOOL' => false],
|
||||
],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->insert('MyTable', 'stuff', ['hi' => 'there', 'yo' => false]);
|
||||
}
|
||||
|
||||
public function testInsertingPreparesNestedAttributes()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['putItem']);
|
||||
|
||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Item' => [
|
||||
'Id' => ['S' => 'stuff'],
|
||||
'hi' => ['S' => 'there'],
|
||||
'what' => ['L' => [
|
||||
['S' => 'Yep'],
|
||||
]],
|
||||
'yo' => ['BOOL' => false],
|
||||
],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->insert('MyTable', 'stuff', ['hi' => 'there', 'yo' => false, 'what' => ['Yep', '']]);
|
||||
}
|
||||
|
||||
public function testUpdateActuallyAlsoCallsInsert()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['putItem']);
|
||||
|
||||
$client->expects($this->once())->method('putItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Item' => [
|
||||
'Id' => ['S' => 'stuff'],
|
||||
'hi' => ['S' => 'there'],
|
||||
'yo' => ['BOOL' => false],
|
||||
],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->update('MyTable', 'stuff', ['hi' => 'there', 'yo' => false]);
|
||||
}
|
||||
|
||||
public function testDeleteItem()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
||||
|
||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Key' => ['Id' => ['S' => 'abc123']],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->delete('MyTable', 'abc123');
|
||||
}
|
||||
|
||||
public function testDeleteItemWithKeyValuePair()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
||||
|
||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Key' => ['Id' => ['S' => 'abc123']],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->delete('MyTable', ['Id' => 'abc123']);
|
||||
}
|
||||
|
||||
public function testPassingArrayAsKeyIsAPassthruToInsert()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['deleteItem']);
|
||||
|
||||
$client->expects($this->once())->method('deleteItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'Key' => ['Id' => ['S' => 'abc123']],
|
||||
]));
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$storage->delete('MyTable', 'abc123');
|
||||
}
|
||||
|
||||
public function testTryingToFindAnItemThatDoesNotExist()
|
||||
{
|
||||
$client = $this->getDynamoDbMock(['getItem']);
|
||||
$client->expects($this->once())->method('getItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'ConsistentRead' => true,
|
||||
'Key' => ['Id' => ['N' => '1000']],
|
||||
]))->willReturn(null);
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$this->setExpectedException(
|
||||
'\Doctrine\KeyValueStore\NotFoundException',
|
||||
'Could not find an item with key: 1000'
|
||||
);
|
||||
$storage->find('MyTable', 1000);
|
||||
}
|
||||
|
||||
public function testFindAnItemThatExists()
|
||||
{
|
||||
$result = $this->getDynamoDbResultMock(['get']);
|
||||
$result->expects($this->once())->method('get')->with('Item')->willReturn([
|
||||
'hello' => ['S' => 'world'],
|
||||
]);
|
||||
|
||||
$client = $this->getDynamoDbMock(['getItem']);
|
||||
$client->expects($this->once())->method('getItem')->with($this->equalTo([
|
||||
'TableName' => 'MyTable',
|
||||
'ConsistentRead' => true,
|
||||
'Key' => ['Id' => ['N' => '1000']],
|
||||
]))->willReturn($result);
|
||||
|
||||
$storage = new DynamoDbStorage($client);
|
||||
$actualResult = $storage->find('MyTable', 1000);
|
||||
|
||||
$this->assertSame(['hello' => 'world'], $actualResult);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,10 @@ class MongoDbStorageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->mongo = $this->getMock('\Mongo');
|
||||
$this->mongo = $this
|
||||
->getMockBuilder('\Mongo')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->mongodb = $this->getMockBuilder('\MongoDB')->disableOriginalConstructor()->getMock();
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ class RiakStorageTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function setup()
|
||||
{
|
||||
if (PHP_MAJOR_VERSION >= 7) {
|
||||
$this->markTestSkipped('Riak extension is not available for PHP versions >= 7');
|
||||
}
|
||||
|
||||
$this->riak = $this->getMockBuilder('Riak\\Client')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
namespace Doctrine\Tests\KeyValueStore\Storage;
|
||||
|
||||
use Doctrine\KeyValueStore\Http\Client;
|
||||
use Doctrine\KeyValueStore\Http\Response;
|
||||
use Doctrine\KeyValueStore\Storage\WindowsAzureTable\AuthorizationSchema;
|
||||
use Doctrine\KeyValueStore\Storage\WindowsAzureTableStorage;
|
||||
|
||||
class WindowsAzureTableStorageTest extends AbstractStorageTestCase
|
||||
@@ -29,8 +31,14 @@ class WindowsAzureTableStorageTest extends AbstractStorageTestCase
|
||||
|
||||
protected function createStorage()
|
||||
{
|
||||
$this->client = $this->getMock('Doctrine\KeyValueStore\Http\Client');
|
||||
$auth = $this->getMock('Doctrine\KeyValueStore\Storage\WindowsAzureTable\AuthorizationSchema');
|
||||
$this->client = $this
|
||||
->getMockBuilder(Client::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$auth = $this
|
||||
->getMockBuilder(AuthorizationSchema::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$auth->expects($this->any())->method('signRequest')->will($this->returnValue('Authorization: SharedKeyLite testaccount1:uay+rilMVayH/SVI8X+a3fL8k/NxCnIePdyZSkqvydM='));
|
||||
|
||||
$storage = new WindowsAzureTableStorage(
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
if ( ! @include __DIR__ . '/../vendor/autoload.php') {
|
||||
if (! @include __DIR__ . '/../vendor/autoload.php') {
|
||||
die(<<<'EOT'
|
||||
You must set up the project dependencies, run the following commands:
|
||||
wget http://getcomposer.org/composer.phar
|
||||
|
||||
@@ -9,4 +9,5 @@ cd ext && ./install.sh && cd "$TRAVIS_BUILD_DIR"
|
||||
echo "extension=cassandra.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
|
||||
# PHP extensions
|
||||
yes | pecl install mongo
|
||||
yes | pecl install redis
|
||||
# PECL extensions
|
||||
echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
|
||||
Reference in New Issue
Block a user