web/lib/Zend/Cloud/DocumentService/Adapter/SimpleDb.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * LICENSE
       
     4  *
       
     5  * This source file is subject to the new BSD license that is bundled
       
     6  * with this package in the file LICENSE.txt.
       
     7  * It is also available through the world-wide-web at this URL:
       
     8  * http://framework.zend.com/license/new-bsd
       
     9  * If you did not receive a copy of the license and are unable to
       
    10  * obtain it through the world-wide-web, please send an email
       
    11  * to license@zend.com so we can send you a copy immediately.
       
    12  *
       
    13  * @category   Zend
       
    14  * @package    Zend_Cloud
       
    15  * @subpackage DocumentService
       
    16  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    17  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    18  */
       
    19 
       
    20 require_once 'Zend/Cloud/DocumentService/Adapter/AbstractAdapter.php';
       
    21 require_once 'Zend/Cloud/DocumentService/Adapter/SimpleDb/Query.php';
       
    22 require_once 'Zend/Cloud/DocumentService/Exception.php';
       
    23 require_once 'Zend/Service/Amazon/SimpleDb.php';
       
    24 require_once 'Zend/Service/Amazon/SimpleDb/Attribute.php';
       
    25 
       
    26 /**
       
    27  * SimpleDB adapter for document service.
       
    28  *
       
    29  * @category   Zend
       
    30  * @package    Zend_Cloud
       
    31  * @subpackage DocumentService
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_Cloud_DocumentService_Adapter_SimpleDb 
       
    36     extends Zend_Cloud_DocumentService_Adapter_AbstractAdapter
       
    37 {
       
    38     /*
       
    39      * Options array keys for the SimpleDB adapter.
       
    40      */
       
    41     const AWS_ACCESS_KEY   = 'aws_accesskey';
       
    42     const AWS_SECRET_KEY   = 'aws_secretkey';
       
    43     
       
    44     const ITEM_NAME        = 'ItemName';
       
    45     
       
    46     const MERGE_OPTION     = "merge";
       
    47     const RETURN_DOCUMENTS = "return_documents";
       
    48 
       
    49     const DEFAULT_QUERY_CLASS = 'Zend_Cloud_DocumentService_Adapter_SimpleDb_Query';
       
    50 
       
    51 
       
    52     /**
       
    53      * SQS service instance.
       
    54      * @var Zend_Service_Amazon_SimpleDb
       
    55      */
       
    56     protected $_simpleDb;
       
    57     
       
    58     /**
       
    59      * Class to utilize for new query objects
       
    60      * @var string
       
    61      */
       
    62     protected $_queryClass = 'Zend_Cloud_DocumentService_Adapter_SimpleDb_Query';
       
    63     
       
    64     /**
       
    65      * Constructor
       
    66      * 
       
    67      * @param  array|Zend_Config $options 
       
    68      * @return void
       
    69      */
       
    70     public function __construct($options = array()) 
       
    71     {
       
    72         if ($options instanceof Zend_Config) {
       
    73             $options = $options->toArray();
       
    74         }
       
    75 
       
    76         if (!is_array($options)) {
       
    77             throw new Zend_Cloud_DocumentService_Exception('Invalid options provided to constructor');
       
    78         }
       
    79 
       
    80         $this->_simpleDb = new Zend_Service_Amazon_SimpleDb(
       
    81             $options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY]
       
    82         );
       
    83 
       
    84         if (isset($options[self::HTTP_ADAPTER])) {
       
    85             $this->_sqs->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
       
    86         } 
       
    87 
       
    88         if (isset($options[self::DOCUMENT_CLASS])) {
       
    89             $this->setDocumentClass($options[self::DOCUMENT_CLASS]);
       
    90         }
       
    91 
       
    92         if (isset($options[self::DOCUMENTSET_CLASS])) {
       
    93             $this->setDocumentSetClass($options[self::DOCUMENTSET_CLASS]);
       
    94         }
       
    95 
       
    96         if (isset($options[self::QUERY_CLASS])) {
       
    97             $this->setQueryClass($options[self::QUERY_CLASS]);
       
    98         }
       
    99     }
       
   100 
       
   101     /**
       
   102      * Create collection.
       
   103      *
       
   104      * @param  string $name
       
   105      * @param  array  $options
       
   106      * @return void
       
   107      */
       
   108     public function createCollection($name, $options = null) 
       
   109     {
       
   110         try {
       
   111             $this->_simpleDb->createDomain($name);
       
   112         } catch(Zend_Service_Amazon_Exception $e) {
       
   113             throw new Zend_Cloud_DocumentService_Exception('Error on domain creation: '.$e->getMessage(), $e->getCode(), $e);
       
   114         }
       
   115     }
       
   116 
       
   117     /**
       
   118      * Delete collection.
       
   119      *
       
   120      * @param  string $name
       
   121      * @param  array  $options
       
   122      * @return void
       
   123      */
       
   124     public function deleteCollection($name, $options = null) 
       
   125     {
       
   126         try {
       
   127             $this->_simpleDb->deleteDomain($name);
       
   128         } catch(Zend_Service_Amazon_Exception $e) {
       
   129             throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
       
   130         }
       
   131     }
       
   132 
       
   133     /**
       
   134      * List collections.
       
   135      *
       
   136      * @param  array  $options
       
   137      * @return array
       
   138      */
       
   139     public function listCollections($options = null) 
       
   140     {
       
   141         try {
       
   142             // TODO package this in Pages
       
   143             $domains = $this->_simpleDb->listDomains()->getData();
       
   144         } catch(Zend_Service_Amazon_Exception $e) {
       
   145             throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
       
   146         }
       
   147 
       
   148         return $domains;
       
   149     }
       
   150 
       
   151     /**
       
   152      * List documents
       
   153      *
       
   154      * Returns a key/value array of document names to document objects.
       
   155      *
       
   156      * @param  string $collectionName Name of collection for which to list documents
       
   157      * @param  array|null $options
       
   158      * @return Zend_Cloud_DocumentService_DocumentSet
       
   159      */
       
   160     public function listDocuments($collectionName, array $options = null) 
       
   161     {
       
   162         $query = $this->select('*')->from($collectionName);
       
   163         $items = $this->query($collectionName, $query, $options);
       
   164         return $items;
       
   165     }
       
   166 
       
   167     /**
       
   168      * Insert document
       
   169      *
       
   170      * @param  string $collectionName Collection into which to insert document
       
   171      * @param  array|Zend_Cloud_DocumentService_Document $document
       
   172      * @param  array $options
       
   173      * @return void
       
   174      */
       
   175     public function insertDocument($collectionName, $document, $options = null)
       
   176     {
       
   177         if (is_array($document)) {
       
   178             $document =  $this->_getDocumentFromArray($document);
       
   179         } 
       
   180         
       
   181         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
       
   182             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
       
   183         }
       
   184         
       
   185         try {
       
   186             $this->_simpleDb->putAttributes(
       
   187                 $collectionName,
       
   188                 $document->getID(),
       
   189                 $this->_makeAttributes($document->getID(), $document->getFields())
       
   190             );
       
   191         } catch(Zend_Service_Amazon_Exception $e) {
       
   192             throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
       
   193         }
       
   194     }
       
   195 
       
   196     /**
       
   197      * Replace an existing document with a new version
       
   198      * 
       
   199      * @param  string $collectionName 
       
   200      * @param  array|Zend_Cloud_DocumentService_Document $document
       
   201      * @param  array $options 
       
   202      * @return void
       
   203      */
       
   204     public function replaceDocument($collectionName, $document, $options = null)
       
   205     {
       
   206         if (is_array($document)) {
       
   207             $document =  $this->_getDocumentFromArray($document);
       
   208         } 
       
   209         
       
   210         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
       
   211             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
       
   212         }
       
   213  
       
   214         // Delete document first, then insert. PutAttributes always keeps any
       
   215         // fields not referenced in the payload, but present in the document
       
   216         $documentId = $document->getId();
       
   217         $fields     = $document->getFields();
       
   218         $docClass   = get_class($document);
       
   219         $this->deleteDocument($collectionName, $document, $options);
       
   220 
       
   221         $document   = new $docClass($fields, $documentId);
       
   222         $this->insertDocument($collectionName, $document);
       
   223     }
       
   224     
       
   225     /**
       
   226      * Update document. The new document replaces the existing document.
       
   227      *
       
   228      * Option 'merge' specifies to add all attributes (if true) or
       
   229      * specific attributes ("attr" => true) instead of replacing them.
       
   230      * By default, attributes are replaced.   
       
   231      * 
       
   232      * @param  string $collectionName
       
   233      * @param  mixed|Zend_Cloud_DocumentService_Document $documentId Document ID, adapter-dependent
       
   234      * @param  array|Zend_Cloud_DocumentService_Document $fieldset Set of fields to update
       
   235      * @param  array                   $options
       
   236      * @return boolean
       
   237      */
       
   238     public function updateDocument($collectionName, $documentId, $fieldset = null, $options = null)
       
   239     {
       
   240         if (null === $fieldset && $documentId instanceof Zend_Cloud_DocumentService_Document) {
       
   241             $fieldset   = $documentId->getFields();
       
   242             if (empty($documentId)) {
       
   243                 $documentId = $documentId->getId();
       
   244             }
       
   245         } elseif ($fieldset instanceof Zend_Cloud_DocumentService_Document) {
       
   246             if (empty($documentId)) {
       
   247                 $documentId = $fieldset->getId();
       
   248             }
       
   249             $fieldset = $fieldset->getFields();
       
   250         }
       
   251         
       
   252         $replace = array();
       
   253         if (empty($options[self::MERGE_OPTION])) {
       
   254             // no merge option - we replace all
       
   255             foreach ($fieldset as $key => $value) {
       
   256                 $replace[$key] = true;
       
   257             }
       
   258         } elseif (is_array($options[self::MERGE_OPTION])) {
       
   259             foreach ($fieldset as $key => $value) {
       
   260                 if (empty($options[self::MERGE_OPTION][$key])) {
       
   261                     // if there's merge key, we add it, otherwise we replace it
       
   262                     $replace[$key] = true;
       
   263                 }
       
   264             }
       
   265         } // otherwise $replace is empty - all is merged
       
   266         
       
   267         try {
       
   268             $this->_simpleDb->putAttributes(
       
   269                 $collectionName,
       
   270                 $documentId,
       
   271                 $this->_makeAttributes($documentId, $fieldset),
       
   272                 $replace
       
   273             );
       
   274         } catch(Zend_Service_Amazon_Exception $e) {
       
   275             throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
       
   276         }
       
   277         return true;
       
   278     }
       
   279 
       
   280     /**
       
   281      * Delete document.
       
   282      *
       
   283      * @param  string $collectionName Collection from which to delete document
       
   284      * @param  mixed  $document Document ID or Document object.
       
   285      * @param  array  $options
       
   286      * @return boolean
       
   287      */
       
   288     public function deleteDocument($collectionName, $document, $options = null)
       
   289     {
       
   290         if ($document instanceof Zend_Cloud_DocumentService_Document) {
       
   291             $document = $document->getId();
       
   292         }
       
   293         try {
       
   294             $this->_simpleDb->deleteAttributes($collectionName, $document);
       
   295         } catch(Zend_Service_Amazon_Exception $e) {
       
   296             throw new Zend_Cloud_DocumentService_Exception('Error on document deletion: '.$e->getMessage(), $e->getCode(), $e);
       
   297         }
       
   298         return true;
       
   299     }
       
   300 
       
   301     /**
       
   302      * Fetch single document by ID
       
   303      * 
       
   304      * @param  string $collectionName Collection name
       
   305      * @param  mixed $documentId Document ID, adapter-dependent
       
   306      * @param  array $options
       
   307      * @return Zend_Cloud_DocumentService_Document
       
   308      */
       
   309     public function fetchDocument($collectionName, $documentId, $options = null)
       
   310     {
       
   311         try {
       
   312             $attributes = $this->_simpleDb->getAttributes($collectionName, $documentId);
       
   313             if ($attributes == false || count($attributes) == 0) {
       
   314                 return false;
       
   315             }
       
   316             return $this->_resolveAttributes($attributes, true);
       
   317         } catch(Zend_Service_Amazon_Exception $e) {
       
   318             throw new Zend_Cloud_DocumentService_Exception('Error on fetching document: '.$e->getMessage(), $e->getCode(), $e);
       
   319         }
       
   320     }
       
   321     
       
   322     /**
       
   323      * Query for documents stored in the document service. If a string is passed in
       
   324      * $query, the query string will be passed directly to the service.
       
   325      *
       
   326      * @param  string $collectionName Collection name
       
   327      * @param  string $query
       
   328      * @param  array $options
       
   329      * @return array Zend_Cloud_DocumentService_DocumentSet
       
   330      */
       
   331     public function query($collectionName, $query, $options = null)
       
   332     {
       
   333         $returnDocs = isset($options[self::RETURN_DOCUMENTS])
       
   334                     ? (bool) $options[self::RETURN_DOCUMENTS]
       
   335                     : true;
       
   336 
       
   337         try {
       
   338             if ($query instanceof Zend_Cloud_DocumentService_Adapter_SimpleDb_Query) {
       
   339                 $query = $query->assemble($collectionName);
       
   340             }
       
   341             $result = $this->_simpleDb->select($query);
       
   342         } catch(Zend_Service_Amazon_Exception $e) {
       
   343             throw new Zend_Cloud_DocumentService_Exception('Error on document query: '.$e->getMessage(), $e->getCode(), $e);
       
   344         }
       
   345 
       
   346         return $this->_getDocumentSetFromResultSet($result, $returnDocs);
       
   347     }
       
   348 
       
   349     /**
       
   350      * Create query statement
       
   351      * 
       
   352      * @param  string $fields
       
   353      * @return Zend_Cloud_DocumentService_Adapter_SimpleDb_Query
       
   354      */
       
   355     public function select($fields = null)
       
   356     {
       
   357         $queryClass = $this->getQueryClass();
       
   358         if (!class_exists($queryClass)) {
       
   359             require_once 'Zend/Loader.php';
       
   360             Zend_Loader::loadClass($queryClass);
       
   361         }
       
   362 
       
   363         $query = new $queryClass($this);
       
   364         $defaultClass = self::DEFAULT_QUERY_CLASS;
       
   365         if (!$query instanceof $defaultClass) {
       
   366             throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
       
   367         }
       
   368 
       
   369         $query->select($fields);
       
   370         return $query;        
       
   371     }
       
   372     
       
   373     /**
       
   374      * Get the concrete service client
       
   375      *
       
   376      * @return Zend_Service_Amazon_SimpleDb
       
   377      */
       
   378     public function getClient()
       
   379     {
       
   380         return $this->_simpleDb;
       
   381     }
       
   382     
       
   383     /**
       
   384      * Convert array of key-value pairs to array of Amazon attributes
       
   385      * 
       
   386      * @param string $name
       
   387      * @param array $attributes
       
   388      * @return array
       
   389      */
       
   390     protected function _makeAttributes($name, $attributes)
       
   391     {
       
   392         $result = array();
       
   393         foreach ($attributes as $key => $attr) {
       
   394             $result[] = new Zend_Service_Amazon_SimpleDb_Attribute($name, $key, $attr);
       
   395         }
       
   396         return $result;
       
   397     }
       
   398     
       
   399     /**
       
   400      * Convert array of Amazon attributes to array of key-value pairs 
       
   401      * 
       
   402      * @param array $attributes
       
   403      * @return array
       
   404      */
       
   405     protected function _resolveAttributes($attributes, $returnDocument = false)
       
   406     {
       
   407         $result = array();
       
   408         foreach ($attributes as $attr) {
       
   409             $value = $attr->getValues();
       
   410             if (count($value) == 0) {
       
   411                 $value = null;
       
   412             } elseif (count($value) == 1) {
       
   413                 $value = $value[0];
       
   414             }
       
   415             $result[$attr->getName()] = $value;
       
   416         }
       
   417 
       
   418         // Return as document object?
       
   419         if ($returnDocument) {
       
   420             $documentClass = $this->getDocumentClass();
       
   421             return new $documentClass($result, $attr->getItemName());
       
   422         }
       
   423 
       
   424         return $result;
       
   425     }
       
   426     
       
   427     /**
       
   428      * Create suitable document from array of fields
       
   429      * 
       
   430      * @param array $document
       
   431      * @return Zend_Cloud_DocumentService_Document
       
   432      */
       
   433     protected function _getDocumentFromArray($document)
       
   434     {
       
   435         if (!isset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD])) {
       
   436             if (isset($document[self::ITEM_NAME])) {
       
   437                 $key = $document[self::ITEM_NAME];
       
   438                 unset($document[self::ITEM_NAME]);
       
   439             } else {
       
   440                 throw new Zend_Cloud_DocumentService_Exception('Fields array should contain the key field '.Zend_Cloud_DocumentService_Document::KEY_FIELD);
       
   441             }
       
   442         } else {
       
   443             $key = $document[Zend_Cloud_DocumentService_Document::KEY_FIELD];
       
   444             unset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD]);
       
   445         }
       
   446 
       
   447         $documentClass = $this->getDocumentClass();
       
   448         return new $documentClass($document, $key);
       
   449     }
       
   450 
       
   451     /**
       
   452      * Create a DocumentSet from a SimpleDb resultset
       
   453      * 
       
   454      * @param  Zend_Service_Amazon_SimpleDb_Page $resultSet 
       
   455      * @param  bool $returnDocs 
       
   456      * @return Zend_Cloud_DocumentService_DocumentSet
       
   457      */
       
   458     protected function _getDocumentSetFromResultSet(Zend_Service_Amazon_SimpleDb_Page $resultSet, $returnDocs = true)
       
   459     {
       
   460         $docs = array();
       
   461         foreach ($resultSet->getData() as $item) {
       
   462             $docs[] = $this->_resolveAttributes($item, $returnDocs);
       
   463         }
       
   464 
       
   465         $setClass = $this->getDocumentSetClass();
       
   466         return new $setClass($docs);
       
   467     }
       
   468 }