File indexing completed on 2025-01-26 05:24:51

0001 <?php
0002 /**
0003  * LICENSE
0004  *
0005  * This source file is subject to the new BSD license that is bundled
0006  * with this package in the file LICENSE.txt.
0007  * It is also available through the world-wide-web at this URL:
0008  * http://framework.zend.com/license/new-bsd
0009  * If you did not receive a copy of the license and are unable to
0010  * obtain it through the world-wide-web, please send an email
0011  * to license@zend.com so we can send you a copy immediately.
0012  *
0013  * @category   Zend
0014  * @package    Zend_Cloud
0015  * @subpackage DocumentService
0016  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0017  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0018  */
0019 
0020 // require_once 'Zend/Cloud/DocumentService/Adapter/AbstractAdapter.php';
0021 // require_once 'Zend/Cloud/DocumentService/Adapter/SimpleDb/Query.php';
0022 // require_once 'Zend/Cloud/DocumentService/Exception.php';
0023 // require_once 'Zend/Service/Amazon/SimpleDb.php';
0024 // require_once 'Zend/Service/Amazon/SimpleDb/Attribute.php';
0025 
0026 /**
0027  * SimpleDB adapter for document service.
0028  *
0029  * @category   Zend
0030  * @package    Zend_Cloud
0031  * @subpackage DocumentService
0032  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0033  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0034  */
0035 class Zend_Cloud_DocumentService_Adapter_SimpleDb
0036     extends Zend_Cloud_DocumentService_Adapter_AbstractAdapter
0037 {
0038     /*
0039      * Options array keys for the SimpleDB adapter.
0040      */
0041     const AWS_ACCESS_KEY   = 'aws_accesskey';
0042     const AWS_SECRET_KEY   = 'aws_secretkey';
0043 
0044     const ITEM_NAME        = 'ItemName';
0045 
0046     const MERGE_OPTION     = "merge";
0047     const RETURN_DOCUMENTS = "return_documents";
0048 
0049     const DEFAULT_QUERY_CLASS = 'Zend_Cloud_DocumentService_Adapter_SimpleDb_Query';
0050 
0051 
0052     /**
0053      * SQS service instance.
0054      * @var Zend_Service_Amazon_SimpleDb
0055      */
0056     protected $_simpleDb;
0057 
0058     /**
0059      * Class to utilize for new query objects
0060      * @var string
0061      */
0062     protected $_queryClass = 'Zend_Cloud_DocumentService_Adapter_SimpleDb_Query';
0063 
0064     /**
0065      * Constructor
0066      *
0067      * @param  array|Zend_Config $options
0068      * @return void
0069      */
0070     public function __construct($options = array())
0071     {
0072         if ($options instanceof Zend_Config) {
0073             $options = $options->toArray();
0074         }
0075 
0076         if (!is_array($options)) {
0077             throw new Zend_Cloud_DocumentService_Exception('Invalid options provided to constructor');
0078         }
0079 
0080         $this->_simpleDb = new Zend_Service_Amazon_SimpleDb(
0081             $options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY]
0082         );
0083 
0084         if (isset($options[self::HTTP_ADAPTER])) {
0085             $this->_simpleDb->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
0086         }
0087 
0088         if (isset($options[self::DOCUMENT_CLASS])) {
0089             $this->setDocumentClass($options[self::DOCUMENT_CLASS]);
0090         }
0091 
0092         if (isset($options[self::DOCUMENTSET_CLASS])) {
0093             $this->setDocumentSetClass($options[self::DOCUMENTSET_CLASS]);
0094         }
0095 
0096         if (isset($options[self::QUERY_CLASS])) {
0097             $this->setQueryClass($options[self::QUERY_CLASS]);
0098         }
0099     }
0100 
0101     /**
0102      * Create collection.
0103      *
0104      * @param  string $name
0105      * @param  array  $options
0106      * @return void
0107      */
0108     public function createCollection($name, $options = null)
0109     {
0110         try {
0111             $this->_simpleDb->createDomain($name);
0112         } catch(Zend_Service_Amazon_Exception $e) {
0113             throw new Zend_Cloud_DocumentService_Exception('Error on domain creation: '.$e->getMessage(), $e->getCode(), $e);
0114         }
0115     }
0116 
0117     /**
0118      * Delete collection.
0119      *
0120      * @param  string $name
0121      * @param  array  $options
0122      * @return void
0123      */
0124     public function deleteCollection($name, $options = null)
0125     {
0126         try {
0127             $this->_simpleDb->deleteDomain($name);
0128         } catch(Zend_Service_Amazon_Exception $e) {
0129             throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
0130         }
0131     }
0132 
0133     /**
0134      * List collections.
0135      *
0136      * @param  array  $options
0137      * @return array
0138      */
0139     public function listCollections($options = null)
0140     {
0141         try {
0142             // TODO package this in Pages
0143             $domains = $this->_simpleDb->listDomains()->getData();
0144         } catch(Zend_Service_Amazon_Exception $e) {
0145             throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
0146         }
0147 
0148         return $domains;
0149     }
0150 
0151     /**
0152      * List documents
0153      *
0154      * Returns a key/value array of document names to document objects.
0155      *
0156      * @param  string $collectionName Name of collection for which to list documents
0157      * @param  array|null $options
0158      * @return Zend_Cloud_DocumentService_DocumentSet
0159      */
0160     public function listDocuments($collectionName, array $options = null)
0161     {
0162         $query = $this->select('*')->from($collectionName);
0163         $items = $this->query($collectionName, $query, $options);
0164         return $items;
0165     }
0166 
0167     /**
0168      * Insert document
0169      *
0170      * @param  string $collectionName Collection into which to insert document
0171      * @param  array|Zend_Cloud_DocumentService_Document $document
0172      * @param  array $options
0173      * @return void
0174      */
0175     public function insertDocument($collectionName, $document, $options = null)
0176     {
0177         if (is_array($document)) {
0178             $document =  $this->_getDocumentFromArray($document);
0179         }
0180 
0181         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
0182             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
0183         }
0184 
0185         try {
0186             $this->_simpleDb->putAttributes(
0187                 $collectionName,
0188                 $document->getID(),
0189                 $this->_makeAttributes($document->getID(), $document->getFields())
0190             );
0191         } catch(Zend_Service_Amazon_Exception $e) {
0192             throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
0193         }
0194     }
0195 
0196     /**
0197      * Replace an existing document with a new version
0198      *
0199      * @param  string $collectionName
0200      * @param  array|Zend_Cloud_DocumentService_Document $document
0201      * @param  array $options
0202      * @return void
0203      */
0204     public function replaceDocument($collectionName, $document, $options = null)
0205     {
0206         if (is_array($document)) {
0207             $document =  $this->_getDocumentFromArray($document);
0208         }
0209 
0210         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
0211             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
0212         }
0213 
0214         // Delete document first, then insert. PutAttributes always keeps any
0215         // fields not referenced in the payload, but present in the document
0216         $documentId = $document->getId();
0217         $fields     = $document->getFields();
0218         $docClass   = get_class($document);
0219         $this->deleteDocument($collectionName, $document, $options);
0220 
0221         $document   = new $docClass($fields, $documentId);
0222         $this->insertDocument($collectionName, $document);
0223     }
0224 
0225     /**
0226      * Update document. The new document replaces the existing document.
0227      *
0228      * Option 'merge' specifies to add all attributes (if true) or
0229      * specific attributes ("attr" => true) instead of replacing them.
0230      * By default, attributes are replaced.
0231      *
0232      * @param  string $collectionName
0233      * @param  mixed|Zend_Cloud_DocumentService_Document $documentId Document ID, adapter-dependent
0234      * @param  array|Zend_Cloud_DocumentService_Document $fieldset Set of fields to update
0235      * @param  array                   $options
0236      * @return boolean
0237      */
0238     public function updateDocument($collectionName, $documentId, $fieldset = null, $options = null)
0239     {
0240         if (null === $fieldset && $documentId instanceof Zend_Cloud_DocumentService_Document) {
0241             $fieldset   = $documentId->getFields();
0242             if (empty($documentId)) {
0243                 $documentId = $documentId->getId();
0244             }
0245         } elseif ($fieldset instanceof Zend_Cloud_DocumentService_Document) {
0246             if (empty($documentId)) {
0247                 $documentId = $fieldset->getId();
0248             }
0249             $fieldset = $fieldset->getFields();
0250         }
0251 
0252         $replace = array();
0253         if (empty($options[self::MERGE_OPTION])) {
0254             // no merge option - we replace all
0255             foreach ($fieldset as $key => $value) {
0256                 $replace[$key] = true;
0257             }
0258         } elseif (is_array($options[self::MERGE_OPTION])) {
0259             foreach ($fieldset as $key => $value) {
0260                 if (empty($options[self::MERGE_OPTION][$key])) {
0261                     // if there's merge key, we add it, otherwise we replace it
0262                     $replace[$key] = true;
0263                 }
0264             }
0265         } // otherwise $replace is empty - all is merged
0266 
0267         try {
0268             $this->_simpleDb->putAttributes(
0269                 $collectionName,
0270                 $documentId,
0271                 $this->_makeAttributes($documentId, $fieldset),
0272                 $replace
0273             );
0274         } catch(Zend_Service_Amazon_Exception $e) {
0275             throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
0276         }
0277         return true;
0278     }
0279 
0280     /**
0281      * Delete document.
0282      *
0283      * @param  string $collectionName Collection from which to delete document
0284      * @param  mixed  $document Document ID or Document object.
0285      * @param  array  $options
0286      * @return boolean
0287      */
0288     public function deleteDocument($collectionName, $document, $options = null)
0289     {
0290         if ($document instanceof Zend_Cloud_DocumentService_Document) {
0291             $document = $document->getId();
0292         }
0293         try {
0294             $this->_simpleDb->deleteAttributes($collectionName, $document);
0295         } catch(Zend_Service_Amazon_Exception $e) {
0296             throw new Zend_Cloud_DocumentService_Exception('Error on document deletion: '.$e->getMessage(), $e->getCode(), $e);
0297         }
0298         return true;
0299     }
0300 
0301     /**
0302      * Fetch single document by ID
0303      *
0304      * @param  string $collectionName Collection name
0305      * @param  mixed $documentId Document ID, adapter-dependent
0306      * @param  array $options
0307      * @return Zend_Cloud_DocumentService_Document
0308      */
0309     public function fetchDocument($collectionName, $documentId, $options = null)
0310     {
0311         try {
0312             $attributes = $this->_simpleDb->getAttributes($collectionName, $documentId);
0313             if ($attributes == false || count($attributes) == 0) {
0314                 return false;
0315             }
0316             return $this->_resolveAttributes($attributes, true);
0317         } catch(Zend_Service_Amazon_Exception $e) {
0318             throw new Zend_Cloud_DocumentService_Exception('Error on fetching document: '.$e->getMessage(), $e->getCode(), $e);
0319         }
0320     }
0321 
0322     /**
0323      * Query for documents stored in the document service. If a string is passed in
0324      * $query, the query string will be passed directly to the service.
0325      *
0326      * @param  string $collectionName Collection name
0327      * @param  string $query
0328      * @param  array $options
0329      * @return array Zend_Cloud_DocumentService_DocumentSet
0330      */
0331     public function query($collectionName, $query, $options = null)
0332     {
0333         $returnDocs = isset($options[self::RETURN_DOCUMENTS])
0334                     ? (bool) $options[self::RETURN_DOCUMENTS]
0335                     : true;
0336 
0337         try {
0338             if ($query instanceof Zend_Cloud_DocumentService_Adapter_SimpleDb_Query) {
0339                 $query = $query->assemble($collectionName);
0340             }
0341             $result = $this->_simpleDb->select($query);
0342         } catch(Zend_Service_Amazon_Exception $e) {
0343             throw new Zend_Cloud_DocumentService_Exception('Error on document query: '.$e->getMessage(), $e->getCode(), $e);
0344         }
0345 
0346         return $this->_getDocumentSetFromResultSet($result, $returnDocs);
0347     }
0348 
0349     /**
0350      * Create query statement
0351      *
0352      * @param  string $fields
0353      * @return Zend_Cloud_DocumentService_Adapter_SimpleDb_Query
0354      */
0355     public function select($fields = null)
0356     {
0357         $queryClass = $this->getQueryClass();
0358         if (!class_exists($queryClass)) {
0359             // require_once 'Zend/Loader.php';
0360             Zend_Loader::loadClass($queryClass);
0361         }
0362 
0363         $query = new $queryClass($this);
0364         $defaultClass = self::DEFAULT_QUERY_CLASS;
0365         if (!$query instanceof $defaultClass) {
0366             throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
0367         }
0368 
0369         $query->select($fields);
0370         return $query;
0371     }
0372 
0373     /**
0374      * Get the concrete service client
0375      *
0376      * @return Zend_Service_Amazon_SimpleDb
0377      */
0378     public function getClient()
0379     {
0380         return $this->_simpleDb;
0381     }
0382 
0383     /**
0384      * Convert array of key-value pairs to array of Amazon attributes
0385      *
0386      * @param string $name
0387      * @param array $attributes
0388      * @return array
0389      */
0390     protected function _makeAttributes($name, $attributes)
0391     {
0392         $result = array();
0393         foreach ($attributes as $key => $attr) {
0394             $result[] = new Zend_Service_Amazon_SimpleDb_Attribute($name, $key, $attr);
0395         }
0396         return $result;
0397     }
0398 
0399     /**
0400      * Convert array of Amazon attributes to array of key-value pairs
0401      *
0402      * @param array $attributes
0403      * @return array
0404      */
0405     protected function _resolveAttributes($attributes, $returnDocument = false)
0406     {
0407         $result = array();
0408         foreach ($attributes as $attr) {
0409             $value = $attr->getValues();
0410             if (count($value) == 0) {
0411                 $value = null;
0412             } elseif (count($value) == 1) {
0413                 $value = $value[0];
0414             }
0415             $result[$attr->getName()] = $value;
0416         }
0417 
0418         // Return as document object?
0419         if ($returnDocument) {
0420             $documentClass = $this->getDocumentClass();
0421             return new $documentClass($result, $attr->getItemName());
0422         }
0423 
0424         return $result;
0425     }
0426 
0427     /**
0428      * Create suitable document from array of fields
0429      *
0430      * @param array $document
0431      * @return Zend_Cloud_DocumentService_Document
0432      */
0433     protected function _getDocumentFromArray($document)
0434     {
0435         if (!isset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD])) {
0436             if (isset($document[self::ITEM_NAME])) {
0437                 $key = $document[self::ITEM_NAME];
0438                 unset($document[self::ITEM_NAME]);
0439             } else {
0440                 throw new Zend_Cloud_DocumentService_Exception('Fields array should contain the key field '.Zend_Cloud_DocumentService_Document::KEY_FIELD);
0441             }
0442         } else {
0443             $key = $document[Zend_Cloud_DocumentService_Document::KEY_FIELD];
0444             unset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD]);
0445         }
0446 
0447         $documentClass = $this->getDocumentClass();
0448         return new $documentClass($document, $key);
0449     }
0450 
0451     /**
0452      * Create a DocumentSet from a SimpleDb resultset
0453      *
0454      * @param  Zend_Service_Amazon_SimpleDb_Page $resultSet
0455      * @param  bool $returnDocs
0456      * @return Zend_Cloud_DocumentService_DocumentSet
0457      */
0458     protected function _getDocumentSetFromResultSet(Zend_Service_Amazon_SimpleDb_Page $resultSet, $returnDocs = true)
0459     {
0460         $docs = array();
0461         foreach ($resultSet->getData() as $item) {
0462             $docs[] = $this->_resolveAttributes($item, $returnDocs);
0463         }
0464 
0465         $setClass = $this->getDocumentSetClass();
0466         return new $setClass($docs);
0467     }
0468 }