From 04fb75b6a765975ad885de04cc9005e7bdff42c0 Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Thu, 5 Dec 2013 13:55:35 +0200 Subject: [PATCH] File retrieve methods added to Mongo File Active Record. --- extensions/mongo/Query.php | 57 +++++++++++-------- extensions/mongo/file/ActiveRecord.php | 63 ++++++++++++++++++++- extensions/mongo/file/Query.php | 59 ++++++++------------ .../extensions/mongo/file/ActiveRecordTest.php | 64 ++++++++++++++++++++++ 4 files changed, 185 insertions(+), 58 deletions(-) diff --git a/extensions/mongo/Query.php b/extensions/mongo/Query.php index 8c46479..5f313f4 100644 --- a/extensions/mongo/Query.php +++ b/extensions/mongo/Query.php @@ -129,33 +129,13 @@ class Query extends Component implements QueryInterface * @throws Exception on failure. * @return array|boolean result. */ - protected function fetchRows(\MongoCursor $cursor, $all = true, $indexBy = null) + protected function fetchRows($cursor, $all = true, $indexBy = null) { $token = 'Querying: ' . Json::encode($cursor->info()); Yii::info($token, __METHOD__); try { Yii::beginProfile($token, __METHOD__); - $result = []; - if ($all) { - foreach ($cursor as $row) { - if ($indexBy !== null) { - if (is_string($indexBy)) { - $key = $row[$indexBy]; - } else { - $key = call_user_func($indexBy, $row); - } - $result[$key] = $row; - } else { - $result[] = $row; - } - } - } else { - if ($cursor->hasNext()) { - $result = $cursor->getNext(); - } else { - $result = false; - } - } + $result = $this->fetchRowsInternal($cursor, $all, $indexBy); Yii::endProfile($token, __METHOD__); return $result; } catch (\Exception $e) { @@ -165,6 +145,39 @@ class Query extends Component implements QueryInterface } /** + * @param \MongoCursor $cursor Mongo cursor instance to fetch data from. + * @param boolean $all whether to fetch all rows or only first one. + * @param string|callable $indexBy value to index by. + * @return array|boolean result. + * @see Query::fetchRows() + */ + protected function fetchRowsInternal($cursor, $all, $indexBy) + { + $result = []; + if ($all) { + foreach ($cursor as $row) { + if ($indexBy !== null) { + if (is_string($indexBy)) { + $key = $row[$indexBy]; + } else { + $key = call_user_func($indexBy, $row); + } + $result[$key] = $row; + } else { + $result[] = $row; + } + } + } else { + if ($cursor->hasNext()) { + $result = $cursor->getNext(); + } else { + $result = false; + } + } + return $result; + } + + /** * Executes the query and returns all results as an array. * @param Connection $db the Mongo connection used to execute the query. * If this parameter is not given, the `mongo` application component will be used. diff --git a/extensions/mongo/file/ActiveRecord.php b/extensions/mongo/file/ActiveRecord.php index bbdf10d..fd9367b 100644 --- a/extensions/mongo/file/ActiveRecord.php +++ b/extensions/mongo/file/ActiveRecord.php @@ -205,7 +205,7 @@ class ActiveRecord extends \yii\mongo\ActiveRecord /** * Returns the associated file content. * @return null|string file content. - * @throws \yii\base\InvalidParamException on invalid file value. + * @throws \yii\base\InvalidParamException on invalid file attribute value. */ public function getFileContent() { @@ -227,6 +227,67 @@ class ActiveRecord extends \yii\mongo\ActiveRecord } elseif (is_string($file)) { if (file_exists($file)) { return file_get_contents($file); + } else { + throw new InvalidParamException("File '{$file}' does not exist."); + } + } else { + throw new InvalidParamException('Unsupported type of "file" attribute.'); + } + } + + /** + * Writes the the internal file content into the given filename. + * @param string $filename full filename to be written. + * @return boolean whether the operation was successful. + * @throws \yii\base\InvalidParamException on invalid file attribute value. + */ + public function writeFile($filename) + { + $file = $this->getAttribute('file'); + if (empty($file) && !$this->getIsNewRecord()) { + $file = $this->refreshFile(); + } + if (empty($file)) { + throw new InvalidParamException('There is no file associated with this object.'); + } elseif ($file instanceof \MongoGridFSFile) { + return ($file->write($filename) == $file->getSize()); + } elseif ($file instanceof UploadedFile) { + return copy($file->tempName, $filename); + } elseif (is_string($file)) { + if (file_exists($file)) { + return copy($file, $filename); + } else { + throw new InvalidParamException("File '{$file}' does not exist."); + } + } else { + throw new InvalidParamException('Unsupported type of "file" attribute.'); + } + } + + /** + * This method returns a stream resource that can be used with all file functions in PHP, + * which deal with reading files. The contents of the file are pulled out of MongoDB on the fly, + * so that the whole file does not have to be loaded into memory first. + * @return resource file stream resource. + * @throws \yii\base\InvalidParamException on invalid file attribute value. + */ + public function getFileResource() + { + $file = $this->getAttribute('file'); + if (empty($file) && !$this->getIsNewRecord()) { + $file = $this->refreshFile(); + } + if (empty($file)) { + throw new InvalidParamException('There is no file associated with this object.'); + } elseif ($file instanceof \MongoGridFSFile) { + return $file->getResource(); + } elseif ($file instanceof UploadedFile) { + return fopen($file->tempName, 'r'); + } elseif (is_string($file)) { + if (file_exists($file)) { + return fopen($file, 'r'); + } else { + throw new InvalidParamException("File '{$file}' does not exist."); } } else { throw new InvalidParamException('Unsupported type of "file" attribute.'); diff --git a/extensions/mongo/file/Query.php b/extensions/mongo/file/Query.php index b22b64f..4c1e5ff 100644 --- a/extensions/mongo/file/Query.php +++ b/extensions/mongo/file/Query.php @@ -33,50 +33,39 @@ class Query extends \yii\mongo\Query } /** - * Fetches rows from the given Mongo cursor. - * @param \MongoCursor $cursor Mongo cursor instance to fetch data from. + * @param \MongoGridFSCursor $cursor Mongo cursor instance to fetch data from. * @param boolean $all whether to fetch all rows or only first one. - * @param string|callable $indexBy the column name or PHP callback, - * by which the query results should be indexed by. - * @throws Exception on failure. + * @param string|callable $indexBy value to index by. * @return array|boolean result. + * @see Query::fetchRows() */ - protected function fetchRows(\MongoCursor $cursor, $all = true, $indexBy = null) + protected function fetchRowsInternal($cursor, $all, $indexBy) { - $token = 'Querying: ' . Json::encode($cursor->info()); - Yii::info($token, __METHOD__); - try { - Yii::beginProfile($token, __METHOD__); - $result = []; - if ($all) { - foreach ($cursor as $file) { - $row = $file->file; - $row['file'] = $file; - if ($indexBy !== null) { - if (is_string($indexBy)) { - $key = $row[$indexBy]; - } else { - $key = call_user_func($indexBy, $row); - } - $result[$key] = $row; + $result = []; + if ($all) { + foreach ($cursor as $file) { + $row = $file->file; + $row['file'] = $file; + if ($indexBy !== null) { + if (is_string($indexBy)) { + $key = $row[$indexBy]; } else { - $result[] = $row; + $key = call_user_func($indexBy, $row); } - } - } else { - if ($cursor->hasNext()) { - $file = $cursor->getNext(); - $result = $file->file; - $result['file'] = $file; + $result[$key] = $row; } else { - $result = false; + $result[] = $row; } } - Yii::endProfile($token, __METHOD__); - return $result; - } catch (\Exception $e) { - Yii::endProfile($token, __METHOD__); - throw new Exception($e->getMessage(), (int)$e->getCode(), $e); + } else { + if ($cursor->hasNext()) { + $file = $cursor->getNext(); + $result = $file->file; + $result['file'] = $file; + } else { + $result = false; + } } + return $result; } } \ No newline at end of file diff --git a/tests/unit/extensions/mongo/file/ActiveRecordTest.php b/tests/unit/extensions/mongo/file/ActiveRecordTest.php index 93c2cc2..93fb552 100644 --- a/tests/unit/extensions/mongo/file/ActiveRecordTest.php +++ b/tests/unit/extensions/mongo/file/ActiveRecordTest.php @@ -2,6 +2,8 @@ namespace yiiunit\extensions\mongo\file; +use Yii; +use yii\helpers\FileHelper; use yiiunit\extensions\mongo\MongoTestCase; use yii\mongo\file\ActiveQuery; use yiiunit\data\ar\mongo\file\ActiveRecord; @@ -22,15 +24,31 @@ class ActiveRecordTest extends MongoTestCase parent::setUp(); ActiveRecord::$db = $this->getConnection(); $this->setUpTestRows(); + $filePath = $this->getTestFilePath(); + if (!file_exists($filePath)) { + FileHelper::createDirectory($filePath); + } } protected function tearDown() { + $filePath = $this->getTestFilePath(); + if (file_exists($filePath)) { + FileHelper::removeDirectory($filePath); + } $this->dropFileCollection(CustomerFile::collectionName()); parent::tearDown(); } /** + * @return string test file path. + */ + protected function getTestFilePath() + { + return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid(); + } + + /** * Sets up test rows. */ protected function setUpTestRows() @@ -256,4 +274,50 @@ class ActiveRecordTest extends MongoTestCase $this->assertEquals($record->status, $record2->status); $this->assertEquals($updateFileContent, $record2->getFileContent()); } + + /** + * @depends testInsertFileContent + */ + public function testWriteFile() + { + $record = new CustomerFile; + $record->tag = 'new new'; + $record->status = 7; + $newFileContent = 'Test new file content'; + $record->setAttribute('newFileContent', $newFileContent); + $record->save(); + + $outputFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . 'out.txt'; + $this->assertTrue($record->writeFile($outputFileName)); + $this->assertEquals($newFileContent, file_get_contents($outputFileName)); + + $record2 = CustomerFile::find($record->_id); + $outputFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . 'out_refreshed.txt'; + $this->assertTrue($record2->writeFile($outputFileName)); + $this->assertEquals($newFileContent, file_get_contents($outputFileName)); + } + + /** + * @depends testInsertFileContent + */ + public function testGetFileResource() + { + $record = new CustomerFile; + $record->tag = 'new new'; + $record->status = 7; + $newFileContent = 'Test new file content'; + $record->setAttribute('newFileContent', $newFileContent); + $record->save(); + + $fileResource = $record->getFileResource(); + $contents = stream_get_contents($fileResource); + fclose($fileResource); + $this->assertEquals($newFileContent, $contents); + + $record2 = CustomerFile::find($record->_id); + $fileResource = $record2->getFileResource(); + $contents = stream_get_contents($fileResource); + fclose($fileResource); + $this->assertEquals($newFileContent, $contents); + } } \ No newline at end of file