Browse Source

Mongo file Active Record updated.

tags/2.0.0-beta
Klimov Paul 11 years ago
parent
commit
ca608a81a4
  1. 36
      extensions/mongo/ActiveRecord.php
  2. 156
      extensions/mongo/file/ActiveRecord.php
  3. 50
      extensions/mongo/file/Query.php
  4. 16
      tests/unit/data/ar/mongo/file/ActiveRecord.php
  5. 27
      tests/unit/data/ar/mongo/file/CustomerFile.php
  6. 119
      tests/unit/extensions/mongo/file/ActiveRecordTest.php
  7. 5
      tests/unit/extensions/mongo/file/QueryTest.php

36
extensions/mongo/ActiveRecord.php

@ -250,7 +250,7 @@ abstract class ActiveRecord extends BaseActiveRecord
}
/**
* @see CActiveRecord::update()
* @see ActiveRecord::update()
* @throws StaleObjectException
*/
protected function updateInternal($attributes = null)
@ -309,24 +309,34 @@ abstract class ActiveRecord extends BaseActiveRecord
{
$result = false;
if ($this->beforeDelete()) {
// we do not check the return value of deleteAll() because it's possible
// the record is already deleted in the database and thus the method will return 0
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
$condition[$lock] = $this->$lock;
}
$result = static::getCollection()->remove($condition);
if ($lock !== null && !$result) {
throw new StaleObjectException('The object being deleted is outdated.');
}
$this->setOldAttributes(null);
$result = $this->deleteInternal();
$this->afterDelete();
}
return $result;
}
/**
* @see ActiveRecord::delete()
* @throws StaleObjectException
*/
protected function deleteInternal()
{
// we do not check the return value of deleteAll() because it's possible
// the record is already deleted in the database and thus the method will return 0
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
$condition[$lock] = $this->$lock;
}
$result = static::getCollection()->remove($condition);
if ($lock !== null && !$result) {
throw new StaleObjectException('The object being deleted is outdated.');
}
$this->setOldAttributes(null);
return $result;
}
/**
* Returns a value indicating whether the given active record is the same as the current one.
* The comparison is made by comparing the table names and the primary key values of the two active records.
* If one of the records [[isNewRecord|is new]] they are also considered not equal.

156
extensions/mongo/file/ActiveRecord.php

@ -7,6 +7,10 @@
namespace yii\mongo\file;
use yii\base\InvalidParamException;
use yii\db\StaleObjectException;
use yii\web\UploadedFile;
/**
* ActiveRecord is the base class for classes representing Mongo GridFS files in terms of objects.
*
@ -16,11 +20,6 @@ namespace yii\mongo\file;
class ActiveRecord extends \yii\mongo\ActiveRecord
{
/**
* @var \MongoGridFSFile|string
*/
public $file;
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]] to start a "find" command.
* You may override this method to return a customized query (e.g. `CustomerQuery` specified
@ -54,29 +53,6 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
}
/**
* Creates an active record object using a row of data.
* This method is called by [[ActiveQuery]] to populate the query results
* into Active Records. It is not meant to be used to create new records.
* @param \MongoGridFSFile $row attribute values (name => value)
* @return ActiveRecord the newly created active record.
*/
public static function create($row)
{
$record = static::instantiate($row);
$columns = array_flip($record->attributes());
foreach ($row->file as $name => $value) {
if (isset($columns[$name])) {
$record->setAttribute($name, $value);
} else {
$record->$name = $value;
}
}
$record->setOldAttributes($record->getAttributes());
$record->afterFind();
return $record;
}
/**
* Returns the list of all attribute names of the model.
* This method could be overridden by child classes to define available attributes.
* Note: primary key attribute "_id" should be always present in returned array.
@ -84,7 +60,16 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
*/
public function attributes()
{
return ['id', 'filename'];
return [
'_id',
'filename',
'uploadDate',
'length',
'chunkSize',
'md5',
'file',
'newFileContent'
];
}
/**
@ -103,7 +88,30 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
}
}
$collection = static::getCollection();
$newId = $collection->insert($values);
if (array_key_exists('newFileContent', $values)) {
$fileContent = $values['newFileContent'];
unset($values['newFileContent']);
unset($values['file']);
$newId = $collection->storeBytes($fileContent, $values);
} elseif (array_key_exists('file', $values)) {
$file = $values['file'];
if ($file instanceof UploadedFile) {
$fileName = $file->tempName;
} elseif (is_string($file)) {
if (file_exists($file)) {
$fileName = $file;
} else {
throw new InvalidParamException("File '{$file}' does not exist.");
}
} else {
throw new InvalidParamException('Unsupported type of "file" attribute.');
}
unset($values['newFileContent']);
unset($values['file']);
$newId = $collection->storeFile($fileName, $values);
} else {
$newId = $collection->insert($values);
}
$this->setAttribute('_id', $newId);
foreach ($values as $name => $value) {
$this->setOldAttribute($name, $value);
@ -113,7 +121,7 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
}
/**
* @see CActiveRecord::update()
* @see ActiveRecord::update()
* @throws StaleObjectException
*/
protected function updateInternal($attributes = null)
@ -126,20 +134,50 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
$this->afterSave(false);
return 0;
}
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
if (!isset($values[$lock])) {
$values[$lock] = $this->$lock + 1;
}
$condition[$lock] = $this->$lock;
}
// We do not check the return value of update() because it's possible
// that it doesn't change anything and thus returns 0.
$rows = static::getCollection()->update($condition, $values);
if ($lock !== null && !$rows) {
throw new StaleObjectException('The object being updated is outdated.');
$collection = static::getCollection();
if (array_key_exists('newFileContent', $values)) {
$fileContent = $values['newFileContent'];
unset($values['newFileContent']);
unset($values['file']);
$values['_id'] = $this->getAttribute('_id');
$this->deleteInternal();
$collection->storeBytes($fileContent, $values);
$rows = 1;
} elseif (array_key_exists('file', $values)) {
$file = $values['file'];
if ($file instanceof UploadedFile) {
$fileName = $file->tempName;
} elseif (is_string($file)) {
if (file_exists($file)) {
$fileName = $file;
} else {
throw new InvalidParamException("File '{$file}' does not exist.");
}
} else {
throw new InvalidParamException('Unsupported type of "file" attribute.');
}
unset($values['newFileContent']);
unset($values['file']);
$values['_id'] = $this->getAttribute('_id');
$this->deleteInternal();
$collection->storeFile($fileName, $values);
$rows = 1;
} else {
$condition = $this->getOldPrimaryKey(true);
$lock = $this->optimisticLock();
if ($lock !== null) {
if (!isset($values[$lock])) {
$values[$lock] = $this->$lock + 1;
}
$condition[$lock] = $this->$lock;
}
// We do not check the return value of update() because it's possible
// that it doesn't change anything and thus returns 0.
$rows = $collection->update($condition, $values);
if ($lock !== null && !$rows) {
throw new StaleObjectException('The object being updated is outdated.');
}
}
foreach ($values as $name => $value) {
@ -149,26 +187,26 @@ class ActiveRecord extends \yii\mongo\ActiveRecord
return $rows;
}
public function getContent()
/**
* Returns the associated file content.
* @return null|string file content.
* @throws \yii\base\InvalidParamException on invalid file value.
*/
public function getFileContent()
{
$file = $this->getAttribute('file');
if (empty($file)) {
return null;
}
if ($file instanceof \MongoGridFSFile) {
} elseif ($file instanceof \MongoGridFSFile) {
return $file->getBytes();
} elseif ($file instanceof UploadedFile) {
return file_get_contents($file->tempName);
} elseif (is_string($file)) {
if (file_exists($file)) {
return file_get_contents($file);
}
} else {
throw new InvalidParamException('Unsupported type of "file" attribute.');
}
}
public function getFileName()
{
$file = $this->getAttribute('file');
if (empty($file)) {
return null;
}
if ($file instanceof \MongoGridFSFile) {
return $file->getFilename();
}
}
}

50
extensions/mongo/file/Query.php

@ -8,6 +8,8 @@
namespace yii\mongo\file;
use Yii;
use yii\helpers\Json;
use yii\mongo\Exception;
/**
* Class Query
@ -29,4 +31,52 @@ class Query extends \yii\mongo\Query
}
return $db->getFileCollection($this->from);
}
/**
* Fetches rows from the given Mongo cursor.
* @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 the column name or PHP callback,
* by which the query results should be indexed by.
* @throws Exception on failure.
* @return array|boolean result.
*/
protected function fetchRows(\MongoCursor $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 $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;
} else {
$result[] = $row;
}
}
} else {
if ($cursor->hasNext()) {
$file = $cursor->getNext();
$result = $file->file;
$result['file'] = $file;
} else {
$result = false;
}
}
Yii::endProfile($token, __METHOD__);
return $result;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int)$e->getCode(), $e);
}
}
}

16
tests/unit/data/ar/mongo/file/ActiveRecord.php

@ -0,0 +1,16 @@
<?php
namespace yiiunit\data\ar\mongo\file;
/**
* Test Mongo ActiveRecord
*/
class ActiveRecord extends \yii\mongo\file\ActiveRecord
{
public static $db;
public static function getDb()
{
return self::$db;
}
}

27
tests/unit/data/ar/mongo/file/CustomerFile.php

@ -0,0 +1,27 @@
<?php
namespace yiiunit\data\ar\mongo\file;
class CustomerFile extends ActiveRecord
{
public static function collectionName()
{
return 'customer_fs';
}
public function attributes()
{
return array_merge(
parent::attributes(),
[
'tag',
'status',
]
);
}
public static function activeOnly($query)
{
$query->andWhere(['status' => 2]);
}
}

119
tests/unit/extensions/mongo/file/ActiveRecordTest.php

@ -0,0 +1,119 @@
<?php
namespace yiiunit\extensions\mongo\file;
use yiiunit\extensions\mongo\MongoTestCase;
use yii\mongo\file\ActiveQuery;
use yiiunit\data\ar\mongo\file\ActiveRecord;
use yiiunit\data\ar\mongo\file\CustomerFile;
/**
* @group mongo
*/
class ActiveRecordTest extends MongoTestCase
{
/**
* @var array[] list of test rows.
*/
protected $testRows = [];
protected function setUp()
{
parent::setUp();
ActiveRecord::$db = $this->getConnection();
$this->setUpTestRows();
}
protected function tearDown()
{
$this->dropFileCollection(CustomerFile::collectionName());
parent::tearDown();
}
/**
* Sets up test rows.
*/
protected function setUpTestRows()
{
$collection = $this->getConnection()->getFileCollection(CustomerFile::collectionName());
$rows = [];
for ($i = 1; $i <= 10; $i++) {
$record = [
'tag' => 'tag' . $i,
'status' => $i,
];
$content = 'content' . $i;
$record['_id'] = $collection->storeBytes($content, $record);
$record['content'] = $content;
$rows[] = $record;
}
$this->testRows = $rows;
}
// Tests :
public function testFind()
{
// find one
$result = CustomerFile::find();
$this->assertTrue($result instanceof ActiveQuery);
$customer = $result->one();
$this->assertTrue($customer instanceof CustomerFile);
// find all
$customers = CustomerFile::find()->all();
$this->assertEquals(10, count($customers));
$this->assertTrue($customers[0] instanceof CustomerFile);
$this->assertTrue($customers[1] instanceof CustomerFile);
// find by _id
$testId = $this->testRows[0]['_id'];
$customer = CustomerFile::find($testId);
$this->assertTrue($customer instanceof CustomerFile);
$this->assertEquals($testId, $customer->_id);
// find by column values
$customer = CustomerFile::find(['tag' => 'tag5']);
$this->assertTrue($customer instanceof CustomerFile);
$this->assertEquals($this->testRows[4]['_id'], $customer->_id);
$this->assertEquals('tag5', $customer->tag);
$customer = CustomerFile::find(['tag' => 'unexisting tag']);
$this->assertNull($customer);
// find by attributes
$customer = CustomerFile::find()->where(['status' => 4])->one();
$this->assertTrue($customer instanceof CustomerFile);
$this->assertEquals(4, $customer->status);
// find count, sum, average, min, max, distinct
$this->assertEquals(10, CustomerFile::find()->count());
$this->assertEquals(1, CustomerFile::find()->where(['status' => 2])->count());
$this->assertEquals((1+10)/2*10, CustomerFile::find()->sum('status'));
$this->assertEquals((1+10)/2, CustomerFile::find()->average('status'));
$this->assertEquals(1, CustomerFile::find()->min('status'));
$this->assertEquals(10, CustomerFile::find()->max('status'));
$this->assertEquals(range(1, 10), CustomerFile::find()->distinct('status'));
// scope
$this->assertEquals(1, CustomerFile::find()->activeOnly()->count());
// asArray
$testRow = $this->testRows[2];
$customer = CustomerFile::find()->where(['_id' => $testRow['_id']])->asArray()->one();
$this->assertEquals($testRow['_id'], $customer['_id']);
$this->assertEquals($testRow['tag'], $customer['tag']);
$this->assertEquals($testRow['status'], $customer['status']);
// indexBy
$customers = CustomerFile::find()->indexBy('tag')->all();
$this->assertTrue($customers['tag1'] instanceof CustomerFile);
$this->assertTrue($customers['tag2'] instanceof CustomerFile);
// indexBy callable
$customers = CustomerFile::find()->indexBy(function ($customer) {
return $customer->status . '-' . $customer->status;
})->all();
$this->assertTrue($customers['1-1'] instanceof CustomerFile);
$this->assertTrue($customers['2-2'] instanceof CustomerFile);
}
}

5
tests/unit/extensions/mongo/file/QueryTest.php

@ -51,7 +51,8 @@ class QueryTest extends MongoTestCase
$connection = $this->getConnection();
$query = new Query;
$row = $query->from('fs')->one($connection);
$this->assertTrue($row instanceof \MongoGridFSFile);
$this->assertTrue(is_array($row));
$this->assertTrue($row['file'] instanceof \MongoGridFSFile);
}
public function testDirectMatch()
@ -64,6 +65,6 @@ class QueryTest extends MongoTestCase
$this->assertEquals(1, count($rows));
/** @var $file \MongoGridFSFile */
$file = $rows[0];
$this->assertEquals('name5', $file->file['filename']);
$this->assertEquals('name5', $file['filename']);
}
}
Loading…
Cancel
Save