yiicod/mongoyii

此包已被弃用,不再维护。作者建议使用sammaye/mongoyii-php7包。

PHP 7的Yii MongoDB ORM

安装: 24

依赖: 0

建议者: 0

安全: 0

星标: 1

关注者: 2

分支: 0

开放问题: 0

类型:yii-extension

1.0.3 2016-04-26 15:04 UTC

This package is not auto-updated.

Last update: 2022-02-01 12:56:06 UTC


README

这是MongoYii扩展的更新。

为PHP 7提供的另一个Yii框架的Active Record处理器,支持MongoDB。

理由

已经有一个名为YiiMongoDBSuite的出色扩展可用于Yii,为什么还要再做一个?YiiMongoDBSuite存在一些缺陷,我希望解决这些问题。

  • 不支持原生的$or
  • 代码库非常大且复杂
  • 不太支持PHP驱动程序的较新版本(1.3.x系列)
  • 隐藏了MongoDB查询语言,在上面叠加了一个查询语言层

在空闲时间之后,我决定自己编写一个MongoDB扩展用于Yii。它基本上是MongoDB和Yii之间的“胶水”,并在此方面设计得相当自由。

我希望强制执行一些设计点

  • 以原始形式公开MongoDB查询语言
  • 使此扩展的编程简单,易于维护
  • 确保此扩展与MongoDB驱动程序的旧版和新版都兼容
  • 尝试使性能更优

好了,我们已经确定了一些理由,现在是时候真正讨论这个扩展了。

设置扩展

运行以下命令:

composer.phar require --prefer-dist yiicod/mongoyii "*"

或者添加以下内容:

"yiicod/mongoyii": "*"

一旦您有了源代码,您需要编辑您的main.php配置文件(如果打算在控制台中使用此扩展,也需要修改console.php),使用以下类型的配置

	'mongodb' => array(
		'class' => 'EMongoClient',
		'server' => 'mongodb://:27017',
		'db' => 'super_test'
	),

并将MongoYii目录添加到您的import部分

'application.extensions.MongoYii.*',
'application.extensions.MongoYii.validators.*',
'application.extensions.MongoYii.behaviors.*',
'application.extensions.MongoYii.util.*'

这就是扩展的基本设置。

您会注意到我使用了EMongoClient。这有点误导,因为它实际上代表了MongoDB\ClientMongoDB的组合。这意味着每次您以这种方式在EMongoClient上调用魔术__call

	Yii::app()->mongodb->getSomething();

它将尝试在EMongoClient中调用getSomething的函数,或者如果该函数不存在,则尝试在MongoDB类中调用它。

如果您想在MongoDB\ClientMongo类上调用函数,您需要像这样检索连接对象

	Yii::app()->mongodb->getConnection()->getSomething();

EMongoClient还设计用于以与所有驱动程序版本兼容的方式处理完整的写入关注和读取首选项。

注意:模型默认会寻找配置中的mongodb组件,因此请确保除非修改扩展,或在不使用Active Record的情况下使用它,否则请确保默认(主)连接的组件名为mongodb

如果您想设置日志以将条目插入MongoDB(如CDbLogRoute),您可以向您的'log'组件配置添加以下内容

		'log'=>array(
			'class'=>'CLogRouter',
			'routes'=>array(
				array(
					'class'=>'CFileLogRoute',
					'levels'=>'error, warning',
				),

				[ ... ]

				array(
					'class'=>'EMongoLogRoute',
					'connectionId'=>'my_connection_id', // optional, defaults to 'mongodb'
					'logCollectionName'=>'my_log_collection', // optional, defaults to 'YiiLog'
				),
				
			),
		),

提供自定义mongodb组件/多个连接

每个继承自EMongoDocumentEMongoModel的类,即您的模型将有一个可重写的函数getMongoComponent()。您可以简单地重写这个函数来返回您自定义的应用程序组件,例如:

	public function getMongoComponent()
	{
		return Yii::app()->someweirddbconnectionINIT;
	}

现在,这个模型将使用这个新的应用程序组件来获取其信息。如果您为不同的模型使用不同的数据库,这也很有用。

Composer

MongoYii完全支持Composer,并在packagist上有列表。

作为附加的注意事项,这是由@ujovlado在相关的问题中发布的;如果您只为Yii扩展使用Composer,则可以将vendor-dir更改为protected/extensions

	{
	    "config": {
	        "vendor-dir": "protected/extensions"
	    }
	}

然而,为了自动处理使用Composer加载MongoYii,您需要降级到1.0.3版本,其中Yii安装器尚未弃用。如果这两个选项都不可行,您也可以创建自己的脚本来处理已删除的安装器所执行的操作。

目前MongoYii不支持命名空间,并且这种情况在Yii1中不太可能改变。

写关注(以前称为“安全”写入)

此扩展使用全局的w变量来处理您希望施加在MongoDB上的写关注级别。

默认情况下,扩展将假设已确认的写入,这意味着safe=truew=1,具体取决于您的驱动程序版本。要更改此设置,请将w添加到您的mongodb组件配置中,并给它一个根据PHP文档提供的值。

对于使用1.3.x系列驱动程序的用户,还有一个j选项,可以在配置中将其设置为truefalse,这允许您控制写入是否被日志确认。

注意:写关注从驱动程序本身抽象出来,以便使该变量与所有版本的驱动程序兼容,因此请在使用时使用配置或EMongoClientwj类变量来设置写关注,否则在活动记录中不会使用该写关注。

注意:当您直接接触数据库时,写关注的行为与在EMongoClient类中发出的写关注不同。在这种情况下,您应该始终确保根据您的驱动程序版本手动指定写关注。

这可能在将来改变,但到目前为止,当您想要活动记录消失时,它就会消失。

在不使用活动记录的情况下使用MongoDB

您可以直接使用与使用驱动程序正常使用相同的实现方法调用数据库。例如,要查询数据库:

	Yii::app()->mongodb->collection->find(array('name' => 'sammaye'));

因此,如果需要,MongoYii的活动记录元素可以快速消失。

EMongoModel

EMongoModelEMongoDocument的简化版本。

将其与EMongoDocument分开,以提供一个小巧精简的活动模型,用于子文档。每次您创建基于类的子文档时,都可以扩展此类。

EMongoModel实现了CModel的所有功能,并增加和修改了一些特性。

魔法函数

获取器和设置器应该继承Yiis的所有功能。

虚拟属性

此扩展支持通过@virtual的doc块注释语法来实现虚拟属性,例如

	class User extends EMongoModel{
	    /** @virtual */
	    public $somevar;
	}

这些变量可以像其他所有变量一样使用,但它们永远不会被保存到MongoDB中。

注意:由于PHP OO访问方式,将所有记录字段(无论是虚拟的还是非虚拟的)设为public是一个好主意。

关系

与SQL中的复杂关系类型不同,在MongoDB中,你通常只有两种关系:onemany

正如你所猜想的,在这个扩展中,你可以定义两种类型的关系——onemany。让我们看一个例子

	function relations(){
		return array(
			'others' => array('many', 'Other', 'otherId', 'sort' => array('_id'=>-1), 'skip' => 1, 'limit' => 10)
		);
	}

你会从Yiis的active record中认出很多内容,实际上很多是相同的。我们定义一个关系名称作为键,然后我们在文本中定义onemany(由于只有两种类型,常量似乎没有用),然后我们定义一个类名,在这个例子中是Other,然后我们定义该类的外键otherId

关系默认行为是尝试使用当前模型的唯一键_id来查询外键。这对于EMongoModel来说是一个问题,因为它没有主键。确保如果你在EMongoModel中使用这个功能,你定义一个on子句来替换当前模型的主键。

on子句支持多种字段类型。它可以接受DBRefObjectIdObjectId数组,具体取决于你如何定义你的文档。

你还可以像在Yii中一样定义一个where子句。这是一个与MongoDB中正常查询语法一对一的关系,扩展将基本上将此子句与你在其中定义的主键字段合并,以查询关系。

缓存

截至5.x版本,MongoYii中的关系缓存默认是开启的。这意味着现在所有关系都被缓存。如果你想要确保某个关系不被缓存,你可以显式地在关系定义中添加'cache' => false,如下所示

	function relations(){
		return array(
			'others' => array('many', 'Other', 'otherId', 'cache' => false)
		);
	}

5.x版本之前,默认没有开启关系缓存。

getDocument()

仅获取“原样”的文档。这意味着如果你在嵌套的EMongoModel中放置了元对象,它会在输出中返回这些对象。

getRawDocument()

会移除扩展使用的所有类,并返回一个适合与MongoDB一起使用的文档。

getJSONDocument()

会运行getRawDocument(),然后返回其输出的JSON字符串。

getBSONDocument()

会运行getRawDocument(),然后返回其输出的BSON字符串。

EMongoDocument

EMongoDocument 继承自 EMongoModel 并实现了其所有功能,同时还包括数据库访问所需的特性。它尽可能多地实现了 CActiveRecord 的功能。

注意: 允许使用数据库的函数并未在本文档的这一节中定义。相反,这些函数实际上在“查询”节中定义。如果您想了解 EMongoDocument 的这部分内容,请转到“查询”节。

collectionName()

返回表示集合名称的字符串。所有活动记录模型都应该实现此函数,尽管它不是 abstract

primaryKey()

目前仅返回 _id 作为键。

使用自定义主键

如果您使用的主键不是 ObjectId(在 PHP 驱动程序中称为 MongoId),则应该重写 EMongoDocumentgetPrimaryKey 函数,以便不返回 MongoId

	public function getPrimaryKey($value=null){
		if($value===null)
			$value=$this->{$this->primaryKey()};
		return (string)$value;
	}

当然,您可以在该函数中添加任何所需的程序或格式化代码,以确保主键在查询 MongoDB 时准备就绪。

作用域

作用域以所有正常方式得到完全支持,就像在 CActiveRecord 中一样,但有一个区别;术语。

在此扩展中的作用域和查询使用这些词来描述其各个部分

  • condition 用于描述条件本身
  • sort 用于描述排序
  • skip 用于描述偏移量
  • limit 用于描述限制

作为一个示例,一个完整的默认作用域,省略已删除的模型,获取最新的 10 个,跳过第一个

	array(
		'condition' => array('deleted' => array('$ne' => 1)),
		'sort' => array('date' => -1),
		'skip' => 1,
		'limit' => 11
	)

您也可以定义自己的作用域,但是与您在 Yii 中习惯的方式略有不同

	function someScope(){
		$this->mergeDbCriteria(array(
			'condition'=>array('scoped' => true),
			'sort'=>array('date'=>-1),
			'skip'=>1,
			'limit'=>11
		));
	}

如您所注意到的,EMongoDocument 中的 _criteria 变量通常是 EMongoCriteria 对象,但实际上是完全基于数组的。

这适用于所有作用域操作;它们都是基于数组的。

为了帮助您避免使用 EMongoCriteria 对象,EMongoDocument 提供了一个合并标准对象的帮助函数,称为 mergeCriteria。使用此函数将不会影响模型本身,而只是合并标准以返回。以下为使用 mergeCriteria 函数的示例

	function someScope(){

		$criteria = array(
			'condition'=>array('scoped' => true),
			'sort'=>array('date'=>-1),
			'skip'=>1,
			'limit'=>11
		);

		if($this->deleted)
			$criteria = $this->mergeCriteria($criteria,array('condition'=>array('deleted'=>1)));

		$this->mergeDbCriteria($criteria);
		reutrn $this;
	}

注意: 与 Yii 类似,通常作用域不会自动重置,请使用 resetScope() 来重置作用域。

equals()

检查当前模型是否等于作为参数传入的另一个模型。

exists()

检查数据库中是否存在具有作为第一个参数提供的标准的文档。

clean()

清除文档的所有属性和关系。

refresh()

运行 clean() 然后从数据库中重新填充模型。

getCollection()

返回原始的 MongoDB\Collection

通常最好不使用此功能,而是使用扩展封装版本 - updateAlldeleteAll。这些函数与手动在 getCollection() 上操作的唯一区别是,这些函数理解扩展的写入关注点。

ensureIndexes()

此函数允许用户通过数组定义来确保一组索引。

当用于 init() 函数以在模型启动时生成预定义索引时,这非常有用。例如:

    public function init()
    {
        if(YII_DEBUG){
            $this->ensureIndexes(array(
                array('username' => 1),
                array(array('email' => 1), array('unique' => true)),
                array(array('description' => 1))
            ));
        }
    }

上面的示例代码片段显示了定义索引的所有不同方法。

默认情况下,函数输入数组的每个元素都将是一个索引定义,元素 0 是字段,1 是选项。

然而,您不需要定义索引选项。您还可以通过不定义 0 元素而是定义一个只包含索引字段的关联数组来进一步简化定义。

setAttributes()

了解MongoYii默认如何分配整数非常重要,甚至可以说是强制性的。由于MongoDB没有对字段类型进行严格处理,因此布尔整数(如复选框等)很容易变成字符串,从而破坏您的应用程序并导致您不得不反复进行对象转换或更改查询方式(因为当然,MongoDB在查询时是类型感知的)。

MongoYii会将任何数字(无论是实数还是“正数”或“无符号整数”,只要不以0开头且不包含字母),转换为int

这是因为MongoDB能够以原生方式存储的最大整数是32位。为了使MongoDB存储更大的整数,您必须使用驱动程序中可用的native_long配置变量。

如果您使用的是32位系统,则需要添加另一个配置变量到堆栈中:long_as_object

注意:大于系统限制的整数将被保留为字符串。这意味着在32位系统上,您可以从表单数据中分配的最大int是2147483647,而在64位系统上是9223372036854775807。如果您希望在系统限制之外使用int数据类型,您将需要自己处理字段,无论是在CHttpRequest处理程序中还是在验证器中。

示例

既然我们已经讨论了EMongoDocument,让我们看看最基础的示例

	class User extends EMongoDocument{
		function collectionName(){
			return 'users';
		}

		public static function model($className=__CLASS__){
			return parent::model($className);
		}
	}

这是可以存在的最基本文档 - 没有预定义的模式,只需要model函数(与Yii活动记录相同)和tableName,也就是所谓的collectionName

随着时间的推移,您可能会想添加某些字段,如虚拟属性等,以使生活更加容易

	class User extends EMongoDocument{

		/** @virtual */
		public $agree = 1;

		public $addresses = array();

		function collectionName(){
			return 'users';
		}

		public static function model($className=__CLASS__){
			return parent::model($className);
		}
	}

请注意,我为什么添加了addresses字段,尽管不需要?我这样做是因为PHP使用魔术函数的方式。

如果您使用魔术方式访问数组,那么您不能在同一个呼吸中操纵它,因为它是对变量的间接访问。所以这里有一个好的提示:如果您打算有子文档,可能最好在类中显式声明字段为变量。

查询

查询尝试尽可能多地暴露原生MongoDB查询语言。提供了一个EMongoCriteria类,但是它不是必需的,并且不提供比通过数组执行更多的功能。该EMongoCriteria类在任何地方都没有依赖关系,也不需要。

缓存

MongoYii 支持通过 EMongoCacheDependency 实现完整的缓存功能(请参阅本文档底部),同时也支持在 文档 中定义的活跃模型查询缓存。

以下是一个示例:

$dep = new EMongoCacheDependency('article', [['_id' => new MongoId('540477726803fad51b8b4568')], 'sort' => ['a' => 1]]);
$c = Article::model()->cache(4, $dep)->findAll();

直到依赖项被认为过期,$c 的结果将从缓存表中提取到您的应用程序中。

就像在正常的 Yii 活跃记录中一样,您也可以指定在依赖项之后应该缓存多少查询。

find()

find() 非常简单。它本质上是对驱动程序自己的 find() 函数的 1-1 实现,并实现了相同的特定功能。就像驱动程序版本一样,它也返回一个 EMongoDocument 数组。

findOne() 和 findBy_id()

findOne,就像 findBy_id 一样,是驱动程序自己的 findOne 方法的直接 1-1 实现,如果找到记录则返回活跃记录记录模型,否则返回 null

findBy_id 函数接受一个十六进制表示的 ObjectId 的字符串形式或包装在 MongoId 类中,并使用 findOne 函数查找具有该 _id 的记录,返回相同的结果。它基本上是 findOne 的辅助函数,以使您的生活更加轻松。

作用域

该扩展的读取函数完全支持模型中的作用域。

示例

因此,我们现在对查询有了基本的了解,让我们看看一个示例

$c = User::model()->recently()->find(array('deleted' => 0))->sort(array('joined' => -1))->skip(2)->limit(3);

这看起来可能很复杂,但现在我将为您分解它

  • User::model() 获取我们的模型
  • ->recently() 实际上是一个作用域,这虽然不是必需的,但有助于演示目的
  • ->find(/*...*/) 基本上是对 MongoDB 驱动程序的 find 方法,并返回一个 EMongoDocument 或 BSONArray 数组
  • ->sort() 基本上是对 EMongoQueryBuilder 上的 MongoDB 驱动程序的 sort 方法
  • ->limit() 再次,基本上是对 EMongoQueryBuilder 上的 MongoDB 驱动程序的 limit 函数

有关支持的操作符的参考,请参阅 MongoDB 文档:http://docs.mongodb.org/manual/reference/operators/

注意:由于 MongoDB 的查询语言似乎没有必要实现像 findByAttributes 这样的函数,因此已省略其他函数。

save()

save 保存文档,并用作从外部访问活跃记录模型上的 insertupdate 的手段,即

if($user->validate()) $user->save();

如果文档是新的,它将插入,否则它将更新。

insert()

此函数由活跃记录模型内部使用。如果记录是新的,它将尝试插入,否则它将引发错误。

update()

此函数由活跃记录模型内部使用。如果记录不是新的,它将尝试更新它,否则它将引发错误。

如果您向此函数或 save 函数发送属性,它将尝试对那些属性执行 $set,否则它将保存模型。

delete()

这个函数用于删除当前活动的记录。

deleteByPk() 和 updateByPk()

这些函数是更新和删除函数的辅助工具,但它们直接作用于数据库,而不是通过活动记录。

以下通过示例展示

	User::model()->deleteByPk($_id[, array('deleted' => 1)[, array('w' => 2)]]);
	User::model()->updateByPk($_id, array('$set' => array('d' => 1)[, array('deleted' => 1)[, array('w' => 2)]]);

方括号[]中的参数是可选的。

这些函数可以接受字符串和 MongoId 作为 $_id 参数。

updateAll() 和 deleteAll()

实际上与上面相同,只是这些函数直接转换为MongoDB驱动程序的 updatedelete 函数。

注意: UpdateAll 默认为 multi true

验证

验证几乎没有变化,只是由于Yiis本身需要SQL,某些验证器的名称发生了变化。

unique

现在,unique 验证器是 EMongoUnqiueValidator

	array('username', 'EMongoUniqueValidator', 'className' => 'User', 'attributeName' => 'username')

exist

现在,exist 验证器是 EMongoExistValidator

	array('user_id', 'EMongoExistValidator', 'className' => 'User', 'attributeName' => '_id')

EMongoIdValidator

此验证器被添加作为一个简单而灵活的方法来自动转换 MongoDB\BSON\ObjectID 的十六进制表示(例如:addffrg33334455add0001)到 MongoDB\BSON\ObjectID 对象,以便进行数据库操作。此验证器还可以处理需要转换为 MongoDB\BSON\ObjectID 的字符串数组。

	array('ids,id', 'EMongoIdValidator'), // ids is an array while id is a single string value

EMongoSubdocumentValidator

这是子文档验证器,请参阅“子文档”部分以获取完整文档。

行为

EMongoTimestampBehaviour

这是MongoYii版本的 CTimestampBehavior 行为,它将使用 MongoDate 字段,但是可以在 timestampExpression 中添加一个表达式,使行为返回整数时间戳。

该行为的使用方法与普通行为非常相似,事实上只有名称不同。

	function behaviors(){
		return array(
			'EMongoTimestampBehaviour'
		);
	}

子文档

此扩展通常不支持自动支持子文档。有几个原因,首先是由于性能——自动化子文档的使用需要大量加载的类来处理不同的子文档及其验证。

另一个主要原因是,在我所做过的任何项目中,每当尝试通过活动记录自动化子文档时,我总是会实际放弃它并手动进行这个过程。多次证明,你很少真正需要自动化的子文档,通常你希望对它们的存储有比此扩展所能提供的更大的控制。

因此,这是对在活动记录中放弃自动处理子文档的想法背后的理念的一个简要了解。

这并不意味着你根本不能嵌入子文档类;在保存时,活动记录类将遍历文档,并尝试剥离任何出现的 EMongoModelEMongoDocument 类。

尽管如此,存在一个子文档验证器,从技术上讲,它可以接受多级嵌套。但是请注意,每次使用它都会在每个级别上造成重复。这将对您的应用程序的性能产生影响。

使用基于数组的子文档的一个例子是

	function rules(){
		return array(
			array('addresses', 'subdocument', 'type' => 'many', 'rules' => array(
				array('road,town,county,post_code', 'safe'),
				array('telephone', 'integer')
			)),
		);
	}

而基于类的子文档的一个例子是

function rules(){
	return array(
		array('addresses', 'subdocument', 'type' => 'many', 'class' => 'Other'),
	);
}

type定义了子文档的类型,正如与关系一样,这可以是onemany

验证器将像它们与原始模型完全独立一样评估规则,因此从理论上讲,没有任何阻止您使用任何想要的验证器。

验证器对子文档的错误输出将在onemany类型之间有所不同。对于one,验证器将直接在字段上输出模型错误,而对于many,它将在父字段中为每个模型(行)创建一个新的元素,并在该新元素中包含嵌入的错误,例如

	array(
		'addresses' => array(
			0 => array(
				'telephone' => array(
					0 => 'Some error here'
				)
			)
		)
	)

注意:为了在1.1.4上使过滤器正常工作,验证器现在默认会覆盖您输入的内容,用验证器输出的结果来替换。这意味着如果您的规则定义不正确,您可能会在子文档中丢失字段。您可以通过将此验证器的strict参数设置为false来解决这个问题。

注意:关于这个问题,为了避免每次保存根文档时都进行迭代(因为Yii默认在保存时执行验证),您应该将子文档验证器限制在特定场景中,在这些场景中它们将被积极使用。

处理子文档

正如我们所知,MongoYii不会自动为您处理子文档。如果您希望有一个自动处理的子文档,通常建议根据您的要求自己制作。一个原因是许多人有各种各样的文档设置,由于没有预定义的子文档模式,所以无法提供自动使用,除非考虑子文档存在的所有可能性。

为了说明这一点,我们将假设您不希望制作自己的子文档处理器,而是愿意使用MongoYii和PHP内置的能力。处理子文档在很大程度上取决于您打算如何管理和使用它们。

好吧,让我们从顶层开始;您是否为这些子文档使用了一个类?如果答案是“是的,先生!”那么您的子文档可能相当复杂,并且可能有自己的部分,包括自己的控制器等,例如,博客文章的评论。

现在,您必须问自己的第二个问题;您是否每次保存时都替换这些子文档,还是您想使用诸如$push$pull$pullAll$pushAll$addToSet等修饰符?

如果您想每次都使用修饰符,那么管理这种类型文档的最佳方式是将子文档的单一类扩展为EMongoModel,例如,Comment会扩展EMongoModel

当,比如说,添加一个评论到文章时,您会这样做

	if(isset($_POST['Comment'])){
		$comment=new Comment;
		$comment->attributes=$_POST['Comment'];
		if($comment->validate())
			$response = Post::model()->updateAll(array('_id' => $someId), array('$push' => $comment->getRawDocument()));
	}

您会使用相对类似的行为来执行大多数其他需要的操作。在这种情况下,MongoYii只是作为您的助手和胶水,使生活变得稍微容易一些,然而,最终,它不会自动管理子文档。

如果您没有使用类,那么您的子文档可能相当原始,并且很可能是根文档的细节,并且您每次都替换它们。这种情况也适用于您使用复杂的类,但在每次保存时都替换子文档列表。

如果情况是这样,您可以使用上述提到的子文档验证器来处理您的子文档,或者您可以真正地以编程方式完成这项工作

	$valid=true;
	$user=User::model()->findBy_id($uid);
	if(isset($_POST['numbers'])){
		foreach($_POST['numbers'] as $row){
			$d=new Model();
			$d->attributes = $row;
			$valid=$d->validate()&&$valid;
			$user->numbers[] = $d;
		}
	}
	if($valid) $user->save();

例如。

作为附加的注意事项,您实际上可以像任何其他字段一样处理文档中的数组字段,这些字段包含子文档。例如,这将有效

	$m=new Something();
	$m->name='thing';
	$parentClass->things[6] = $m;
	$parentClass->save();

因此,在这个扩展中,子文档非常灵活,它们不会让您陷入只考虑一种或一种方式的思维定势,这实际上就像MongoDB本身一样。

使用ActiveDataProvider

此扩展附带了一个名为EMongoDataProviderCActiveDataProvider辅助工具。它的工作方式完全相同,只是在调用方式上有所不同。

您不是使用EMongoCriteria或类似的东西,而是使用如下所示的数组

	new EMongoDataProvider(array(
		'criteria' => array(
			'condition' => array(),
			'sort' => array(),
			'skip' => 1,
			'limit' => 1
		),
		/* All other options */
	));

criteria选项基本上与游标的各个部分相关。

此扩展完全支持CGridView(感谢@acardinale提供的修复)并且也应该能够处理CListView

关于上述内容的附加说明,CGridView在您在CGridView小部件的定义中预定义要显示的模式时最好使用。因此,为了显示用户模型的示例

	$this->widget('zii.widgets.grid.CGridView', array(
		'id'=>'user-grid',
		'dataProvider'=>$model->search(),
		'filter'=>$model,
		'columns'=>array(
			'_id',
			'username',
			'addresses',
			'create_time',
			array(
				'class'=>'CButtonColumn',
				'template'=>'{update}{delete}',
			),
		),
	));

这通常是最佳方法,因为,当然,MongoDB是无模式的(具有灵活的模式可能更合适)因此有时它不适合刚性表格。

EMongoCriteria

EMongoCriteria类可以帮助在应用程序的许多部分构建模块化查询,提供具有辅助函数的抽象层,使您能够更好地创建复杂查询。

使用EMongoCriteria的简短但完整的示例将是

	$c = new EMongoCriteria();
	User::model()->find($c
					->addCondition(array('name' => 'sammaye')) // This is basically a select
					->addOrCondition(array(array('interest' => 'Drinking'), array('interest' => 'Clubbing'))) // This is adding a $or condition to our select
					->skip(2) // This skips a number of rows
					->limit(3) // This limits by a number of rows
						);

因此,您可以看到,我们可以轻松地构建非常复杂的查询。

就像与CDbCriteria一样,您也可以直接从构造函数设置查询的所有这些属性,如下所示

	$c = new EMongoCriteria(array(
		'condition' => array('name'=>'sammaye'),
		'limit' => 10
	));

EMongoCriteria类实现了许多您期望从CDbCriteria获得的函数。

setCondition() / getCondition()

这些基本上只是设置和获取查询的条件。

addCondition()

向查询添加一个正常、非$or条件,并接受一个array作为其唯一的参数。

addOrCondition()

添加一个$or条件,并接受一个包含arrays的数组作为其唯一的参数,每个嵌套的array都是$or中的条件(就像在驱动程序中一样)。

值得注意的是,调用此函数将覆盖条件中之前放置的任何$or

getSort() / setSort()

获取和设置查询的排序。

getSkip() / setSkip()

获取和设置查询的跳过数。

getLimit() / setLimit()

获取和设置查询的限制。

getProject() / setProject()

设置标准投影以指定要包括/排除的特定字段。

getSelect() / setSelect()

这些提供了对getProject()setProject()的别名。

compare()

这与CDbCriteria非常相似,并在此基础上进行了大量改进。

您只需输入columnvaluematchPartial参数值(按此顺序),然后EMongoCriteria类将创建一个条件并将其合并到您当前的条件中,基于输入的数据。例如

	$c->compare('name', 'sammaye');

	$c->compare('i', '<4');

如第二个示例中所示,比较函数将接受一定数量的运算符。支持的运算符有:<><=>=<>=

需要注意的是,该函数目前仅接受AND条件。

mergeWith()

就像在CDbCriteria中一样,这会将数组或另一个EMongoCriteria对象合并到其中,并转移其所有属性。

例如

$c->mergeWith($otherC);

现在$c将具有合并自$otherC的所有属性。

toArray()

这基本上会将您的EMongoCriteria转换为语法数组的格式

	array(
		'condition' => array(),
		'skip' => 1,
		'limit' => 1,
		'sort' => array(),
		'project' => array()
	)

默认情况下,调用方式如下

$c->toArray();

全量和部分查询

当您不希望检索整个文档时,可以仅返回部分结果。

无论是EMongoCriteria还是基于数组的常规查询,都支持通过两种方法进行投影。首先,作为EMongoCriteria中的project变量

	$c->project=array('_id'=>0,'d'=>1);

或者作为定义数组中的元素(例如,作为范围)

	functions scope(){
		return array(
			'project' => array('_id'=>0,'d'=>1)
		);
	}

其次,作为注入到活动记录模型读取函数的参数,例如

	User::model()->find(array(),array('_id'=>0,'d'=>1));

这将返回partial=trueEMongoDocument实例,无论是通过预加载还是通过游标对象。这种规范在所有当前存在的读取函数中实现,例如findOnefindBy_idfindByPk,然而,它们不能在写入函数(updateinsertsave等)中使用。

当文档以部分形式返回时,它将仅保存查询结果中包含的根级别字段。

注意:在使用$elemMatch投影时,请记住MongoYii将此结果视为该字段的最终结果。换句话说,当您尝试保存根文档时,MongoYii将考虑单个投影子文档作为完整的字段值,并将删除该字段中的所有其他子文档。

注意:如果通过'_id' => 0从根文档中省略了_id,则将不允许保存该文档。扩展程序将抛出一个关于_id字段未设置的异常。

使用urlManager

如果您希望在URL中使用urlManager排除_id,可以使用

'<controller:\w+>/<action:\w+>/<id:[a-z0-9]{24}>'=>'<controller>/<action>',

它将尝试提取长度为24位的字母数字_id

版本化文档模型

MongoYii 2.5.x版本添加了版本化文档的能力。

如果您对版本控制或它在某些场景中的益处感到困惑,那么可以找到一篇由 MongoDB 创造者撰写的解释清晰、简单易读的博客文章,介绍其如何添加到 Mongoose 中。[查看博客文章](http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning)。

要设置一个版本化的文档,您只需创建一个实现 version() 返回 true 的模型,并且可选地实现 versionField()

	class versioned extends EMongoDocument{
		public function versioned(){
			return true;
		}
		
		public function versionField(){
			return '_v'; // This is actually the default value in EMongoDocument
		}
		
		public static function model($className=__CLASS__){
			return parent::model($className);
	    }	
	}

一旦设置,文档的版本化能力在运行时不能更改,换句话说,您不能执行 $doc->removeVersion() 来停止对特定插入的版本控制产生影响。

文档模型设置后,版本控制将在幕后工作,您无需做任何事情,每次调用 save 时,它都会确保您拥有的版本是最新的。

数据库迁移

尽管 MongoDB 是无模式的,但您有时可能需要修改您的记录。为此,您可以使用 yiic migratemongo 命令。它与 yiic migrate 完全相同。有关详细用法,请参阅[Yii 文档](https://yiiframework.cn/doc/guide/1.1/en/database.migration)。

要启用此命令,请在您的配置文件中添加 commandMap 条目。

    'commandMap' => array(
        'migratemongo' => array(
            'class' => 'application.extensions.MongoYii.util.EMigrateMongoCommand'
        )
    )

实用工具

util 文件夹包含一些通用的扩展,可能对人们有用。这些扩展被认为是此文件夹的一部分,因为它们可能是 Yii 内部组件的替代品,而这些组件似乎超出了此存储库的根目录范围。

EMongoCache

这是由 Rajcsányi Zoltán 撰写的 CCache 的 MongoYii 实现。

要使用它,首先将其放置在您的配置中

	'components'=>array(
		...
		'cache' => array(
			'class'=>'application.extensions.MongoYii.util.EMongoCache',
			// 'ensureIndex' => true, //set to false after first use of the cache
			// 'mongoConnectionId' => 'mongodb',
			// 'collectionName' => 'mongodb_cache',		
		),
	}

注释掉的行是可选参数,如果需要,您可以发送它们。

以下是它的使用示例

	// flush cache
	Yii::app()->cache->flush();
	
	// add data to cache
	Yii::app()->cache->set('apple', 'fruit');
	Yii::app()->cache->set('onion', 'vegetables');
	Yii::app()->cache->set(1, 'one');
	Yii::app()->cache->set(2, 'two');
	Yii::app()->cache->set('one', 1);
	Yii::app()->cache->set('two', 2);
	
	// delete from cache
	Yii::app()->cache->delete(1);
	Yii::app()->cache->delete('two');
	
	// read from cache
	echo Yii::app()->cache->get(2);
	
	// multiple read from cache
	$arr = Yii::app()->cache->mget(array('apple', 1, 'two'));

  	print_r($arr); // Array( [apple] => fruit [1] => [two] => )

EMongoMessageSource

这是由 Rajcsányi Zoltán 实现的 MongoYii Yii::t()

要使用它,首先将其添加到您的配置中

	'components' => array(
		...
		'messages' => array(
			'class' => 'application.extensions.MongoYii.util.EMongoMessageSource',
			// 'mongoConnectionId' => 'mongodb', 
			// 'collectionName' => 'YiiMessages',               
		)        
	)

注释掉的行是可选参数,如果需要,您可以发送它们。

然后向翻译表中添加一些消息

db.YiiMessages.insert( { category:"users", message:"Freund", translations: [ {language:"eng", message:"Friend"} ] } );

然后简单获取这些消息

<?=Yii::t('users', 'Freund'); ?>

EMongoSession

这是由本人实现的 MongoYii CHttpSession

要使用它,只需将其包含在您的配置中

	'session' => array(
		'class' => 'application.extensions.MongoYii.util.EMongoSession',
	)

然后像使用 Yiis 的正常会话一样使用它。

EMongoAuthManager

这是由 @tvollstaedt 实现的 MongoDB 替代 Yiis 的 auth manager。

要使用它,只需将其放置在您的配置中

	'authManager' => array(
    	'class' => 'EMongoAuthManager',
    )

它将以与任何其他 auth manager 相同的方式工作。

注意:您可能想使用数据库迁移来保持您的应用程序实例中的授权设置保持最新。

EMongoPagination

这是由 @kimbeejay 构建的 MongoYii 的 CPagination 替代品。

它使用与 CPagination 相同的 API,除了使您意识到它的存在之外,不需要额外的文档。

EMongoCacheDependency

这用于启用MongoYii版本的缓存

这个类的示例用法是

	$cache = Yii::app()->cache;
	$cache->set(
		'12', 
		'dfgdfgf', 
		30,
		new EMongoCacheDependency('t', [
			[],
			'limit' => 5
		])
	);
	var_dump($cache->get('12'));

当缓存有效时将返回dfgdfgf,但如果您将其无效化,它将根据文档返回false

因此,如果我现在运行

	$cache = Yii::app()->cache;
	Yii::app()->mongodb->t->insert(['g' => 1]);
	var_dump($cache->get('12'));

我将得到返回值false。

这个缓存类的构造函数接受两个参数,一个是集合名称,另一个是查询。

查询参数的第一个(0)索引始终是find()查询,这正是该类解析查询参数的方式。

	$query = array();
	if(isset($this->query[0])){
		$query = $this->query[0];
	}
		
	$cursor = $this->getDbConnection()->{$this->collection}->find($query);
		
	if(isset($this->query['sort'])){
		$cursor->sort($this->query['sort']);
	}
		
	if(isset($this->query['skip'])){
		$cursor->limit($this->query['skip']);
	}
		
	if(isset($this->query['limit'])){
		$cursor->limit($this->query['limit']);
	}

目前,这个类的查询参数仅接受上面显示的解析部分,它目前不允许您直接获取游标。

注意:不要将游标放入这个类中,它将以PHP MongoDB驱动程序无法使用的方式保存到您的数据存储中。相反,您将被告知MongoCursor没有被其父类正确初始化。

已知缺陷

  • 子文档不是自动化的,然而,我已在上文说明了原因。
  • 聚合框架与活动记录不太匹配,因此它不在模型中直接支持,但是每个模型都有一个aggregate辅助函数,但它不会返回模型实例,而是直接返回MongoDB服务器的响应结果。

我相信还有更多,但这是您需要考虑的这个扩展的立即缺陷。

示例

请查看测试文件夹以获取如何使用此扩展的更多示例,它相当全面。

还有一个使用MongoYii构建的演示应用程序。它有效地模拟了一个维基百科类型的网站,并允许用户(包括会话)和文章管理。如果您仍在学习Yii,这不是一个好的起点,但是如果您在学习MongoYii,这是一个好的起点。

运行测试

测试需要编译所有依赖项的PHPUnit插件。使用PEAR,您可以发起以下命令

sudo pear install --force --alldeps phpunit/PHPUnit &&
pear install phpunit/dbUnit &&
pear install phpunit/PHPUnit_Story &&
pear install phpunit/PHPUnit_Selenium &&
pear install phpunit/PHP_Invoker

之后,您可以告诉PHPUnit运行tests/文件夹中的所有测试,而无需真正的顺序。

贡献

当向MongoYii添加大量功能时,请尽量提供相应的单元测试。没有单元测试,您的功能(您的项目很可能依赖于的功能)可能会在未来的版本中中断。

如果您打算向MongoYii贡献更改,我应该解释我对EMongoCriteria类存在的看法。我个人认为它是不必要的。

有多个原因。在SQL中,抽象是由以下原因之一(但不是全部) justified

  • 不同的实现(即MySQL、MSSQL和PostgreSQL)创建了略微不同的语法
  • SQL是一种基于字符串的查询语言,因此拥有一个面向对象的抽象层是有意义的
  • SQL有一些相当复杂且难以编写的查询,这些查询使得抽象层很有用

MongoDB 没有这些问题的困扰;首先它已经有一个面向对象的查询接口,其次,通过简单地使用 CMap::MergeArray() 就可以合并不同的查询,最重要的是,由于 MongoDB 只是一个数据库,它只有一个语法。除此之外,由于 MongoDB 的查询构建方式,这个类实际上可能会限制你的查询,让生活变得更加艰难,甚至可能创建低效的查询(尤其是在这个类中执行 $or 查询非常困难)。

因此,我认为 EMongoCriteria 类只是消耗内存的冗余,我可以将其用于其他任务。

这个扩展在内部不依赖于 EMongoCriteria

因此,我期望所有对 MongoYii 的特定部分的修改都应与 EMongoCriteria 的存在与否兼容。

许可证

这个扩展在 BSD 3 条款许可证下授权。简单来说:你可以用它做任何你想做的事情。