web/lib/Zend/Cloud/DocumentService/Adapter/WindowsAzure.php
changeset 807 877f952ae2bd
parent 207 621fa6caec0c
child 1230 68c69c656a2c
equal deleted inserted replaced
805:5e7a0fedabdf 807:877f952ae2bd
    11  * to license@zend.com so we can send you a copy immediately.
    11  * to license@zend.com so we can send you a copy immediately.
    12  *
    12  *
    13  * @category   Zend
    13  * @category   Zend
    14  * @package    Zend_Cloud
    14  * @package    Zend_Cloud
    15  * @subpackage DocumentService
    15  * @subpackage DocumentService
    16  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    16  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    17  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    17  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    18  */
    18  */
    19 
    19 
    20 require_once 'Zend/Cloud/DocumentService/Adapter/AbstractAdapter.php';
    20 require_once 'Zend/Cloud/DocumentService/Adapter/AbstractAdapter.php';
    21 require_once 'Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php';
    21 require_once 'Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php';
    27  * SimpleDB adapter for document service.
    27  * SimpleDB adapter for document service.
    28  *
    28  *
    29  * @category   Zend
    29  * @category   Zend
    30  * @package    Zend_Cloud
    30  * @package    Zend_Cloud
    31  * @subpackage DocumentService
    31  * @subpackage DocumentService
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    32  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    34  */
    34  */
    35 class Zend_Cloud_DocumentService_Adapter_WindowsAzure 
    35 class Zend_Cloud_DocumentService_Adapter_WindowsAzure
    36     extends Zend_Cloud_DocumentService_Adapter_AbstractAdapter
    36     extends Zend_Cloud_DocumentService_Adapter_AbstractAdapter
    37 {
    37 {
    38     /*
    38     /*
    39      * Options array keys for the Azure adapter.
    39      * Options array keys for the Azure adapter.
    40      */
    40      */
    48 
    48 
    49     const PARTITION_KEY         = 'PartitionKey';
    49     const PARTITION_KEY         = 'PartitionKey';
    50     const ROW_KEY               = 'RowKey';
    50     const ROW_KEY               = 'RowKey';
    51     const VERIFY_ETAG           = "verify_etag";
    51     const VERIFY_ETAG           = "verify_etag";
    52     const TIMESTAMP_KEY         = "Timestamp";
    52     const TIMESTAMP_KEY         = "Timestamp";
    53     
    53 
    54     const DEFAULT_HOST          = Zend_Service_WindowsAzure_Storage::URL_CLOUD_TABLE;
    54     const DEFAULT_HOST          = Zend_Service_WindowsAzure_Storage::URL_CLOUD_TABLE;
    55     const DEFAULT_QUERY_CLASS   = 'Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query';
    55     const DEFAULT_QUERY_CLASS   = 'Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query';
    56 
    56 
    57     /**
    57     /**
    58      * Azure  service instance.
    58      * Azure  service instance.
    59      * 
    59      *
    60      * @var Zend_Service_WindowsAzure_Storage_Table
    60      * @var Zend_Service_WindowsAzure_Storage_Table
    61      */
    61      */
    62     protected $_storageClient;
    62     protected $_storageClient;
    63 
    63 
    64     /**
    64     /**
    65      * Class to utilize for new query objects
    65      * Class to utilize for new query objects
    66      * 
    66      *
    67      * @var string
    67      * @var string
    68      */
    68      */
    69     protected $_queryClass = 'Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query';
    69     protected $_queryClass = 'Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query';
    70 
    70 
    71     /**
    71     /**
    74      */
    74      */
    75     protected $_defaultPartitionKey;
    75     protected $_defaultPartitionKey;
    76 
    76 
    77     /**
    77     /**
    78      * Constructor
    78      * Constructor
    79      * 
    79      *
    80      * @param array $options 
    80      * @param array $options
    81      * @return void
    81      * @return void
    82      */
    82      */
    83     public function __construct($options = array()) 
    83     public function __construct($options = array())
    84     {
    84     {
    85         if ($options instanceof Zend_Config) {
    85         if ($options instanceof Zend_Config) {
    86             $options = $options->toArray();
    86             $options = $options->toArray();
    87         }
    87         }
    88 
    88 
   140         }
   140         }
   141     }
   141     }
   142 
   142 
   143     /**
   143     /**
   144      * Set the default partition key
   144      * Set the default partition key
   145      * 
   145      *
   146      * @param  string $key 
   146      * @param  string $key
   147      * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure
   147      * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure
   148      */
   148      */
   149     public function setDefaultPartitionKey($key)
   149     public function setDefaultPartitionKey($key)
   150     {
   150     {
   151         $this->_validateKey($key);
   151         $this->_validateKey($key);
   153         return $this;
   153         return $this;
   154     }
   154     }
   155 
   155 
   156     /**
   156     /**
   157      * Retrieve default partition key
   157      * Retrieve default partition key
   158      * 
   158      *
   159      * @return null|string
   159      * @return null|string
   160      */
   160      */
   161     public function getDefaultPartitionKey()
   161     public function getDefaultPartitionKey()
   162     {
   162     {
   163         return $this->_defaultPartitionKey;
   163         return $this->_defaultPartitionKey;
   168      *
   168      *
   169      * @param  string $name
   169      * @param  string $name
   170      * @param  array  $options
   170      * @param  array  $options
   171      * @return boolean
   171      * @return boolean
   172      */
   172      */
   173     public function createCollection($name, $options = null) 
   173     public function createCollection($name, $options = null)
   174     {
   174     {
   175         if (!preg_match('/^[A-Za-z][A-Za-z0-9]{2,}$/', $name)) {
   175         if (!preg_match('/^[A-Za-z][A-Za-z0-9]{2,}$/', $name)) {
   176             throw new Zend_Cloud_DocumentService_Exception('Invalid collection name; Windows Azure collection names must consist of alphanumeric characters only, and be at least 3 characters long');
   176             throw new Zend_Cloud_DocumentService_Exception('Invalid collection name; Windows Azure collection names must consist of alphanumeric characters only, and be at least 3 characters long');
   177         }
   177         }
   178         try {
   178         try {
   190      *
   190      *
   191      * @param  string $name
   191      * @param  string $name
   192      * @param  array  $options
   192      * @param  array  $options
   193      * @return boolean
   193      * @return boolean
   194      */
   194      */
   195     public function deleteCollection($name, $options = null) 
   195     public function deleteCollection($name, $options = null)
   196     {
   196     {
   197         try {
   197         try {
   198             $this->_storageClient->deleteTable($name);
   198             $this->_storageClient->deleteTable($name);
   199         } catch(Zend_Service_WindowsAzure_Exception $e) {
   199         } catch(Zend_Service_WindowsAzure_Exception $e) {
   200             if (strpos($e->getMessage(), "does not exist") === false) {
   200             if (strpos($e->getMessage(), "does not exist") === false) {
   208      * List collections.
   208      * List collections.
   209      *
   209      *
   210      * @param  array  $options
   210      * @param  array  $options
   211      * @return array
   211      * @return array
   212      */
   212      */
   213     public function listCollections($options = null) 
   213     public function listCollections($options = null)
   214     {
   214     {
   215         try {
   215         try {
   216             $tables = $this->_storageClient->listTables();
   216             $tables = $this->_storageClient->listTables();
   217             $restables = array();
   217             $restables = array();
   218             foreach ($tables as $table) {
   218             foreach ($tables as $table) {
   226         return $tables;
   226         return $tables;
   227     }
   227     }
   228 
   228 
   229     /**
   229     /**
   230      * Create suitable document from array of fields
   230      * Create suitable document from array of fields
   231      * 
   231      *
   232      * @param  array $document
   232      * @param  array $document
   233      * @param  null|string $collectionName Collection to which this document belongs
   233      * @param  null|string $collectionName Collection to which this document belongs
   234      * @return Zend_Cloud_DocumentService_Document
   234      * @return Zend_Cloud_DocumentService_Document
   235      */
   235      */
   236     protected function _getDocumentFromArray($document, $collectionName = null)
   236     protected function _getDocumentFromArray($document, $collectionName = null)
   255         }
   255         }
   256 
   256 
   257         $documentClass = $this->getDocumentClass();
   257         $documentClass = $this->getDocumentClass();
   258         return new $documentClass($document, $key);
   258         return new $documentClass($document, $key);
   259     }
   259     }
   260     
   260 
   261     /**
   261     /**
   262      * List all documents in a collection
   262      * List all documents in a collection
   263      * 
   263      *
   264      * @param  string $collectionName 
   264      * @param  string $collectionName
   265      * @param  null|array $options 
   265      * @param  null|array $options
   266      * @return Zend_Cloud_DocumentService_DocumentSet
   266      * @return Zend_Cloud_DocumentService_DocumentSet
   267      */
   267      */
   268     public function listDocuments($collectionName, array $options = null)
   268     public function listDocuments($collectionName, array $options = null)
   269     {
   269     {
   270         $select = $this->select()->from($collectionName);
   270         $select = $this->select()->from($collectionName);
   280      */
   280      */
   281     public function insertDocument($collectionName, $document, $options = null)
   281     public function insertDocument($collectionName, $document, $options = null)
   282     {
   282     {
   283         if (is_array($document)) {
   283         if (is_array($document)) {
   284             $document =  $this->_getDocumentFromArray($document, $collectionName);
   284             $document =  $this->_getDocumentFromArray($document, $collectionName);
   285         } 
   285         }
   286         
   286 
   287         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
   287         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
   288             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
   288             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
   289         }
   289         }
   290         
   290 
   291         $key = $this->_validateDocumentId($document->getId(), $collectionName);
   291         $key = $this->_validateDocumentId($document->getId(), $collectionName);
   292         $document->setId($key);
   292         $document->setId($key);
   293         
   293 
   294         $this->_validateCompositeKey($key);
   294         $this->_validateCompositeKey($key);
   295         $this->_validateFields($document);
   295         $this->_validateFields($document);
   296         try {
   296         try {
   297         
   297 
   298             $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
   298             $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
   299             $entity->setAzureValues($document->getFields(), true);
   299             $entity->setAzureValues($document->getFields(), true);
   300             $this->_storageClient->insertEntity($collectionName, $entity);
   300             $this->_storageClient->insertEntity($collectionName, $entity);
   301         } catch(Zend_Service_WindowsAzure_Exception $e) {
   301         } catch(Zend_Service_WindowsAzure_Exception $e) {
   302             throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
   302             throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
   303         }
   303         }
   304     }
   304     }
   305 
   305 
   306     /**
   306     /**
   307      * Replace document. 
   307      * Replace document.
   308      * 
   308      *
   309      * The new document replaces the existing document.
   309      * The new document replaces the existing document.
   310      *
   310      *
   311      * @param  Zend_Cloud_DocumentService_Document $document
   311      * @param  Zend_Cloud_DocumentService_Document $document
   312      * @param  array                         $options
   312      * @param  array                         $options
   313      * @return boolean
   313      * @return boolean
   314      */
   314      */
   315     public function replaceDocument($collectionName, $document, $options = null)
   315     public function replaceDocument($collectionName, $document, $options = null)
   316     {
   316     {
   317         if (is_array($document)) {
   317         if (is_array($document)) {
   318             $document = $this->_getDocumentFromArray($document, $collectionName);
   318             $document = $this->_getDocumentFromArray($document, $collectionName);
   319         } 
   319         }
   320         
   320 
   321         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
   321         if (!$document instanceof Zend_Cloud_DocumentService_Document) {
   322             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
   322             throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
   323         }
   323         }
   324         
   324 
   325         $key = $this->_validateDocumentId($document->getId(), $collectionName);
   325         $key = $this->_validateDocumentId($document->getId(), $collectionName);
   326         $this->_validateFields($document);
   326         $this->_validateFields($document);
   327         try {
   327         try {
   328             $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
   328             $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
   329             $entity->setAzureValues($document->getFields(), true);
   329             $entity->setAzureValues($document->getFields(), true);
   330             if (isset($options[self::VERIFY_ETAG])) {
   330             if (isset($options[self::VERIFY_ETAG])) {
   331                 $entity->setEtag($options[self::VERIFY_ETAG]);
   331                 $entity->setEtag($options[self::VERIFY_ETAG]);
   332             }
   332             }
   333             
   333 
   334             $this->_storageClient->updateEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
   334             $this->_storageClient->updateEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
   335         } catch(Zend_Service_WindowsAzure_Exception $e) {
   335         } catch(Zend_Service_WindowsAzure_Exception $e) {
   336             throw new Zend_Cloud_DocumentService_Exception('Error on document replace: '.$e->getMessage(), $e->getCode(), $e);
   336             throw new Zend_Cloud_DocumentService_Exception('Error on document replace: '.$e->getMessage(), $e->getCode(), $e);
   337         }
   337         }
   338     }
   338     }
   339 
   339 
   340     /**
   340     /**
   341      * Update document. 
   341      * Update document.
   342      * 
   342      *
   343      * The new document is merged the existing document.
   343      * The new document is merged the existing document.
   344      *
   344      *
   345      * @param  string $collectionName
   345      * @param  string $collectionName
   346      * @param  mixed|Zend_Cloud_DocumentService_Document $documentId Document identifier or document contaiing updates
   346      * @param  mixed|Zend_Cloud_DocumentService_Document $documentId Document identifier or document contaiing updates
   347      * @param  null|array|Zend_Cloud_DocumentService_Document Fields to update (or new fields))
   347      * @param  null|array|Zend_Cloud_DocumentService_Document Fields to update (or new fields))
   373 
   373 
   374             $entity->setAzureValues($fieldset, true);
   374             $entity->setAzureValues($fieldset, true);
   375             if (isset($options[self::VERIFY_ETAG])) {
   375             if (isset($options[self::VERIFY_ETAG])) {
   376                 $entity->setEtag($options[self::VERIFY_ETAG]);
   376                 $entity->setEtag($options[self::VERIFY_ETAG]);
   377             }
   377             }
   378             
   378 
   379             $this->_storageClient->mergeEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
   379             $this->_storageClient->mergeEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
   380         } catch(Zend_Service_WindowsAzure_Exception $e) {
   380         } catch(Zend_Service_WindowsAzure_Exception $e) {
   381             throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
   381             throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
   382         }
   382         }
   383     }
   383     }
   384     
   384 
   385     /**
   385     /**
   386      * Delete document.
   386      * Delete document.
   387      *
   387      *
   388      * @param  mixed  $document Document ID or Document object.
   388      * @param  mixed  $document Document ID or Document object.
   389      * @param  array  $options
   389      * @param  array  $options
   410         }
   410         }
   411     }
   411     }
   412 
   412 
   413     /**
   413     /**
   414      * Fetch single document by ID
   414      * Fetch single document by ID
   415      * 
   415      *
   416      * @param  string $collectionName Collection name
   416      * @param  string $collectionName Collection name
   417      * @param  mixed $documentId Document ID, adapter-dependent
   417      * @param  mixed $documentId Document ID, adapter-dependent
   418      * @param  array $options
   418      * @param  array $options
   419      * @return Zend_Cloud_DocumentService_Document
   419      * @return Zend_Cloud_DocumentService_Document
   420      */
   420      */
   430                 return false;
   430                 return false;
   431             }
   431             }
   432             throw new Zend_Cloud_DocumentService_Exception('Error on document fetch: '.$e->getMessage(), $e->getCode(), $e);
   432             throw new Zend_Cloud_DocumentService_Exception('Error on document fetch: '.$e->getMessage(), $e->getCode(), $e);
   433         }
   433         }
   434     }
   434     }
   435     
   435 
   436     /**
   436     /**
   437      * Query for documents stored in the document service. If a string is passed in
   437      * Query for documents stored in the document service. If a string is passed in
   438      * $query, the query string will be passed directly to the service.
   438      * $query, the query string will be passed directly to the service.
   439      *
   439      *
   440      * @param  string $collectionName Collection name
   440      * @param  string $collectionName Collection name
   464         }
   464         }
   465 
   465 
   466         $setClass = $this->getDocumentSetClass();
   466         $setClass = $this->getDocumentSetClass();
   467         return new $setClass($resultSet);
   467         return new $setClass($resultSet);
   468     }
   468     }
   469     
   469 
   470     /**
   470     /**
   471      * Create query statement
   471      * Create query statement
   472      *
   472      *
   473      * @return Zend_Cloud_DocumentService_Query
   473      * @return Zend_Cloud_DocumentService_Query
   474      */
   474      */
   485         if (!$query instanceof $defaultClass) {
   485         if (!$query instanceof $defaultClass) {
   486             throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
   486             throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
   487         }
   487         }
   488 
   488 
   489         $query->select($fields);
   489         $query->select($fields);
   490         return $query;        
   490         return $query;
   491     }
   491     }
   492     
   492 
   493     /**
   493     /**
   494      * Get the concrete service client
   494      * Get the concrete service client
   495      *
   495      *
   496      * @return Zend_Service_WindowsAzure_Storage_Table
   496      * @return Zend_Service_WindowsAzure_Storage_Table
   497      */
   497      */
   498     public function getClient()
   498     public function getClient()
   499     {
   499     {
   500         return $this->_storageClient;
   500         return $this->_storageClient;
   501     }
   501     }
   502     
   502 
   503     /**
   503     /**
   504      * Resolve table values to attributes
   504      * Resolve table values to attributes
   505      * 
   505      *
   506      * @param  Zend_Service_WindowsAzure_Storage_TableEntity $entity 
   506      * @param  Zend_Service_WindowsAzure_Storage_TableEntity $entity
   507      * @return array
   507      * @return array
   508      */
   508      */
   509     protected function _resolveAttributes(Zend_Service_WindowsAzure_Storage_TableEntity $entity)
   509     protected function _resolveAttributes(Zend_Service_WindowsAzure_Storage_TableEntity $entity)
   510     {
   510     {
   511         $result = array();
   511         $result = array();
   512         foreach ($entity->getAzureValues() as $attr) {
   512         foreach ($entity->getAzureValues() as $attr) {
   513             $result[$attr->Name] = $attr->Value;
   513             $result[$attr->Name] = $attr->Value;
   514         }
   514         }
   515         return $result;
   515         return $result;
   516     }
   516     }
   517     
   517 
   518 
   518 
   519     /**
   519     /**
   520      * Validate a partition or row key
   520      * Validate a partition or row key
   521      * 
   521      *
   522      * @param  string $key 
   522      * @param  string $key
   523      * @return void
   523      * @return void
   524      * @throws Zend_Cloud_DocumentService_Exception
   524      * @throws Zend_Cloud_DocumentService_Exception
   525      */
   525      */
   526     protected function _validateKey($key)
   526     protected function _validateKey($key)
   527     {
   527     {
   530         }
   530         }
   531     }
   531     }
   532 
   532 
   533     /**
   533     /**
   534      * Validate a composite key
   534      * Validate a composite key
   535      * 
   535      *
   536      * @param  array $key 
   536      * @param  array $key
   537      * @return throws Zend_Cloud_DocumentService_Exception
   537      * @return throws Zend_Cloud_DocumentService_Exception
   538      */
   538      */
   539     protected function _validateCompositeKey(array $key)
   539     protected function _validateCompositeKey(array $key)
   540     {
   540     {
   541         if (2 != count($key)) {
   541         if (2 != count($key)) {
   547     }
   547     }
   548 
   548 
   549     /**
   549     /**
   550      * Validate a document identifier
   550      * Validate a document identifier
   551      *
   551      *
   552      * If the identifier is an array containing a valid partition and row key, 
   552      * If the identifier is an array containing a valid partition and row key,
   553      * returns it. If the identifier is a string:
   553      * returns it. If the identifier is a string:
   554      * - if a default partition key is present, it creates an identifier using 
   554      * - if a default partition key is present, it creates an identifier using
   555      *   that and the provided document ID
   555      *   that and the provided document ID
   556      * - if a collection name is provided, it will use that for the partition key
   556      * - if a collection name is provided, it will use that for the partition key
   557      * - otherwise, it's invalid
   557      * - otherwise, it's invalid
   558      * 
   558      *
   559      * @param  array|string $documentId 
   559      * @param  array|string $documentId
   560      * @param  null|string $collectionName 
   560      * @param  null|string $collectionName
   561      * @return array
   561      * @return array
   562      * @throws Zend_Cloud_DocumentService_Exception
   562      * @throws Zend_Cloud_DocumentService_Exception
   563      */
   563      */
   564     protected function _validateDocumentId($documentId, $collectionName = false)
   564     protected function _validateDocumentId($documentId, $collectionName = false)
   565     {
   565     {
   583     }
   583     }
   584 
   584 
   585     /**
   585     /**
   586      * Validate a document's fields for well-formedness
   586      * Validate a document's fields for well-formedness
   587      *
   587      *
   588      * Since Azure uses Atom, and fieldnames are included as part of XML 
   588      * Since Azure uses Atom, and fieldnames are included as part of XML
   589      * element tag names, the field names must be valid XML names.
   589      * element tag names, the field names must be valid XML names.
   590      *
   590      *
   591      * @param  Zend_Cloud_DocumentService_Document|array $document
   591      * @param  Zend_Cloud_DocumentService_Document|array $document
   592      * @return void
   592      * @return void
   593      * @throws Zend_Cloud_DocumentService_Exception
   593      * @throws Zend_Cloud_DocumentService_Exception
   606     }
   606     }
   607 
   607 
   608     /**
   608     /**
   609      * Validate an individual field name for well-formedness
   609      * Validate an individual field name for well-formedness
   610      *
   610      *
   611      * Since Azure uses Atom, and fieldnames are included as part of XML 
   611      * Since Azure uses Atom, and fieldnames are included as part of XML
   612      * element tag names, the field names must be valid XML names.
   612      * element tag names, the field names must be valid XML names.
   613      *
   613      *
   614      * While we could potentially normalize names, this could also lead to 
   614      * While we could potentially normalize names, this could also lead to
   615      * conflict with other field names -- which we should avoid. As such,
   615      * conflict with other field names -- which we should avoid. As such,
   616      * invalid field names will raise an exception.
   616      * invalid field names will raise an exception.
   617      *
   617      *
   618      * @param  string $key
   618      * @param  string $key
   619      * @return void
   619      * @return void