init
This commit is contained in:
159
vendor/yiisoft/yii2-mongodb/CHANGELOG.md
vendored
Normal file
159
vendor/yiisoft/yii2-mongodb/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
Yii Framework 2 mongodb extension Change Log
|
||||
============================================
|
||||
|
||||
2.1.7 March 30, 2018
|
||||
--------------------
|
||||
|
||||
- Bug #251: Fixed `yii\mongodb\ActiveQuery::indexBy()` does not apply while using Yii 2.0.14 (klimov-paul)
|
||||
- Enh: `yii\mongodb\Session` now relies on error handler to display errors (samdark)
|
||||
|
||||
|
||||
2.1.6 February 13, 2018
|
||||
-----------------------
|
||||
|
||||
- Bug #241: Fixed `yii\mongodb\Command::aggregate()` without 'cursor' option produces error on MongoDB Server 3.6 (Lisio, klimov-paul)
|
||||
- Bug #247: Fixed `yii\mongodb\Collection::dropIndex()` unable to drop index specified with sort via index plugin (klimov-paul)
|
||||
|
||||
|
||||
2.1.5 November 03, 2017
|
||||
-----------------------
|
||||
|
||||
- Bug #223: Usage of deprecated `yii\base\Object` changed to `yii\base\BaseObject` allowing compatibility with PHP 7.2 (klimov-paul)
|
||||
- Bug #227: Fixed `yii\mongodb\file\Collection::remove()` does not removes all file chunks in case `limit` is specified (klimov-paul)
|
||||
- Bug #228: Fixed `yii\mongodb\Command::aggregate()` does not support 'cursor' option (klimov-paul)
|
||||
- Enh #224: Provided support for 'migrate/fresh' command to truncate database and apply migrations again (klimov-paul)
|
||||
- Enh #225: Added `yii\mongodb\Migration::$compact` supporting `yii\console\controllers\BaseMigrateController::$compact` option (klimov-paul)
|
||||
- Chg #158: Data structure for `yii\mongodb\i18n\MongoDbMessageSource` changed avoiding usage message key as BSON key (klimov-paul)
|
||||
|
||||
|
||||
2.1.4 June 23, 2017
|
||||
-------------------
|
||||
|
||||
- Bug #187: Fixed exception is thrown on `yii\mongodb\rbac\MongoDbManager::invalidateCache()` invocation (jafaripur)
|
||||
- Bug #201: Fixed selection of master/slave server for read/write operations at `yii\mongodb\Command` (KhristenkoYura)
|
||||
- Bug #205: Fixed negative value passed to `yii\mongodb\Query::limit()` or `yii\mongodb\Query::offset()` does not disables query limit or offset correspondingly (klimov-paul)
|
||||
- Bug #207: Fixed `yii\mongodb\validators\MongoDateValidator` corrupts date value, while validating existing `MongoDB\BSON\UTCDateTime` instance (klimov-paul)
|
||||
- Bug #210: Fixed `yii\mongodb\debug\MongoDbPanel` overrides explain action of `yii\debug\panels\DbPanel` (Liv1020, klimov-paul)
|
||||
- Bug #213: Made `MigrateController` compatible with Yii 2.0.12 (cebe)
|
||||
|
||||
|
||||
2.1.3 February 15, 2017
|
||||
-----------------------
|
||||
|
||||
- Bug #168: Fixed `yii\mongodb\Command::update()` uses `upsert` option by default (klimov-paul)
|
||||
- Bug #170: Fixed incorrect order of migrations history in case `yii\mongodb\console\controllers\MigrateController::$migrationNamespaces` is in use (klimov-paul)
|
||||
- Bug #173: Fixed `yii\mongodb\ActiveQuery` does not respects relational link at methods `count()`, `distinct()`, `sum()`, `average()`, `modify()` (tuyakhov, klimov-paul)
|
||||
- Bug #176: Fixed `yii\mongodb\validators\MongoDateValidator` uses seconds instead of milliseconds while creating `MongoDB\BSON\UTCDateTime` instance (reza-id, klimov-paul)
|
||||
- Bug #179: Fixed `yii\mongodb\file\Upload` unable to handle custom `_id` value, if it does not provided as `\MongoDB\BSON\ObjectID` instance (klimov-paul)
|
||||
- Bug #186: Fixed `yii\mongodb\rbac\MongoDbManager::getRolesByUser()` results now includes default roles (klimov-paul)
|
||||
- Enh #171: Added support for `yii\db\QueryInterface::emulateExecution()` to force returning an empty result for a query (klimov-paul)
|
||||
- Enh #177: Method `yii\mongodb\ActiveQuery::exists()` optimized avoiding redundant ActiveRecord and relations population (klimov-paul)
|
||||
|
||||
|
||||
2.1.2 October 31, 2016
|
||||
----------------------
|
||||
|
||||
- Bug #150: Fixed `yii\mongodb\Query::exists()` always returning true (klimov-paul)
|
||||
- Bug #155: Fixed `yii\mongodb\Query` unable to process `not` condition with `null` compare value (klimov-paul)
|
||||
- Enh #152: Added support for namespaced migrations via [[yii\mongodb\console\controllers\MigrateController::migrationNamespaces]] (klimov-paul)
|
||||
- Enh #153: Added `yii\mongodb\rbac\MongoDbManager::getChildRoles()` method allowing finding child roles for the given one (githubjeka, klimov-paul)
|
||||
- Enh #154: Methods `scalar()` and `column()` added to `yii\mongodb\Query` (klimov-paul)
|
||||
|
||||
|
||||
2.1.1 August 29, 2016
|
||||
---------------------
|
||||
|
||||
- Bug #136: Fixed `yii\mongodb\Collection::findOne()` returns `false` instead of `null` on empty result (klimov-paul)
|
||||
- Bug #142: Fixed `yii\mongodb\Migration::createIndexes()` triggers E_NOTICE (klimov-paul)
|
||||
- Bug #145: Fixed `yii\mongodb\ActiveFixture` fails to find default data file if `collectionName` is specified in array format (klimov-paul)
|
||||
- Bug #146: Fixed `yii\mongodb\ActiveRecord` and `yii\mongodb\file\ActiveRecord` looses `_id` custom value on insertion (lxyfirst, klimov-paul)
|
||||
- Enh #147: Added unknown methods `stream_seek` and `stream_tell` to `yii\mongodb\file\StreamWrapper` for `fseek()` and `ftell()` (AstRonin)
|
||||
- Enh: Added `yii\mongodb\Migration::listCollections()` method (klimov-paul)
|
||||
|
||||
|
||||
2.1.0 June 27, 2016
|
||||
-------------------
|
||||
|
||||
- Enh #33: Added support for batch (bulk) write operations (klimov-paul)
|
||||
- Enh #56: Now 'mongodb' PHP extension used instead of 'mongo' (klimov-paul, hardsetting, Sammaye)
|
||||
- Enh #76: Added ability to disable logging and/or profiling for the commands and queries (klimov-paul)
|
||||
- Enh #77: Added support for fetching data from MongoDB in batches (klimov-paul)
|
||||
- Enh #79: `yii\mongodb\ActiveRecord::toArray()` provides better representation for BSON objects in recursive mode (klimov-paul, rowdyroad)
|
||||
|
||||
|
||||
2.0.5 May 9, 2016
|
||||
-----------------
|
||||
|
||||
- Bug #40: Fixed `yii\mongodb\ActiveFixture` throws exception on empty fixture data (darkunz)
|
||||
- Bug #73: Fixed `yii\mongodb\Collection::buildInCondition()` unable to process composite 'IN' condition (klimov-paul)
|
||||
- Bug #75: Fixed `yii\mongodb\Collection::distinct()` always returns `false` on empty condition for MongoDB 2.8 (boxoft)
|
||||
- Bug #101: Fixed `yii\mongodb\Collection::buildCondition()` does not compose 'IN' condition for the values with broken index sequence (klimov-paul)
|
||||
- Bug: Avoid serializing PHP 7 errors (zuozp8, cebe)
|
||||
- Enh #23: Added support for complex sort specification at `yii\mongodb\Query` (raoptimus)
|
||||
- Enh #24: `yii\mongodb\Query` now contains a `andFilterCompare()` method that allows filtering using operators in the query value (lennartvdd)
|
||||
- Enh #27: Added support for saving extra fields in session collection for `yii\mongodb\Session` (klimov-paul)
|
||||
- Enh #35: Added support for cursor options setup at `yii\mongodb\Query` (klimov-paul)
|
||||
- Enh #36: Added support for compare operators (like '>', '<' and so on) at `yii\mongodb\Query` (klimov-paul)
|
||||
- Enh #37: Now `yii\mongodb\Collection::buildInCondition` is not added '$in' for array contains one element (webdevsega)
|
||||
- Enh #41: Added `yii\mongodb\Connection::driverOptions` allowing setup of the options for the MongoDB driver (klimov-paul)
|
||||
- Enh #57: Added i18n support via `yii\mongodb\i18n\MongoDbMessageSource` (klimov-paul)
|
||||
- Enh #69: Fixed log target to display exceptions like DbTarget in Yii core, also avoids problems with Exceptions that contain closures (cebe)
|
||||
- Enh #74: Added explain method to `MongoDbPanel` debug panel (webdevsega)
|
||||
- Enh #87: Added RBAC support via `yii\mongodb\rbac\MongoDbManager` (klimov-paul)
|
||||
- Enh #102: `MongoDbTarget` now uses `batchInsert()` while exporting log messages (klimov-paul)
|
||||
|
||||
|
||||
2.0.4 May 10, 2015
|
||||
------------------
|
||||
|
||||
- Bug #7010: Fixed `yii\mongodb\Query::one()` fails on PHP MongoDB extension version 1.6.x (im-kulikov, klimov-paul)
|
||||
- Enh #5802: Added `yii\mongodb\validators\MongoIdValidator` and `yii\mongodb\validators\MongoDateValidator` validators (klimov-paul)
|
||||
- Enh #7798: Added support for 'NOT' conditions at `yii\mongodb\Collection` (klimov-paul)
|
||||
- Chg #7924: Migrations in history are now ordered by time applied allowing to roll back in reverse order no matter how these were applied (klimov-paul)
|
||||
|
||||
|
||||
2.0.3 March 01, 2015
|
||||
--------------------
|
||||
|
||||
- Bug #7010: Fixed `yii\mongodb\Query::select` now allows excluding fields (Sammaye, klimov-paul)
|
||||
|
||||
|
||||
2.0.2 January 11, 2015
|
||||
----------------------
|
||||
|
||||
- Bug #6376: Fixed lazy load of relations to `yii\mongodb\file\ActiveRecord` (klimov-paul)
|
||||
|
||||
|
||||
2.0.1 December 07, 2014
|
||||
-----------------------
|
||||
|
||||
- Bug #6026: Fixed `yii\mongodb\ActiveRecord` saves `null` as `_id`, if attributes are empty (klimov-paul)
|
||||
- Enh #3855: Added debug toolbar panel for MongoDB (klimov-paul)
|
||||
- Enh #5592: Added support for 'findAndModify' operation at `yii\mongodb\Query` and `yii\mongodb\ActiveQuery` (klimov-paul)
|
||||
|
||||
|
||||
2.0.0 October 12, 2014
|
||||
----------------------
|
||||
|
||||
- Bug #5303: Fixed `yii\mongodb\Collection` unable to fetch default database name from DSN with parameters (klimov-paul)
|
||||
- Bug #5411: Fixed `yii\mongodb\ActiveRecord` unable to fetch 'hasMany' referred by array of `\MongoId` (klimov-paul)
|
||||
|
||||
|
||||
2.0.0-rc September 27, 2014
|
||||
---------------------------
|
||||
|
||||
- Bug #2337: `yii\mongodb\Collection::buildLikeCondition()` fixed to escape regular expression (klimov-paul)
|
||||
- Bug #3385: Fixed "The 'connected' property is deprecated" (samdark)
|
||||
- Bug #4879: Fixed `yii\mongodb\Collection::buildInCondition()` handles non-sequent key arrays (klimov-paul)
|
||||
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
|
||||
- Enh #3778: Gii generator for Active Record model added (klimov-paul)
|
||||
- Enh #3947: Migration support added (klimov-paul)
|
||||
- Enh #4048: Added `init` event to `ActiveQuery` classes (qiangxue)
|
||||
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
|
||||
- Enh #4335: `yii\mongodb\log\MongoDbTarget` log target added (klimov-paul)
|
||||
|
||||
|
||||
2.0.0-beta April 13, 2014
|
||||
-------------------------
|
||||
|
||||
- Initial release.
|
||||
32
vendor/yiisoft/yii2-mongodb/LICENSE.md
vendored
Normal file
32
vendor/yiisoft/yii2-mongodb/LICENSE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
The Yii framework is free software. It is released under the terms of
|
||||
the following BSD License.
|
||||
|
||||
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Yii Software LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
62
vendor/yiisoft/yii2-mongodb/README.md
vendored
Normal file
62
vendor/yiisoft/yii2-mongodb/README.md
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<p align="center">
|
||||
<a href="https://www.mongodb.com/" target="_blank" rel="external">
|
||||
<img src="https://webassets.mongodb.com/_com_assets/cms/mongodb-logo-rgb-j6w271g1xn.jpg" height="80px">
|
||||
</a>
|
||||
<h1 align="center">MongoDB Extension for Yii 2</h1>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
This extension provides the [MongoDB](https://www.mongodb.com/) integration for the [Yii framework 2.0](http://www.yiiframework.com).
|
||||
|
||||
For license information check the [LICENSE](LICENSE.md)-file.
|
||||
|
||||
Documentation is at [docs/guide/README.md](docs/guide/README.md).
|
||||
|
||||
[](https://packagist.org/packages/yiisoft/yii2-mongodb)
|
||||
[](https://packagist.org/packages/yiisoft/yii2-mongodb)
|
||||
[](https://travis-ci.org/yiisoft/yii2-mongodb)
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
This extension requires [MongoDB PHP Extension](http://us1.php.net/manual/en/set.mongodb.php) version 1.0.0 or higher.
|
||||
|
||||
This extension requires MongoDB server version 3.0 or higher.
|
||||
|
||||
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
|
||||
|
||||
Either run
|
||||
|
||||
```
|
||||
php composer.phar require --prefer-dist yiisoft/yii2-mongodb
|
||||
```
|
||||
|
||||
or add
|
||||
|
||||
```
|
||||
"yiisoft/yii2-mongodb": "~2.1.0"
|
||||
```
|
||||
|
||||
to the require section of your composer.json.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To use this extension, simply add the following code in your application configuration:
|
||||
|
||||
```php
|
||||
return [
|
||||
//....
|
||||
'components' => [
|
||||
'mongodb' => [
|
||||
'class' => '\yii\mongodb\Connection',
|
||||
'dsn' => 'mongodb://@localhost:27017/mydatabase',
|
||||
'options' => [
|
||||
"username" => "Username",
|
||||
"password" => "Password"
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
```
|
||||
52
vendor/yiisoft/yii2-mongodb/UPGRADE.md
vendored
Normal file
52
vendor/yiisoft/yii2-mongodb/UPGRADE.md
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
Upgrading Instructions for Yii Framework v2
|
||||
===========================================
|
||||
|
||||
!!!IMPORTANT!!!
|
||||
|
||||
The following upgrading instructions are cumulative. That is,
|
||||
if you want to upgrade from version A to version C and there is
|
||||
version B between A and C, you need to following the instructions
|
||||
for both A and B.
|
||||
|
||||
Upgrade from Yii 2.0.5
|
||||
----------------------
|
||||
|
||||
* PHP [mongodb](http://php.net/manual/en/set.mongodb.php) extension is now used instead of [mongo](http://php.net/manual/en/book.mongo.php).
|
||||
Make sure you have 'mongodb' extension at your environment. Some features based on old driver may become unavailable.
|
||||
In particular: fields `Connection::mongoClient`, `Database::mongoDb` and `Collection::mongoCollection` are no longer exist.
|
||||
Old driver type classes such as `\MongoId`, `\MongoCode`, `\MongoDate` and so on, are no longer returned or
|
||||
recognized. Make sure you are using their analogs from `\MongoDB\BSON\*` namespace.
|
||||
|
||||
* MongoDB server versions < 3.0 are no longer supported. Make sure you are running MongoDB server >= 3.0
|
||||
|
||||
* The signature of the following `\yii\mongodb\Collection` methods has been changed: `aggregate()`, `distinct()`,
|
||||
`find()`, `findOne()`, `findAndModify()`. Make sure you invoke those methods correctly. In case you are
|
||||
extending `\yii\mongodb\Collection`, you should check, if overridden methods match parent declaration.
|
||||
|
||||
* Command and query composition methods at `\yii\mongodb\Collection`, such as `buildCondition()`, `ensureMongoId()`
|
||||
and so on, have been removed. Use `\yii\mongodb\QueryBuilder` methods instead.
|
||||
|
||||
* Method `Database::executeCommand()` has been removed. Use `Command` class for plain MongoDB command execution.
|
||||
You may create command with database scope using `Database::createCommand()` method.
|
||||
|
||||
* Method `Collection::fullTextSearch()` has been removed. Use `$text` query condition instead.
|
||||
|
||||
* Method `Collection::getName()` has been removed. Use `Collection::name` in order to get collection self name.
|
||||
|
||||
* For GridFS `yii\mongodb\file\Download` is returned instead of `\MongoGridFSFile` for the query result set.
|
||||
|
||||
* Cursor composed via `yii\mongodb\file\Collection::find()` now returns result in the same format as `yii\mongodb\file\Query::one()`.
|
||||
If you wish to perform file manipulations on returned row you should use `file` key instead of direct method invocations.
|
||||
|
||||
Upgrade from Yii 2.0.1
|
||||
----------------------
|
||||
|
||||
* MongoDB PHP extension min version raised up to 1.5.0. You should upgrade your environment in case you are
|
||||
using older version.
|
||||
|
||||
Upgrade from Yii 2.0.0
|
||||
----------------------
|
||||
|
||||
* MongoDB PHP extension min version raised up to 1.4.0. You should upgrade your environment in case you are
|
||||
using older version.
|
||||
|
||||
38
vendor/yiisoft/yii2-mongodb/composer.json
vendored
Normal file
38
vendor/yiisoft/yii2-mongodb/composer.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "yiisoft/yii2-mongodb",
|
||||
"description": "MongoDB extension for the Yii framework",
|
||||
"keywords": ["yii2", "mongo", "mongodb", "active-record", "gridfs"],
|
||||
"type": "yii2-extension",
|
||||
"license": "BSD-3-Clause",
|
||||
"support": {
|
||||
"issues": "https://github.com/yiisoft/yii2-mongodb/issues",
|
||||
"forum": "http://www.yiiframework.com/forum/",
|
||||
"wiki": "http://www.yiiframework.com/wiki/",
|
||||
"irc": "irc://irc.freenode.net/yii",
|
||||
"source": "https://github.com/yiisoft/yii2-mongodb"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paul Klimov",
|
||||
"email": "klimov.paul@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"yiisoft/yii2": "~2.0.14",
|
||||
"ext-mongodb": ">=1.0.0"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://asset-packagist.org"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": { "yii\\mongodb\\": "src" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
131
vendor/yiisoft/yii2-mongodb/src/ActiveFixture.php
vendored
Normal file
131
vendor/yiisoft/yii2-mongodb/src/ActiveFixture.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\test\BaseActiveFixture;
|
||||
|
||||
/**
|
||||
* ActiveFixture represents a fixture backed up by a [[modelClass|MongoDB ActiveRecord class]] or a [[collectionName|MongoDB collection]].
|
||||
*
|
||||
* Either [[modelClass]] or [[collectionName]] must be set. You should also provide fixture data in the file
|
||||
* specified by [[dataFile]] or overriding [[getData()]] if you want to use code to generate the fixture data.
|
||||
*
|
||||
* When the fixture is being loaded, it will first call [[resetCollection()]] to remove any existing data in the collection.
|
||||
* It will then populate the collection with the data returned by [[getData()]].
|
||||
*
|
||||
* After the fixture is loaded, you can access the loaded data via the [[data]] property. If you set [[modelClass]],
|
||||
* you will also be able to retrieve an instance of [[modelClass]] with the populated data via [[getModel()]].
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveFixture extends BaseActiveFixture
|
||||
{
|
||||
/**
|
||||
* @var Connection|string the DB connection object or the application component ID of the DB connection.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var string|array the collection name that this fixture is about. If this property is not set,
|
||||
* the collection name will be determined via [[modelClass]].
|
||||
* @see Connection::getCollection()
|
||||
*/
|
||||
public $collectionName;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if (!isset($this->modelClass) && !isset($this->collectionName)) {
|
||||
throw new InvalidConfigException('Either "modelClass" or "collectionName" must be set.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the fixture data.
|
||||
* The default implementation will first reset the MongoDB collection and then populate it with the data
|
||||
* returned by [[getData()]].
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$this->resetCollection();
|
||||
$this->data = [];
|
||||
$data = $this->getData();
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
$this->getCollection()->batchInsert($data);
|
||||
foreach ($data as $alias => $row) {
|
||||
$this->data[$alias] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns collection used by this fixture.
|
||||
* @return Collection related collection.
|
||||
*/
|
||||
protected function getCollection()
|
||||
{
|
||||
return $this->db->getCollection($this->getCollectionName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns collection name used by this fixture.
|
||||
* @return array|string related collection name
|
||||
*/
|
||||
protected function getCollectionName()
|
||||
{
|
||||
if ($this->collectionName) {
|
||||
return $this->collectionName;
|
||||
}
|
||||
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
return $modelClass::collectionName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fixture data.
|
||||
*
|
||||
* This method is called by [[loadData()]] to get the needed fixture data.
|
||||
*
|
||||
* The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
|
||||
* The file should return an array of data rows (column name => column value), each corresponding to a row in the collection.
|
||||
*
|
||||
* If the data file does not exist, an empty array will be returned.
|
||||
*
|
||||
* @return array the data rows to be inserted into the collection.
|
||||
*/
|
||||
protected function getData()
|
||||
{
|
||||
if ($this->dataFile === null) {
|
||||
$class = new \ReflectionClass($this);
|
||||
|
||||
$collectionName = $this->getCollectionName();
|
||||
$dataFile = dirname($class->getFileName()) . '/data/' . (is_array($collectionName) ? implode('.', $collectionName) : $collectionName) . '.php';
|
||||
|
||||
return is_file($dataFile) ? require($dataFile) : [];
|
||||
}
|
||||
|
||||
return parent::getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all existing data from the specified collection and resets sequence number if any.
|
||||
* This method is called before populating fixture data into the collection associated with this fixture.
|
||||
*/
|
||||
protected function resetCollection()
|
||||
{
|
||||
$this->getCollection()->remove();
|
||||
}
|
||||
}
|
||||
222
vendor/yiisoft/yii2-mongodb/src/ActiveQuery.php
vendored
Normal file
222
vendor/yiisoft/yii2-mongodb/src/ActiveQuery.php
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\ActiveQueryTrait;
|
||||
use yii\db\ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* ActiveQuery represents a Mongo query associated with an Active Record class.
|
||||
*
|
||||
* An ActiveQuery can be a normal query or be used in a relational context.
|
||||
*
|
||||
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
|
||||
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
|
||||
*
|
||||
* Normal Query
|
||||
* ------------
|
||||
*
|
||||
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
|
||||
*
|
||||
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
|
||||
* [[orderBy()]] to customize the query options.
|
||||
*
|
||||
* ActiveQuery also provides the following additional query options:
|
||||
*
|
||||
* - [[with()]]: list of relations that this query should be performed with.
|
||||
* - [[asArray()]]: whether to return each record as an array.
|
||||
*
|
||||
* These options can be configured using methods of the same name. For example:
|
||||
*
|
||||
* ```php
|
||||
* $customers = Customer::find()->with('orders')->asArray()->all();
|
||||
* ```
|
||||
*
|
||||
* Relational query
|
||||
* ----------------
|
||||
*
|
||||
* In relational context ActiveQuery represents a relation between two Active Record classes.
|
||||
*
|
||||
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
|
||||
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
|
||||
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
|
||||
*
|
||||
* A relation is specified by [[link]] which represents the association between columns
|
||||
* of different collections; and the multiplicity of the relation is indicated by [[multiple]].
|
||||
*
|
||||
* If a relation involves a junction collection, it may be specified by [[via()]].
|
||||
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
|
||||
* marks a relation as inverse of another relation.
|
||||
*
|
||||
* @property Collection $collection Collection instance. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveQuery extends Query implements ActiveQueryInterface
|
||||
{
|
||||
use ActiveQueryTrait;
|
||||
use ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* @event Event an event that is triggered when the query is initialized via [[init()]].
|
||||
*/
|
||||
const EVENT_INIT = 'init';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param array $modelClass the model class associated with this query
|
||||
* @param array $config configurations to be applied to the newly created query object
|
||||
*/
|
||||
public function __construct($modelClass, $config = [])
|
||||
{
|
||||
$this->modelClass = $modelClass;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
* This method is called at the end of the constructor. The default implementation will trigger
|
||||
* an [[EVENT_INIT]] event. If you override this method, make sure you call the parent implementation at the end
|
||||
* to ensure triggering of the event.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->trigger(self::EVENT_INIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepare()
|
||||
{
|
||||
if ($this->primaryModel !== null) {
|
||||
// lazy loading
|
||||
if ($this->via instanceof self) {
|
||||
// via pivot collection
|
||||
$viaModels = $this->via->findJunctionRows([$this->primaryModel]);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/* @var $viaQuery ActiveQuery */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
if ($viaQuery->multiple) {
|
||||
$viaModels = $viaQuery->all();
|
||||
$this->primaryModel->populateRelation($viaName, $viaModels);
|
||||
} else {
|
||||
$model = $viaQuery->one();
|
||||
$this->primaryModel->populateRelation($viaName, $model);
|
||||
$viaModels = $model === null ? [] : [$model];
|
||||
}
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels([$this->primaryModel]);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns all results as an array.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If null, the Mongo connection returned by [[modelClass]] will be used.
|
||||
* @return array|ActiveRecord the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null)
|
||||
{
|
||||
return parent::all($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns a single row of result.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If null, the Mongo connection returned by [[modelClass]] will be used.
|
||||
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
|
||||
* the query result may be either an array or an ActiveRecord object. Null will be returned
|
||||
* if the query results in nothing.
|
||||
*/
|
||||
public function one($db = null)
|
||||
{
|
||||
$row = parent::one($db);
|
||||
if ($row !== false) {
|
||||
$models = $this->populate([$row]);
|
||||
return reset($models) ?: null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs 'findAndModify' query and returns a single row of result.
|
||||
* Warning: in case 'new' option is set to 'false' (which is by default) usage of this method may lead
|
||||
* to unexpected behavior at some Active Record features, because object will be populated by outdated data.
|
||||
* @param array $update update criteria
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* @return ActiveRecord|array|null the original document, or the modified document when $options['new'] is set.
|
||||
* Depending on the setting of [[asArray]], the query result may be either an array or an ActiveRecord object.
|
||||
* Null will be returned if the query results in nothing.
|
||||
*/
|
||||
public function modify($update, $options = [], $db = null)
|
||||
{
|
||||
$row = parent::modify($update, $options, $db);
|
||||
if ($row !== null) {
|
||||
$models = $this->populate([$row]);
|
||||
return reset($models) ?: null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Mongo collection for this query.
|
||||
* @param Connection $db Mongo connection.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public function getCollection($db = null)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
if ($this->from === null) {
|
||||
$this->from = $modelClass::collectionName();
|
||||
}
|
||||
|
||||
return $db->getCollection($this->from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the raw query results into the format as specified by this query.
|
||||
* This method is internally used to convert the data fetched from MongoDB
|
||||
* into the format as required by this query.
|
||||
* @param array $rows the raw query result from MongoDB
|
||||
* @return array the converted query result
|
||||
*/
|
||||
public function populate($rows)
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$models = $this->createModels($rows);
|
||||
if (!empty($this->with)) {
|
||||
$this->findWith($this->with, $models);
|
||||
}
|
||||
if (!$this->asArray) {
|
||||
foreach ($models as $model) {
|
||||
$model->afterFind();
|
||||
}
|
||||
}
|
||||
|
||||
return parent::populate($models);
|
||||
}
|
||||
}
|
||||
414
vendor/yiisoft/yii2-mongodb/src/ActiveRecord.php
vendored
Normal file
414
vendor/yiisoft/yii2-mongodb/src/ActiveRecord.php
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\Type;
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\db\BaseActiveRecord;
|
||||
use yii\db\StaleObjectException;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* ActiveRecord is the base class for classes representing Mongo documents in terms of objects.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class ActiveRecord extends BaseActiveRecord
|
||||
{
|
||||
/**
|
||||
* Returns the Mongo connection used by this AR class.
|
||||
* By default, the "mongodb" application component is used as the Mongo connection.
|
||||
* You may override this method if you want to use a different database connection.
|
||||
* @return Connection the database connection used by this AR class.
|
||||
*/
|
||||
public static function getDb()
|
||||
{
|
||||
return Yii::$app->get('mongodb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all documents in the collection using the provided attribute values and conditions.
|
||||
* For example, to change the status to be 1 for all customers whose status is 2:
|
||||
*
|
||||
* ```php
|
||||
* Customer::updateAll(['status' => 1], ['status' => 2]);
|
||||
* ```
|
||||
*
|
||||
* @param array $attributes attribute values (name-value pairs) to be saved into the collection
|
||||
* @param array $condition description of the objects to update.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int the number of documents updated.
|
||||
*/
|
||||
public static function updateAll($attributes, $condition = [], $options = [])
|
||||
{
|
||||
return static::getCollection()->update($condition, $attributes, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all documents in the collection using the provided counter changes and conditions.
|
||||
* For example, to increment all customers' age by 1,
|
||||
*
|
||||
* ```php
|
||||
* Customer::updateAllCounters(['age' => 1]);
|
||||
* ```
|
||||
*
|
||||
* @param array $counters the counters to be updated (attribute name => increment value).
|
||||
* Use negative values if you want to decrement the counters.
|
||||
* @param array $condition description of the objects to update.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int the number of documents updated.
|
||||
*/
|
||||
public static function updateAllCounters($counters, $condition = [], $options = [])
|
||||
{
|
||||
return static::getCollection()->update($condition, ['$inc' => $counters], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes documents in the collection using the provided conditions.
|
||||
* WARNING: If you do not specify any condition, this method will delete documents rows in the collection.
|
||||
*
|
||||
* For example, to delete all customers whose status is 3:
|
||||
*
|
||||
* ```php
|
||||
* Customer::deleteAll(['status' => 3]);
|
||||
* ```
|
||||
*
|
||||
* @param array $condition description of the objects to delete.
|
||||
* Please refer to [[Query::where()]] on how to specify this parameter.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int the number of documents deleted.
|
||||
*/
|
||||
public static function deleteAll($condition = [], $options = [])
|
||||
{
|
||||
return static::getCollection()->remove($condition, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
|
||||
*/
|
||||
public static function find()
|
||||
{
|
||||
return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares the name of the Mongo collection associated with this AR class.
|
||||
*
|
||||
* Collection name can be either a string or array:
|
||||
* - if string considered as the name of the collection inside the default database.
|
||||
* - if array - first element considered as the name of the database, second - as
|
||||
* name of collection inside that database
|
||||
*
|
||||
* By default this method returns the class name as the collection name by calling [[Inflector::camel2id()]].
|
||||
* For example, 'Customer' becomes 'customer', and 'OrderItem' becomes
|
||||
* 'order_item'. You may override this method if the collection is not named after this convention.
|
||||
* @return string|array the collection name
|
||||
*/
|
||||
public static function collectionName()
|
||||
{
|
||||
return Inflector::camel2id(StringHelper::basename(get_called_class()), '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Mongo collection instance for this AR class.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public static function getCollection()
|
||||
{
|
||||
return static::getDb()->getCollection(static::collectionName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary key name(s) for this AR class.
|
||||
* The default implementation will return ['_id'].
|
||||
*
|
||||
* Note that an array should be returned even for a collection with single primary key.
|
||||
*
|
||||
* @return string[] the primary keys of the associated Mongo collection.
|
||||
*/
|
||||
public static function primaryKey()
|
||||
{
|
||||
return ['_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all attribute names of the model.
|
||||
* This method must be overridden by child classes to define available attributes.
|
||||
* Note: primary key attribute "_id" should be always present in returned array.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* public function attributes()
|
||||
* {
|
||||
* return ['_id', 'name', 'address', 'status'];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @throws \yii\base\InvalidConfigException if not implemented
|
||||
* @return array list of attribute names.
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
throw new InvalidConfigException('The attributes() method of mongodb ActiveRecord has to be implemented by child classes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a row into the associated Mongo collection using the attribute values of this record.
|
||||
*
|
||||
* This method performs the following steps in order:
|
||||
*
|
||||
* 1. call [[beforeValidate()]] when `$runValidation` is true. If validation
|
||||
* fails, it will skip the rest of the steps;
|
||||
* 2. call [[afterValidate()]] when `$runValidation` is true.
|
||||
* 3. call [[beforeSave()]]. If the method returns false, it will skip the
|
||||
* rest of the steps;
|
||||
* 4. insert the record into collection. If this fails, it will skip the rest of the steps;
|
||||
* 5. call [[afterSave()]];
|
||||
*
|
||||
* In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
|
||||
* [[EVENT_BEFORE_INSERT]], [[EVENT_AFTER_INSERT]] and [[EVENT_AFTER_VALIDATE]]
|
||||
* will be raised by the corresponding methods.
|
||||
*
|
||||
* Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
|
||||
*
|
||||
* If the primary key is null during insertion, it will be populated with the actual
|
||||
* value after insertion.
|
||||
*
|
||||
* For example, to insert a customer record:
|
||||
*
|
||||
* ```php
|
||||
* $customer = new Customer();
|
||||
* $customer->name = $name;
|
||||
* $customer->email = $email;
|
||||
* $customer->insert();
|
||||
* ```
|
||||
*
|
||||
* @param bool $runValidation whether to perform validation before saving the record.
|
||||
* If the validation fails, the record will not be inserted into the collection.
|
||||
* @param array $attributes list of attributes that need to be saved. Defaults to null,
|
||||
* meaning all attributes that are loaded will be saved.
|
||||
* @return bool whether the attributes are valid and the record is inserted successfully.
|
||||
* @throws \Exception in case insert failed.
|
||||
*/
|
||||
public function insert($runValidation = true, $attributes = null)
|
||||
{
|
||||
if ($runValidation && !$this->validate($attributes)) {
|
||||
return false;
|
||||
}
|
||||
$result = $this->insertInternal($attributes);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ActiveRecord::insert()
|
||||
*/
|
||||
protected function insertInternal($attributes = null)
|
||||
{
|
||||
if (!$this->beforeSave(true)) {
|
||||
return false;
|
||||
}
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
if (empty($values)) {
|
||||
$currentAttributes = $this->getAttributes();
|
||||
foreach ($this->primaryKey() as $key) {
|
||||
if (isset($currentAttributes[$key])) {
|
||||
$values[$key] = $currentAttributes[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
$newId = static::getCollection()->insert($values);
|
||||
if ($newId !== null) {
|
||||
$this->setAttribute('_id', $newId);
|
||||
$values['_id'] = $newId;
|
||||
}
|
||||
|
||||
$changedAttributes = array_fill_keys(array_keys($values), null);
|
||||
$this->setOldAttributes($values);
|
||||
$this->afterSave(true, $changedAttributes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ActiveRecord::update()
|
||||
* @throws StaleObjectException
|
||||
*/
|
||||
protected function updateInternal($attributes = null)
|
||||
{
|
||||
if (!$this->beforeSave(false)) {
|
||||
return false;
|
||||
}
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
if (empty($values)) {
|
||||
$this->afterSave(false, $values);
|
||||
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.');
|
||||
}
|
||||
|
||||
if (isset($values[$lock])) {
|
||||
$this->$lock = $values[$lock];
|
||||
}
|
||||
|
||||
$changedAttributes = [];
|
||||
foreach ($values as $name => $value) {
|
||||
$changedAttributes[$name] = $this->getOldAttribute($name);
|
||||
$this->setOldAttribute($name, $value);
|
||||
}
|
||||
$this->afterSave(false, $changedAttributes);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the document corresponding to this active record from the collection.
|
||||
*
|
||||
* This method performs the following steps in order:
|
||||
*
|
||||
* 1. call [[beforeDelete()]]. If the method returns false, it will skip the
|
||||
* rest of the steps;
|
||||
* 2. delete the document from the collection;
|
||||
* 3. call [[afterDelete()]].
|
||||
*
|
||||
* In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]
|
||||
* will be raised by the corresponding methods.
|
||||
*
|
||||
* @return int|bool the number of documents deleted, or false if the deletion is unsuccessful for some reason.
|
||||
* Note that it is possible the number of documents deleted is 0, even though the deletion execution is successful.
|
||||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data
|
||||
* being deleted is outdated.
|
||||
* @throws \Exception in case delete failed.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$result = false;
|
||||
if ($this->beforeDelete()) {
|
||||
$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 collection 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.
|
||||
* @param ActiveRecord $record record to compare to
|
||||
* @return bool whether the two active records refer to the same row in the same Mongo collection.
|
||||
*/
|
||||
public function equals($record)
|
||||
{
|
||||
if ($this->isNewRecord || $record->isNewRecord) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->collectionName() === $record->collectionName() && (string) $this->getPrimaryKey() === (string) $record->getPrimaryKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toArray(array $fields = [], array $expand = [], $recursive = true)
|
||||
{
|
||||
$data = parent::toArray($fields, $expand, false);
|
||||
if (!$recursive) {
|
||||
return $data;
|
||||
}
|
||||
return $this->toArrayInternal($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts data to array recursively, converting MongoDB BSON objects to readable values.
|
||||
* @param mixed $data the data to be converted into an array.
|
||||
* @return array the array representation of the data.
|
||||
* @since 2.1
|
||||
*/
|
||||
private function toArrayInternal($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$data[$key] = $this->toArrayInternal($value);
|
||||
}
|
||||
if (is_object($value)) {
|
||||
if ($value instanceof Type) {
|
||||
$data[$key] = $this->dumpBsonObject($value);
|
||||
} else {
|
||||
$data[$key] = ArrayHelper::toArray($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
} elseif (is_object($data)) {
|
||||
return ArrayHelper::toArray($data);
|
||||
}
|
||||
return [$data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts MongoDB BSON object to readable value.
|
||||
* @param Type $object MongoDB BSON object.
|
||||
* @return array|string object dump value.
|
||||
* @since 2.1
|
||||
*/
|
||||
private function dumpBsonObject(Type $object)
|
||||
{
|
||||
if ($object instanceof Binary) {
|
||||
return $object->getData();
|
||||
}
|
||||
if (method_exists($object, '__toString')) {
|
||||
return $object->__toString();
|
||||
}
|
||||
return ArrayHelper::toArray($object);
|
||||
}
|
||||
}
|
||||
189
vendor/yiisoft/yii2-mongodb/src/BatchQueryResult.php
vendored
Normal file
189
vendor/yiisoft/yii2-mongodb/src/BatchQueryResult.php
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* BatchQueryResult represents a batch query from which you can retrieve data in batches.
|
||||
*
|
||||
* You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by
|
||||
* calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the `Iterator` interface,
|
||||
* you can iterate it to obtain a batch of data in each iteration. For example,
|
||||
*
|
||||
* ```php
|
||||
* $query = (new Query())->from('user');
|
||||
* foreach ($query->batch() as $i => $users) {
|
||||
* // $users represents the rows in the $i-th batch
|
||||
* }
|
||||
* foreach ($query->each() as $user) {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class BatchQueryResult extends BaseObject implements \Iterator
|
||||
{
|
||||
/**
|
||||
* @var Connection the MongoDB connection to be used when performing batch query.
|
||||
* If null, the "mongodb" application component will be used.
|
||||
*/
|
||||
public $db;
|
||||
/**
|
||||
* @var Query the query object associated with this batch query.
|
||||
* Do not modify this property directly unless after [[reset()]] is called explicitly.
|
||||
*/
|
||||
public $query;
|
||||
/**
|
||||
* @var int the number of rows to be returned in each batch.
|
||||
*/
|
||||
public $batchSize = 100;
|
||||
/**
|
||||
* @var bool whether to return a single row during each iteration.
|
||||
* If false, a whole batch of rows will be returned in each iteration.
|
||||
*/
|
||||
public $each = false;
|
||||
|
||||
/**
|
||||
* @var array the data retrieved in the current batch
|
||||
*/
|
||||
private $_batch;
|
||||
/**
|
||||
* @var mixed the value for the current iteration
|
||||
*/
|
||||
private $_value;
|
||||
/**
|
||||
* @var string|int the key for the current iteration
|
||||
*/
|
||||
private $_key;
|
||||
/**
|
||||
* @var \Iterator
|
||||
*/
|
||||
private $_iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Resets the batch query.
|
||||
* This method will clean up the existing batch query so that a new batch query can be performed.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_iterator = null;
|
||||
$this->_batch = null;
|
||||
$this->_value = null;
|
||||
$this->_key = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the iterator to the initial state.
|
||||
* This method is required by the interface Iterator.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal pointer to the next dataset.
|
||||
* This method is required by the interface Iterator.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
|
||||
$this->_batch = $this->fetchData();
|
||||
reset($this->_batch);
|
||||
}
|
||||
|
||||
if ($this->each) {
|
||||
$this->_value = current($this->_batch);
|
||||
if ($this->query->indexBy !== null) {
|
||||
$this->_key = key($this->_batch);
|
||||
} elseif (key($this->_batch) !== null) {
|
||||
$this->_key++;
|
||||
} else {
|
||||
$this->_key = null;
|
||||
}
|
||||
} else {
|
||||
$this->_value = $this->_batch;
|
||||
$this->_key = $this->_key === null ? 0 : $this->_key + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next batch of data.
|
||||
* @return array the data fetched
|
||||
*/
|
||||
protected function fetchData()
|
||||
{
|
||||
if ($this->_iterator === null) {
|
||||
if (empty($this->query->orderBy)) {
|
||||
// setting cursor batch size may setup implicit limit on the query with 'sort'
|
||||
// @see https://jira.mongodb.org/browse/PHP-457
|
||||
$this->query->addOptions(['batchSize' => $this->batchSize]);
|
||||
}
|
||||
$cursor = $this->query->buildCursor($this->db);
|
||||
$token = 'fetch cursor id = ' . $cursor->getId();
|
||||
Yii::info($token, __METHOD__);
|
||||
|
||||
if ($cursor instanceof \Iterator) {
|
||||
$this->_iterator = $cursor;
|
||||
} else {
|
||||
$this->_iterator = new \IteratorIterator($cursor);
|
||||
}
|
||||
|
||||
$this->_iterator->rewind();
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$count = 0;
|
||||
|
||||
while ($count++ < $this->batchSize) {
|
||||
$row = $this->_iterator->current();
|
||||
if ($row === null) {
|
||||
break;
|
||||
}
|
||||
$this->_iterator->next();
|
||||
//var_dump($row);
|
||||
$rows[] = $row;
|
||||
}
|
||||
return $this->query->populate($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current dataset.
|
||||
* This method is required by the interface Iterator.
|
||||
* @return int the index of the current row.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current dataset.
|
||||
* This method is required by the interface Iterator.
|
||||
* @return mixed the current dataset.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether there is a valid dataset at the current position.
|
||||
* This method is required by the interface Iterator.
|
||||
* @return bool whether there is a valid dataset at the current position.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return !empty($this->_batch);
|
||||
}
|
||||
}
|
||||
198
vendor/yiisoft/yii2-mongodb/src/Cache.php
vendored
Normal file
198
vendor/yiisoft/yii2-mongodb/src/Cache.php
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\di\Instance;
|
||||
|
||||
/**
|
||||
* Cache implements a cache application component by storing cached data in a MongoDB.
|
||||
*
|
||||
* By default, Cache stores session data in a MongoDB collection named 'cache' inside the default database.
|
||||
* This collection is better to be pre-created with fields 'id' and 'expire' indexed.
|
||||
* The collection name can be changed by setting [[cacheCollection]].
|
||||
*
|
||||
* Please refer to [[\yii\caching\Cache]] for common cache operations that are supported by Cache.
|
||||
*
|
||||
* The following example shows how you can configure the application to use Cache:
|
||||
*
|
||||
* ```php
|
||||
* 'cache' => [
|
||||
* 'class' => 'yii\mongodb\Cache',
|
||||
* // 'db' => 'mymongodb',
|
||||
* // 'cacheCollection' => 'my_cache',
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Cache extends \yii\caching\Cache
|
||||
{
|
||||
/**
|
||||
* @var Connection|array|string the MongoDB connection object or the application component ID of the MongoDB connection.
|
||||
* After the Cache object is created, if you want to change this property, you should only assign it
|
||||
* with a MongoDB connection object.
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var string|array the name of the MongoDB collection that stores the cache data.
|
||||
* Please refer to [[Connection::getCollection()]] on how to specify this parameter.
|
||||
* This collection is better to be pre-created with fields 'id' and 'expire' indexed.
|
||||
*/
|
||||
public $cacheCollection = 'cache';
|
||||
/**
|
||||
* @var int the probability (parts per million) that garbage collection (GC) should be performed
|
||||
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||
* This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all.
|
||||
*/
|
||||
public $gcProbability = 100;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the Cache component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid MongoDB connection.
|
||||
* @throws InvalidConfigException if [[db]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value from cache with a specified key.
|
||||
* This method should be implemented by child classes to retrieve the data
|
||||
* from specific cache storage.
|
||||
* @param string $key a unique key identifying the cached value
|
||||
* @return string|bool the value stored in cache, false if the value is not in the cache or expired.
|
||||
*/
|
||||
protected function getValue($key)
|
||||
{
|
||||
$query = new Query;
|
||||
$row = $query->select(['data'])
|
||||
->from($this->cacheCollection)
|
||||
->where([
|
||||
'id' => $key,
|
||||
'$or' => [
|
||||
[
|
||||
'expire' => 0
|
||||
],
|
||||
[
|
||||
'expire' => ['$gt' => time()]
|
||||
],
|
||||
],
|
||||
])
|
||||
->one($this->db);
|
||||
|
||||
if (empty($row)) {
|
||||
return false;
|
||||
}
|
||||
return $row['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a value identified by a key in cache.
|
||||
* This method should be implemented by child classes to store the data
|
||||
* in specific cache storage.
|
||||
* @param string $key the key identifying the value to be cached
|
||||
* @param string $value the value to be cached
|
||||
* @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||
* @return bool true if the value is successfully stored into cache, false otherwise
|
||||
*/
|
||||
protected function setValue($key, $value, $expire)
|
||||
{
|
||||
$result = $this->db->getCollection($this->cacheCollection)
|
||||
->update(['id' => $key], [
|
||||
'expire' => $expire > 0 ? $expire + time() : 0,
|
||||
'data' => $value,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
$this->gc();
|
||||
return true;
|
||||
}
|
||||
return $this->addValue($key, $value, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||
* This method should be implemented by child classes to store the data
|
||||
* in specific cache storage.
|
||||
* @param string $key the key identifying the value to be cached
|
||||
* @param string $value the value to be cached
|
||||
* @param int $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||
* @return bool true if the value is successfully stored into cache, false otherwise
|
||||
*/
|
||||
protected function addValue($key, $value, $expire)
|
||||
{
|
||||
$this->gc();
|
||||
|
||||
if ($expire > 0) {
|
||||
$expire += time();
|
||||
} else {
|
||||
$expire = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->db->getCollection($this->cacheCollection)
|
||||
->insert([
|
||||
'id' => $key,
|
||||
'expire' => $expire,
|
||||
'data' => $value,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value with the specified key from cache
|
||||
* This method should be implemented by child classes to delete the data from actual cache storage.
|
||||
* @param string $key the key of the value to be deleted
|
||||
* @return bool if no error happens during deletion
|
||||
*/
|
||||
protected function deleteValue($key)
|
||||
{
|
||||
$this->db->getCollection($this->cacheCollection)->remove(['id' => $key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all values from cache.
|
||||
* Child classes may implement this method to realize the flush operation.
|
||||
* @return bool whether the flush operation was successful.
|
||||
*/
|
||||
protected function flushValues()
|
||||
{
|
||||
$this->db->getCollection($this->cacheCollection)->remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the expired data values.
|
||||
* @param bool $force whether to enforce the garbage collection regardless of [[gcProbability]].
|
||||
* Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]].
|
||||
*/
|
||||
public function gc($force = false)
|
||||
{
|
||||
if ($force || mt_rand(0, 1000000) < $this->gcProbability) {
|
||||
$this->db->getCollection($this->cacheCollection)
|
||||
->remove([
|
||||
'expire' => [
|
||||
'$gt' => 0,
|
||||
'$lt' => time(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
436
vendor/yiisoft/yii2-mongodb/src/Collection.php
vendored
Normal file
436
vendor/yiisoft/yii2-mongodb/src/Collection.php
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use yii\base\BaseObject;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* Collection represents the Mongo collection information.
|
||||
*
|
||||
* A collection object is usually created by calling [[Database::getCollection()]] or [[Connection::getCollection()]].
|
||||
*
|
||||
* Collection provides the basic interface for the Mongo queries, mostly: insert, update, delete operations.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $collection = Yii::$app->mongodb->getCollection('customer');
|
||||
* $collection->insert(['name' => 'John Smith', 'status' => 1]);
|
||||
* ```
|
||||
*
|
||||
* Collection also provides shortcut for [[Command]] methods, such as [[group()]], [[mapReduce()]] and so on.
|
||||
*
|
||||
* To perform "find" queries, please use [[Query]] instead.
|
||||
*
|
||||
* @property string $fullName Full name of this collection, including database name. This property is
|
||||
* read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Collection extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Database MongoDB database instance.
|
||||
*/
|
||||
public $database;
|
||||
/**
|
||||
* @var string name of this collection.
|
||||
*/
|
||||
public $name;
|
||||
|
||||
|
||||
/**
|
||||
* @return string full name of this collection, including database name.
|
||||
*/
|
||||
public function getFullName()
|
||||
{
|
||||
return $this->database->name . '.' . $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops this collection.
|
||||
* @throws Exception on failure.
|
||||
* @return bool whether the operation successful.
|
||||
*/
|
||||
public function drop()
|
||||
{
|
||||
return $this->database->dropCollection($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of defined indexes.
|
||||
* @return array list of indexes info.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function listIndexes($options = [])
|
||||
{
|
||||
return $this->database->createCommand()->listIndexes($this->name, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates several indexes at once.
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* $collection = Yii::$app->mongo->getCollection('customer');
|
||||
* $collection->createIndexes([
|
||||
* [
|
||||
* 'key' => ['name'],
|
||||
* ],
|
||||
* [
|
||||
* 'key' => [
|
||||
* 'email' => 1,
|
||||
* 'address' => -1,
|
||||
* ],
|
||||
* 'name' => 'my_index'
|
||||
* ],
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param array $indexes indexes specification, each index should be specified as an array.
|
||||
* @param array[] $indexes indexes specification. Each specification should be an array in format: optionName => value
|
||||
* The main options are:
|
||||
*
|
||||
* - keys: array, column names with sort order, to be indexed. This option is mandatory.
|
||||
* - unique: bool, whether to create unique index.
|
||||
* - name: string, the name of the index, if not set it will be generated automatically.
|
||||
* - background: bool, whether to bind index in the background.
|
||||
* - sparse: bool, whether index should reference only documents with the specified field.
|
||||
*
|
||||
* See [[https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/#options-for-all-index-types]]
|
||||
* for the full list of options.
|
||||
* @return bool whether operation was successful.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createIndexes($indexes)
|
||||
{
|
||||
return $this->database->createCommand()->createIndexes($this->name, $indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops collection indexes by name.
|
||||
* @param string $indexes wildcard for name of the indexes to be dropped.
|
||||
* You can use `*` to drop all indexes.
|
||||
* @return int count of dropped indexes.
|
||||
*/
|
||||
public function dropIndexes($indexes)
|
||||
{
|
||||
$result = $this->database->createCommand()->dropIndexes($this->name, $indexes);
|
||||
return $result['nIndexesWas'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the collection and the specified fields.
|
||||
* @param array|string $columns column name or list of column names.
|
||||
* If array is given, each element in the array has as key the field name, and as
|
||||
* value either 1 for ascending sort, or -1 for descending sort.
|
||||
* You can specify field using native numeric key with the field name as a value,
|
||||
* in this case ascending sort will be used.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'name',
|
||||
* 'status' => -1,
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @throws Exception on failure.
|
||||
* @return bool whether the operation successful.
|
||||
*/
|
||||
public function createIndex($columns, $options = [])
|
||||
{
|
||||
$index = array_merge(['key' => $columns], $options);
|
||||
return $this->database->createCommand()->createIndexes($this->name, [$index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop indexes for specified column(s).
|
||||
* @param string|array $columns column name or list of column names.
|
||||
* If array is given, each element in the array has as key the field name, and as
|
||||
* value either 1 for ascending sort, or -1 for descending sort.
|
||||
* Use value 'text' to specify text index.
|
||||
* You can specify field using native numeric key with the field name as a value,
|
||||
* in this case ascending sort will be used.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'name',
|
||||
* 'status' => -1,
|
||||
* 'description' => 'text',
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @throws Exception on failure.
|
||||
* @return bool whether the operation successful.
|
||||
*/
|
||||
public function dropIndex($columns)
|
||||
{
|
||||
$existingIndexes = $this->listIndexes();
|
||||
|
||||
$indexKey = $this->database->connection->getQueryBuilder()->buildSortFields($columns);
|
||||
foreach ($existingIndexes as $index) {
|
||||
if ($index['key'] == $indexKey) {
|
||||
$this->database->createCommand()->dropIndexes($this->name, $index['name']);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Index plugin usage such as 'text' may cause unpredictable index 'key' structure, thus index name should be used
|
||||
$indexName = $this->database->connection->getQueryBuilder()->generateIndexName($indexKey);
|
||||
foreach ($existingIndexes as $index) {
|
||||
if ($index['name'] === $indexName) {
|
||||
$this->database->createCommand()->dropIndexes($this->name, $index['name']);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('Index to be dropped does not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all indexes for this collection.
|
||||
* @throws Exception on failure.
|
||||
* @return int count of dropped indexes.
|
||||
*/
|
||||
public function dropAllIndexes()
|
||||
{
|
||||
$result = $this->database->createCommand()->dropIndexes($this->name, '*');
|
||||
return $result['nIndexesWas'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor for the search results.
|
||||
* In order to perform "find" queries use [[Query]] class.
|
||||
* @param array $condition query condition
|
||||
* @param array $fields fields to be selected
|
||||
* @param array $options query options (available since 2.1).
|
||||
* @return \MongoDB\Driver\Cursor cursor for the search results
|
||||
* @see Query
|
||||
*/
|
||||
public function find($condition = [], $fields = [], $options = [])
|
||||
{
|
||||
if (!empty($fields)) {
|
||||
$options['projection'] = $fields;
|
||||
}
|
||||
return $this->database->createCommand()->find($this->name, $condition, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single document.
|
||||
* @param array $condition query condition
|
||||
* @param array $fields fields to be selected
|
||||
* @param array $options query options (available since 2.1).
|
||||
* @return array|null the single document. Null is returned if the query results in nothing.
|
||||
*/
|
||||
public function findOne($condition = [], $fields = [], $options = [])
|
||||
{
|
||||
$options['limit'] = 1;
|
||||
$cursor = $this->find($condition, $fields, $options);
|
||||
$rows = $cursor->toArray();
|
||||
return empty($rows) ? null : current($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a document and returns it.
|
||||
* @param array $condition query condition
|
||||
* @param array $update update criteria
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array|null the original document, or the modified document when $options['new'] is set.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function findAndModify($condition, $update, $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->findAndModify($this->name, $condition, $update, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new data into collection.
|
||||
* @param array|object $data data to be inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return \MongoDB\BSON\ObjectID new record ID instance.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function insert($data, $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->insert($this->name, $data, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts several new rows into collection.
|
||||
* @param array $rows array of arrays or objects to be inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array inserted data, each row will have "_id" key assigned to it.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function batchInsert($rows, $options = [])
|
||||
{
|
||||
$insertedIds = $this->database->createCommand()->batchInsert($this->name, $rows, $options);
|
||||
foreach ($rows as $key => $row) {
|
||||
$rows[$key]['_id'] = $insertedIds[$key];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the rows, which matches given criteria by given data.
|
||||
* Note: for "multi" mode Mongo requires explicit strategy "$set" or "$inc"
|
||||
* to be specified for the "newData". If no strategy is passed "$set" will be used.
|
||||
* @param array $condition description of the objects to update.
|
||||
* @param array $newData the object with which to update the matching records.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int|bool number of updated documents or whether operation was successful.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function update($condition, $newData, $options = [])
|
||||
{
|
||||
$writeResult = $this->database->createCommand()->update($this->name, $condition, $newData, $options);
|
||||
return $writeResult->getModifiedCount() + $writeResult->getUpsertedCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the existing database data, otherwise insert this data
|
||||
* @param array|object $data data to be updated/inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return \MongoId updated/new record id instance.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function save($data, $options = [])
|
||||
{
|
||||
if (empty($data['_id'])) {
|
||||
return $this->insert($data, $options);
|
||||
}
|
||||
$id = $data['_id'];
|
||||
unset($data['_id']);
|
||||
$this->update(['_id' => $id], ['$set' => $data], ['upsert' => true]);
|
||||
|
||||
return is_object($id) ? $id : new ObjectID($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes data from the collection.
|
||||
* @param array $condition description of records to remove.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int|bool number of updated documents or whether operation was successful.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function remove($condition = [], $options = [])
|
||||
{
|
||||
$options = array_merge(['limit' => 0], $options);
|
||||
$writeResult = $this->database->createCommand()->delete($this->name, $condition, $options);
|
||||
return $writeResult->getDeletedCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts records in this collection.
|
||||
* @param array $condition query condition
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int records count.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function count($condition = [], $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->count($this->name, $condition, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of distinct values for the given column across a collection.
|
||||
* @param string $column column to use.
|
||||
* @param array $condition query parameters.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array|bool array of distinct values, or "false" on failure.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function distinct($column, $condition = [], $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->distinct($this->name, $column, $condition, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs aggregation using Mongo Aggregation Framework.
|
||||
* In case 'cursor' option is specified [[\MongoDB\Driver\Cursor]] instance is returned,
|
||||
* otherwise - an array of aggregation results.
|
||||
* @param array $pipelines list of pipeline operators.
|
||||
* @param array $options optional parameters.
|
||||
* @return array|\MongoDB\Driver\Cursor the result of the aggregation.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function aggregate($pipelines, $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->aggregate($this->name, $pipelines, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs aggregation using Mongo "group" command.
|
||||
* @param mixed $keys fields to group by. If an array or non-code object is passed,
|
||||
* it will be the key used to group results. If instance of [[\MongoDB\BSON\Javascript]] passed,
|
||||
* it will be treated as a function that returns the key to group by.
|
||||
* @param array $initial Initial value of the aggregation counter object.
|
||||
* @param \MongoDB\BSON\Javascript|string $reduce function that takes two arguments (the current
|
||||
* document and the aggregation to this point) and does the aggregation.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param array $options optional parameters to the group command. Valid options include:
|
||||
* - condition - criteria for including a document in the aggregation.
|
||||
* - finalize - function called once per unique key that takes the final output of the reduce function.
|
||||
* @return array the result of the aggregation.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function group($keys, $initial, $reduce, $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->group($this->name, $keys, $initial, $reduce, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs aggregation using MongoDB "map-reduce" mechanism.
|
||||
* Note: this function will not return the aggregation result, instead it will
|
||||
* write it inside the another Mongo collection specified by "out" parameter.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $customerCollection = Yii::$app->mongo->getCollection('customer');
|
||||
* $resultCollectionName = $customerCollection->mapReduce(
|
||||
* 'function () {emit(this.status, this.amount)}',
|
||||
* 'function (key, values) {return Array.sum(values)}',
|
||||
* 'mapReduceOut',
|
||||
* ['status' => 3]
|
||||
* );
|
||||
* $query = new Query();
|
||||
* $results = $query->from($resultCollectionName)->all();
|
||||
* ```
|
||||
*
|
||||
* @param \MongoDB\BSON\Javascript|string $map function, which emits map data from collection.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param \MongoDB\BSON\Javascript|string $reduce function that takes two arguments (the map key
|
||||
* and the map values) and does the aggregation.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param string|array $out output collection name. It could be a string for simple output
|
||||
* ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
|
||||
* You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
|
||||
* @param array $condition criteria for including a document in the aggregation.
|
||||
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
|
||||
*
|
||||
* - sort: array, key to sort the input documents. The sort key must be in an existing index for this collection.
|
||||
* - limit: int, the maximum number of documents to return in the collection.
|
||||
* - finalize: \MongoDB\BSON\Javascript|string, function, which follows the reduce method and modifies the output.
|
||||
* - scope: array, specifies global variables that are accessible in the map, reduce and finalize functions.
|
||||
* - jsMode: bool, specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
|
||||
* - verbose: bool, specifies whether to include the timing information in the result information.
|
||||
*
|
||||
* @return string|array the map reduce output collection name or output results.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function mapReduce($map, $reduce, $out, $condition = [], $options = [])
|
||||
{
|
||||
return $this->database->createCommand()->mapReduce($this->name, $map, $reduce, $out, $condition, $options);
|
||||
}
|
||||
}
|
||||
842
vendor/yiisoft/yii2-mongodb/src/Command.php
vendored
Normal file
842
vendor/yiisoft/yii2-mongodb/src/Command.php
vendored
Normal file
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\Driver\BulkWrite;
|
||||
use MongoDB\Driver\Exception\RuntimeException;
|
||||
use MongoDB\Driver\ReadConcern;
|
||||
use MongoDB\Driver\ReadPreference;
|
||||
use MongoDB\Driver\WriteConcern;
|
||||
use MongoDB\Driver\WriteResult;
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\BaseObject;
|
||||
|
||||
/**
|
||||
* Command represents MongoDB statement such as command or query.
|
||||
*
|
||||
* A command object is usually created by calling [[Connection::createCommand()]] or [[Database::createCommand()]].
|
||||
* The statement it represents can be set via the [[document]] property.
|
||||
*
|
||||
* To execute a non-query command, such as 'listIndexes', 'count', 'distinct' and so on, call [[execute()]].
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $result = Yii::$app->mongodb->createCommand(['listIndexes' => 'some_collection'])->execute();
|
||||
* ```
|
||||
*
|
||||
* To execute a 'find' command, which return cursor, call [[query()]].
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $cursor = Yii::$app->mongodb->createCommand(['projection' => ['name' => true]])->query('some_collection');
|
||||
* ```
|
||||
*
|
||||
* To execute batch (bulk) operations, call [[executeBatch()]].
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* Yii::$app->mongodb->createCommand()
|
||||
* ->addInsert(['name' => 'new'])
|
||||
* ->addUpdate(['name' => 'existing'], ['name' => 'updated'])
|
||||
* ->addDelete(['name' => 'old'])
|
||||
* ->executeBatch('some_collection');
|
||||
* ```
|
||||
*
|
||||
* @property ReadConcern|string $readConcern Read concern to be used in this command.
|
||||
* @property ReadPreference $readPreference Read preference. Note that the type of this property differs in
|
||||
* getter and setter. See [[getReadPreference()]] and [[setReadPreference()]] for details.
|
||||
* @property WriteConcern|null $writeConcern Write concern to be used in this command. Note that the type of
|
||||
* this property differs in getter and setter. See [[getWriteConcern()]] and [[setWriteConcern()]] for details.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class Command extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Connection the MongoDB connection that this command is associated with.
|
||||
*/
|
||||
public $db;
|
||||
/**
|
||||
* @var string name of the database that this command is associated with.
|
||||
*/
|
||||
public $databaseName;
|
||||
/**
|
||||
* @var array command document contents.
|
||||
*/
|
||||
public $document = [];
|
||||
|
||||
/**
|
||||
* @var ReadPreference|int|string|null command read preference.
|
||||
*/
|
||||
private $_readPreference;
|
||||
/**
|
||||
* @var WriteConcern|int|string|null write concern to be used by this command.
|
||||
*/
|
||||
private $_writeConcern;
|
||||
/**
|
||||
* @var ReadConcern|string read concern to be used by this command
|
||||
*/
|
||||
private $_readConcern;
|
||||
|
||||
|
||||
/**
|
||||
* Returns read preference for this command.
|
||||
* @return ReadPreference read preference.
|
||||
*/
|
||||
public function getReadPreference()
|
||||
{
|
||||
if (!is_object($this->_readPreference)) {
|
||||
if ($this->_readPreference === null) {
|
||||
$this->_readPreference = $this->db->manager->getReadPreference();
|
||||
} elseif (is_scalar($this->_readPreference)) {
|
||||
$this->_readPreference = new ReadPreference($this->_readPreference);
|
||||
}
|
||||
}
|
||||
return $this->_readPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets read preference for this command.
|
||||
* @param ReadPreference|int|string|null $readPreference read reference, it can be specified as
|
||||
* instance of [[ReadPreference]] or scalar mode value, for example: `ReadPreference::RP_PRIMARY`.
|
||||
* @return $this self reference.
|
||||
*/
|
||||
public function setReadPreference($readPreference)
|
||||
{
|
||||
$this->_readPreference = $readPreference;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns write concern for this command.
|
||||
* @return WriteConcern|null write concern to be used in this command.
|
||||
*/
|
||||
public function getWriteConcern()
|
||||
{
|
||||
if ($this->_writeConcern !== null) {
|
||||
if (is_scalar($this->_writeConcern)) {
|
||||
$this->_writeConcern = new WriteConcern($this->_writeConcern);
|
||||
}
|
||||
}
|
||||
return $this->_writeConcern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets write concern for this command.
|
||||
* @param WriteConcern|int|string|null $writeConcern write concern, it can be an instance of [[WriteConcern]]
|
||||
* or its scalar mode value, for example: `majority`.
|
||||
* @return $this self reference
|
||||
*/
|
||||
public function setWriteConcern($writeConcern)
|
||||
{
|
||||
$this->_writeConcern = $writeConcern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retuns read concern for this command.
|
||||
* @return ReadConcern|string read concern to be used in this command.
|
||||
*/
|
||||
public function getReadConcern()
|
||||
{
|
||||
if ($this->_readConcern !== null) {
|
||||
if (is_scalar($this->_readConcern)) {
|
||||
$this->_readConcern = new ReadConcern($this->_readConcern);
|
||||
}
|
||||
}
|
||||
return $this->_readConcern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets read concern for this command.
|
||||
* @param ReadConcern|string $readConcern read concern, it can be an instance of [[ReadConcern]] or
|
||||
* scalar level value, for example: 'local'.
|
||||
* @return $this self reference
|
||||
*/
|
||||
public function setReadConcern($readConcern)
|
||||
{
|
||||
$this->_readConcern = $readConcern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this command.
|
||||
* @return \MongoDB\Driver\Cursor result cursor.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$databaseName = $this->databaseName === null ? $this->db->defaultDatabaseName : $this->databaseName;
|
||||
|
||||
$token = $this->log([$databaseName, 'command'], $this->document, __METHOD__);
|
||||
|
||||
try {
|
||||
$this->beginProfile($token, __METHOD__);
|
||||
|
||||
$this->db->open();
|
||||
$mongoCommand = new \MongoDB\Driver\Command($this->document);
|
||||
$cursor = $this->db->manager->executeCommand($databaseName, $mongoCommand, $this->getReadPreference());
|
||||
$cursor->setTypeMap($this->db->typeMap);
|
||||
|
||||
$this->endProfile($token, __METHOD__);
|
||||
} catch (RuntimeException $e) {
|
||||
$this->endProfile($token, __METHOD__);
|
||||
throw new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute commands batch (bulk).
|
||||
* @param string $collectionName collection name.
|
||||
* @param array $options batch options.
|
||||
* @return array array of 2 elements:
|
||||
*
|
||||
* - 'insertedIds' - contains inserted IDs.
|
||||
* - 'result' - [[\MongoDB\Driver\WriteResult]] instance.
|
||||
*
|
||||
* @throws Exception on failure.
|
||||
* @throws InvalidConfigException on invalid [[document]] format.
|
||||
*/
|
||||
public function executeBatch($collectionName, $options = [])
|
||||
{
|
||||
$databaseName = $this->databaseName === null ? $this->db->defaultDatabaseName : $this->databaseName;
|
||||
|
||||
$token = $this->log([$databaseName, $collectionName, 'bulkWrite'], $this->document, __METHOD__);
|
||||
|
||||
try {
|
||||
$this->beginProfile($token, __METHOD__);
|
||||
|
||||
$batch = new BulkWrite($options);
|
||||
|
||||
$insertedIds = [];
|
||||
foreach ($this->document as $key => $operation) {
|
||||
switch ($operation['type']) {
|
||||
case 'insert':
|
||||
$insertedIds[$key] = $batch->insert($operation['document']);
|
||||
break;
|
||||
case 'update':
|
||||
$batch->update($operation['condition'], $operation['document'], $operation['options']);
|
||||
break;
|
||||
case 'delete':
|
||||
$batch->delete($operation['condition'], isset($operation['options']) ? $operation['options'] : []);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidConfigException("Unsupported batch operation type '{$operation['type']}'");
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->open();
|
||||
$writeResult = $this->db->manager->executeBulkWrite($databaseName . '.' . $collectionName, $batch, $this->getWriteConcern());
|
||||
|
||||
$this->endProfile($token, __METHOD__);
|
||||
} catch (RuntimeException $e) {
|
||||
$this->endProfile($token, __METHOD__);
|
||||
throw new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return [
|
||||
'insertedIds' => $insertedIds,
|
||||
'result' => $writeResult,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this command as a mongo query
|
||||
* @param string $collectionName collection name
|
||||
* @param array $options query options.
|
||||
* @return \MongoDB\Driver\Cursor result cursor.
|
||||
* @throws Exception on failure
|
||||
*/
|
||||
public function query($collectionName, $options = [])
|
||||
{
|
||||
$databaseName = $this->databaseName === null ? $this->db->defaultDatabaseName : $this->databaseName;
|
||||
|
||||
$token = $this->log(
|
||||
'find',
|
||||
array_merge(
|
||||
[
|
||||
'ns' => $databaseName . '.' . $collectionName,
|
||||
'filter' => $this->document,
|
||||
],
|
||||
$options
|
||||
),
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
$readConcern = $this->getReadConcern();
|
||||
if ($readConcern !== null) {
|
||||
$options['readConcern'] = $readConcern;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->beginProfile($token, __METHOD__);
|
||||
|
||||
$query = new \MongoDB\Driver\Query($this->document, $options);
|
||||
$this->db->open();
|
||||
$cursor = $this->db->manager->executeQuery($databaseName . '.' . $collectionName, $query, $this->getReadPreference());
|
||||
$cursor->setTypeMap($this->db->typeMap);
|
||||
|
||||
$this->endProfile($token, __METHOD__);
|
||||
} catch (RuntimeException $e) {
|
||||
$this->endProfile($token, __METHOD__);
|
||||
throw new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops database associated with this command.
|
||||
* @return bool whether operation was successful.
|
||||
*/
|
||||
public function dropDatabase()
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->dropDatabase();
|
||||
|
||||
$result = current($this->execute()->toArray());
|
||||
return $result['ok'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection in database associated with this command.s
|
||||
* @param string $collectionName collection name
|
||||
* @param array $options collection options in format: "name" => "value"
|
||||
* @return bool whether operation was successful.
|
||||
*/
|
||||
public function createCollection($collectionName, array $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->createCollection($collectionName, $options);
|
||||
|
||||
$result = current($this->execute()->toArray());
|
||||
return $result['ok'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops specified collection.
|
||||
* @param string $collectionName name of the collection to be dropped.
|
||||
* @return bool whether operation was successful.
|
||||
*/
|
||||
public function dropCollection($collectionName)
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->dropCollection($collectionName);
|
||||
|
||||
$result = current($this->execute()->toArray());
|
||||
return $result['ok'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexes in the collection.
|
||||
* @param string $collectionName collection name.
|
||||
* @param array[] $indexes indexes specification. Each specification should be an array in format: optionName => value
|
||||
* The main options are:
|
||||
*
|
||||
* - keys: array, column names with sort order, to be indexed. This option is mandatory.
|
||||
* - unique: bool, whether to create unique index.
|
||||
* - name: string, the name of the index, if not set it will be generated automatically.
|
||||
* - background: bool, whether to bind index in the background.
|
||||
* - sparse: bool, whether index should reference only documents with the specified field.
|
||||
*
|
||||
* See [[https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/#options-for-all-index-types]]
|
||||
* for the full list of options.
|
||||
* @return bool whether operation was successful.
|
||||
*/
|
||||
public function createIndexes($collectionName, $indexes)
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->createIndexes($this->databaseName, $collectionName, $indexes);
|
||||
|
||||
$result = current($this->execute()->toArray());
|
||||
return $result['ok'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops collection indexes by name.
|
||||
* @param string $collectionName collection name.
|
||||
* @param string $indexes wildcard for name of the indexes to be dropped.
|
||||
* @return array result data.
|
||||
*/
|
||||
public function dropIndexes($collectionName, $indexes)
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->dropIndexes($collectionName, $indexes);
|
||||
|
||||
return current($this->execute()->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about current collection indexes.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array list of indexes info.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function listIndexes($collectionName, $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->listIndexes($collectionName, $options);
|
||||
|
||||
try {
|
||||
$cursor = $this->execute();
|
||||
} catch (Exception $e) {
|
||||
// The server may return an error if the collection does not exist.
|
||||
$notFoundCodes = [
|
||||
26, // namespace not found
|
||||
60 // database not found
|
||||
];
|
||||
if (in_array($e->getCode(), $notFoundCodes, true)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $cursor->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts records in specified collection.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $condition filter condition
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int records count
|
||||
*/
|
||||
public function count($collectionName, $condition = [], $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->count($collectionName, $condition, $options);
|
||||
|
||||
$result = current($this->execute()->toArray());
|
||||
return $result['n'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the insert operation to the batch command.
|
||||
* @param array $document document to be inserted
|
||||
* @return $this self reference.
|
||||
* @see executeBatch()
|
||||
*/
|
||||
public function addInsert($document)
|
||||
{
|
||||
$this->document[] = [
|
||||
'type' => 'insert',
|
||||
'document' => $document,
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the update operation to the batch command.
|
||||
* @param array $condition filter condition
|
||||
* @param array $document data to be updated
|
||||
* @param array $options update options.
|
||||
* @return $this self reference.
|
||||
* @see executeBatch()
|
||||
*/
|
||||
public function addUpdate($condition, $document, $options = [])
|
||||
{
|
||||
$options = array_merge(
|
||||
[
|
||||
'multi' => true,
|
||||
'upsert' => false,
|
||||
],
|
||||
$options
|
||||
);
|
||||
|
||||
if ($options['multi']) {
|
||||
$keys = array_keys($document);
|
||||
if (!empty($keys) && strncmp('$', $keys[0], 1) !== 0) {
|
||||
$document = ['$set' => $document];
|
||||
}
|
||||
}
|
||||
|
||||
$this->document[] = [
|
||||
'type' => 'update',
|
||||
'condition' => $this->db->getQueryBuilder()->buildCondition($condition),
|
||||
'document' => $document,
|
||||
'options' => $options,
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the delete operation to the batch command.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options delete options.
|
||||
* @return $this self reference.
|
||||
* @see executeBatch()
|
||||
*/
|
||||
public function addDelete($condition, $options = [])
|
||||
{
|
||||
$this->document[] = [
|
||||
'type' => 'delete',
|
||||
'condition' => $this->db->getQueryBuilder()->buildCondition($condition),
|
||||
'options' => $options,
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new document into collection.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $document document content
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return ObjectID|bool inserted record ID, `false` - on failure.
|
||||
*/
|
||||
public function insert($collectionName, $document, $options = [])
|
||||
{
|
||||
$this->document = [];
|
||||
$this->addInsert($document);
|
||||
$result = $this->executeBatch($collectionName, $options);
|
||||
|
||||
if ($result['result']->getInsertedCount() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return reset($result['insertedIds']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts batch of new documents into collection.
|
||||
* @param string $collectionName collection name
|
||||
* @param array[] $documents documents list
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array|false list of inserted IDs, `false` on failure.
|
||||
*/
|
||||
public function batchInsert($collectionName, $documents, $options = [])
|
||||
{
|
||||
$this->document = [];
|
||||
foreach ($documents as $key => $document) {
|
||||
$this->document[$key] = [
|
||||
'type' => 'insert',
|
||||
'document' => $document
|
||||
];
|
||||
}
|
||||
|
||||
$result = $this->executeBatch($collectionName, $options);
|
||||
|
||||
if ($result['result']->getInsertedCount() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result['insertedIds'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing documents in the collection.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $condition filter condition
|
||||
* @param array $document data to be updated.
|
||||
* @param array $options update options.
|
||||
* @return WriteResult write result.
|
||||
*/
|
||||
public function update($collectionName, $condition, $document, $options = [])
|
||||
{
|
||||
$batchOptions = [];
|
||||
foreach (['bypassDocumentValidation'] as $name) {
|
||||
if (isset($options[$name])) {
|
||||
$batchOptions[$name] = $options[$name];
|
||||
unset($options[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->document = [];
|
||||
$this->addUpdate($condition, $document, $options);
|
||||
$result = $this->executeBatch($collectionName, $batchOptions);
|
||||
|
||||
return $result['result'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes documents from the collection.
|
||||
* @param string $collectionName collection name.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options delete options.
|
||||
* @return WriteResult write result.
|
||||
*/
|
||||
public function delete($collectionName, $condition, $options = [])
|
||||
{
|
||||
$batchOptions = [];
|
||||
foreach (['bypassDocumentValidation'] as $name) {
|
||||
if (isset($options[$name])) {
|
||||
$batchOptions[$name] = $options[$name];
|
||||
unset($options[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->document = [];
|
||||
$this->addDelete($condition, $options);
|
||||
$result = $this->executeBatch($collectionName, $batchOptions);
|
||||
|
||||
return $result['result'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs find query.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $condition filter condition
|
||||
* @param array $options query options.
|
||||
* @return \MongoDB\Driver\Cursor result cursor.
|
||||
*/
|
||||
public function find($collectionName, $condition, $options = [])
|
||||
{
|
||||
$queryBuilder = $this->db->getQueryBuilder();
|
||||
|
||||
$this->document = $queryBuilder->buildCondition($condition);
|
||||
|
||||
if (isset($options['projection'])) {
|
||||
$options['projection'] = $queryBuilder->buildSelectFields($options['projection']);
|
||||
}
|
||||
|
||||
if (isset($options['sort'])) {
|
||||
$options['sort'] = $queryBuilder->buildSortFields($options['sort']);
|
||||
}
|
||||
|
||||
if (array_key_exists('limit', $options)) {
|
||||
if ($options['limit'] === null || !ctype_digit((string) $options['limit'])) {
|
||||
unset($options['limit']);
|
||||
} else {
|
||||
$options['limit'] = (int)$options['limit'];
|
||||
}
|
||||
}
|
||||
if (array_key_exists('skip', $options)) {
|
||||
if ($options['skip'] === null || !ctype_digit((string) $options['skip'])) {
|
||||
unset($options['skip']);
|
||||
} else {
|
||||
$options['skip'] = (int)$options['skip'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->query($collectionName, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a document and returns it.
|
||||
* @param $collectionName
|
||||
* @param array $condition query condition
|
||||
* @param array $update update criteria
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array|null the original document, or the modified document when $options['new'] is set.
|
||||
*/
|
||||
public function findAndModify($collectionName, $condition = [], $update = [], $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->findAndModify($collectionName, $condition, $update, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
if (!isset($result['value'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $result['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of distinct values for the given column across a collection.
|
||||
* @param string $collectionName collection name.
|
||||
* @param string $fieldName field name to use.
|
||||
* @param array $condition query parameters.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array array of distinct values, or "false" on failure.
|
||||
*/
|
||||
public function distinct($collectionName, $fieldName, $condition = [], $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->distinct($collectionName, $fieldName, $condition, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
if (!isset($result['values']) || !is_array($result['values'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result['values'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs aggregation using MongoDB "group" command.
|
||||
* @param string $collectionName collection name.
|
||||
* @param mixed $keys fields to group by. If an array or non-code object is passed,
|
||||
* it will be the key used to group results. If instance of [[\MongoDB\BSON\Javascript]] passed,
|
||||
* it will be treated as a function that returns the key to group by.
|
||||
* @param array $initial Initial value of the aggregation counter object.
|
||||
* @param \MongoDB\BSON\Javascript|string $reduce function that takes two arguments (the current
|
||||
* document and the aggregation to this point) and does the aggregation.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param array $options optional parameters to the group command. Valid options include:
|
||||
* - condition - criteria for including a document in the aggregation.
|
||||
* - finalize - function called once per unique key that takes the final output of the reduce function.
|
||||
* @return array the result of the aggregation.
|
||||
*/
|
||||
public function group($collectionName, $keys, $initial, $reduce, $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->group($collectionName, $keys, $initial, $reduce, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
return $result['retval'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs MongoDB "map-reduce" command.
|
||||
* @param string $collectionName collection name.
|
||||
* @param \MongoDB\BSON\Javascript|string $map function, which emits map data from collection.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param \MongoDB\BSON\Javascript|string $reduce function that takes two arguments (the map key
|
||||
* and the map values) and does the aggregation.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param string|array $out output collection name. It could be a string for simple output
|
||||
* ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
|
||||
* You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
|
||||
* @param array $condition filter condition for including a document in the aggregation.
|
||||
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
|
||||
*
|
||||
* - sort: array, key to sort the input documents. The sort key must be in an existing index for this collection.
|
||||
* - limit: int, the maximum number of documents to return in the collection.
|
||||
* - finalize: \MongoDB\BSON\Javascript|string, function, which follows the reduce method and modifies the output.
|
||||
* - scope: array, specifies global variables that are accessible in the map, reduce and finalize functions.
|
||||
* - jsMode: bool, specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
|
||||
* - verbose: bool, specifies whether to include the timing information in the result information.
|
||||
*
|
||||
* @return string|array the map reduce output collection name or output results.
|
||||
*/
|
||||
public function mapReduce($collectionName, $map, $reduce, $out, $condition = [], $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->mapReduce($collectionName, $map, $reduce, $out, $condition, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
return array_key_exists('results', $result) ? $result['results'] : $result['result'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs aggregation using MongoDB Aggregation Framework.
|
||||
* In case 'cursor' option is specified [[\MongoDB\Driver\Cursor]] instance is returned,
|
||||
* otherwise - an array of aggregation results.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $pipelines list of pipeline operators.
|
||||
* @param array $options optional parameters.
|
||||
* @return array|\MongoDB\Driver\Cursor aggregation result.
|
||||
*/
|
||||
public function aggregate($collectionName, $pipelines, $options = [])
|
||||
{
|
||||
if (empty($options['cursor'])) {
|
||||
$returnCursor = false;
|
||||
$options['cursor'] = new \stdClass();
|
||||
} else {
|
||||
$returnCursor = true;
|
||||
}
|
||||
|
||||
$this->document = $this->db->getQueryBuilder()->aggregate($collectionName, $pipelines, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
if ($returnCursor) {
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
return $cursor->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an explanation of the query, often useful for optimization and debugging.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $query query document.
|
||||
* @return array explanation of the query.
|
||||
*/
|
||||
public function explain($collectionName, $query)
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->explain($collectionName, $query);
|
||||
$cursor = $this->execute();
|
||||
|
||||
return current($cursor->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available databases.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options options list.
|
||||
* @return array database information
|
||||
*/
|
||||
public function listDatabases($condition = [], $options = [])
|
||||
{
|
||||
if ($this->databaseName === null) {
|
||||
$this->databaseName = 'admin';
|
||||
}
|
||||
$this->document = $this->db->getQueryBuilder()->listDatabases($condition, $options);
|
||||
|
||||
$cursor = $this->execute();
|
||||
$result = current($cursor->toArray());
|
||||
|
||||
if (empty($result['databases'])) {
|
||||
return [];
|
||||
}
|
||||
return $result['databases'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available collections.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options options list.
|
||||
* @return array collections information.
|
||||
*/
|
||||
public function listCollections($condition = [], $options = [])
|
||||
{
|
||||
$this->document = $this->db->getQueryBuilder()->listCollections($condition, $options);
|
||||
$cursor = $this->execute();
|
||||
|
||||
return $cursor->toArray();
|
||||
}
|
||||
|
||||
// Logging :
|
||||
|
||||
/**
|
||||
* Logs the command data if logging is enabled at [[db]].
|
||||
* @param array|string $namespace command namespace.
|
||||
* @param array $data command data.
|
||||
* @param string $category log category
|
||||
* @return string|false log token, `false` if log is not enabled.
|
||||
*/
|
||||
protected function log($namespace, $data, $category)
|
||||
{
|
||||
if ($this->db->enableLogging) {
|
||||
$token = $this->db->getLogBuilder()->generateToken($namespace, $data);
|
||||
Yii::info($token, $category);
|
||||
return $token;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the beginning of a code block for profiling.
|
||||
* @param string $token token for the code block
|
||||
* @param string $category the category of this log message
|
||||
* @see endProfile()
|
||||
*/
|
||||
protected function beginProfile($token, $category)
|
||||
{
|
||||
if ($token !== false && $this->db->enableProfiling) {
|
||||
Yii::beginProfile($token, $category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the end of a code block for profiling.
|
||||
* @param string $token token for the code block
|
||||
* @param string $category the category of this log message
|
||||
* @see beginProfile()
|
||||
*/
|
||||
protected function endProfile($token, $category)
|
||||
{
|
||||
if ($token !== false && $this->db->enableProfiling) {
|
||||
Yii::endProfile($token, $category);
|
||||
}
|
||||
}
|
||||
}
|
||||
435
vendor/yiisoft/yii2-mongodb/src/Connection.php
vendored
Normal file
435
vendor/yiisoft/yii2-mongodb/src/Connection.php
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\Driver\Manager;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidConfigException;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* Connection represents a connection to a MongoDb server.
|
||||
*
|
||||
* Connection works together with [[Database]] and [[Collection]] to provide data access
|
||||
* to the Mongo database. They are wrappers of the [[MongoDB PHP extension]](http://us1.php.net/manual/en/book.mongo.php).
|
||||
*
|
||||
* To establish a DB connection, set [[dsn]] and then call [[open()]] to be true.
|
||||
*
|
||||
* The following example shows how to create a Connection instance and establish
|
||||
* the DB connection:
|
||||
*
|
||||
* ```php
|
||||
* $connection = new \yii\mongodb\Connection([
|
||||
* 'dsn' => $dsn,
|
||||
* ]);
|
||||
* $connection->open();
|
||||
* ```
|
||||
*
|
||||
* After the Mongo connection is established, one can access Mongo databases and collections:
|
||||
*
|
||||
* ```php
|
||||
* $database = $connection->getDatabase('my_mongo_db');
|
||||
* $collection = $database->getCollection('customer');
|
||||
* $collection->insert(['name' => 'John Smith', 'status' => 1]);
|
||||
* ```
|
||||
*
|
||||
* You can work with several different databases at the same server using this class.
|
||||
* However, while it is unlikely your application will actually need it, the Connection class
|
||||
* provides ability to use [[defaultDatabaseName]] as well as a shortcut method [[getCollection()]]
|
||||
* to retrieve a particular collection instance:
|
||||
*
|
||||
* ```php
|
||||
* // get collection 'customer' from default database:
|
||||
* $collection = $connection->getCollection('customer');
|
||||
* // get collection 'customer' from database 'mydatabase':
|
||||
* $collection = $connection->getCollection(['mydatabase', 'customer']);
|
||||
* ```
|
||||
*
|
||||
* Connection is often used as an application component and configured in the application
|
||||
* configuration like the following:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'mongodb' => [
|
||||
* 'class' => '\yii\mongodb\Connection',
|
||||
* 'dsn' => 'mongodb://developer:password@localhost:27017/mydatabase',
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @property Database $database Database instance. This property is read-only.
|
||||
* @property string $defaultDatabaseName Default database name.
|
||||
* @property file\Collection $fileCollection Mongo GridFS collection instance. This property is read-only.
|
||||
* @property bool $isActive Whether the Mongo connection is established. This property is read-only.
|
||||
* @property LogBuilder $logBuilder The log builder for this connection. Note that the type of this property
|
||||
* differs in getter and setter. See [[getLogBuilder()]] and [[setLogBuilder()]] for details.
|
||||
* @property QueryBuilder $queryBuilder The query builder for the this MongoDB connection. Note that the type
|
||||
* of this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for
|
||||
* details.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Connection extends Component
|
||||
{
|
||||
/**
|
||||
* @event Event an event that is triggered after a DB connection is established
|
||||
*/
|
||||
const EVENT_AFTER_OPEN = 'afterOpen';
|
||||
|
||||
/**
|
||||
* @var string host:port
|
||||
*
|
||||
* Correct syntax is:
|
||||
* mongodb://[username:password@]host1[:port1][,host2[:port2:],...][/dbname]
|
||||
* For example:
|
||||
* mongodb://localhost:27017
|
||||
* mongodb://developer:password@localhost:27017
|
||||
* mongodb://developer:password@localhost:27017/mydatabase
|
||||
*/
|
||||
public $dsn;
|
||||
/**
|
||||
* @var array connection options.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'socketTimeoutMS' => 1000, // how long a send or receive on a socket can take before timing out
|
||||
* 'ssl' => true // initiate the connection with TLS/SSL
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @see https://docs.mongodb.com/manual/reference/connection-string/#connections-connection-options
|
||||
*/
|
||||
public $options = [];
|
||||
/**
|
||||
* @var array options for the MongoDB driver.
|
||||
* Any driver-specific options not included in MongoDB connection string specification.
|
||||
*
|
||||
* @see http://php.net/manual/en/mongodb-driver-manager.construct.php
|
||||
*/
|
||||
public $driverOptions = [];
|
||||
/**
|
||||
* @var Manager MongoDB driver manager.
|
||||
* @since 2.1
|
||||
*/
|
||||
public $manager;
|
||||
/**
|
||||
* @var array type map to use for BSON unserialization.
|
||||
* Note: default type map will be automatically merged into this field, possibly overriding user-defined values.
|
||||
* @see http://php.net/manual/en/mongodb-driver-cursor.settypemap.php
|
||||
* @since 2.1
|
||||
*/
|
||||
public $typeMap = [];
|
||||
/**
|
||||
* @var bool whether to log command and query executions.
|
||||
* When enabled this option may reduce performance. MongoDB commands may contain large data,
|
||||
* consuming both CPU and memory.
|
||||
* It makes sense to disable this option in the production environment.
|
||||
* @since 2.1
|
||||
*/
|
||||
public $enableLogging = true;
|
||||
/**
|
||||
* @var bool whether to enable profiling the commands and queries being executed.
|
||||
* This option will have no effect in case [[enableLogging]] is disabled.
|
||||
* @since 2.1
|
||||
*/
|
||||
public $enableProfiling = true;
|
||||
/**
|
||||
* @var string name of the protocol, which should be used for the GridFS stream wrapper.
|
||||
* Only alphanumeric values are allowed: do not use any URL special characters, such as '/', '&', ':' etc.
|
||||
* @see \yii\mongodb\file\StreamWrapper
|
||||
* @since 2.1
|
||||
*/
|
||||
public $fileStreamProtocol = 'gridfs';
|
||||
/**
|
||||
* @var string name of the class, which should serve as a stream wrapper for [[fileStreamProtocol]] protocol.
|
||||
* @since 2.1
|
||||
*/
|
||||
public $fileStreamWrapperClass = 'yii\mongodb\file\StreamWrapper';
|
||||
|
||||
/**
|
||||
* @var string name of the MongoDB database to use by default.
|
||||
* If this field left blank, connection instance will attempt to determine it from
|
||||
* [[dsn]] automatically, if needed.
|
||||
*/
|
||||
private $_defaultDatabaseName;
|
||||
/**
|
||||
* @var Database[] list of Mongo databases
|
||||
*/
|
||||
private $_databases = [];
|
||||
/**
|
||||
* @var QueryBuilder|array|string the query builder for this connection
|
||||
* @since 2.1
|
||||
*/
|
||||
private $_queryBuilder = 'yii\mongodb\QueryBuilder';
|
||||
/**
|
||||
* @var LogBuilder|array|string log entries builder used for this connecton.
|
||||
* @since 2.1
|
||||
*/
|
||||
private $_logBuilder = 'yii\mongodb\LogBuilder';
|
||||
/**
|
||||
* @var bool whether GridFS stream wrapper has been already registered.
|
||||
* @since 2.1
|
||||
*/
|
||||
private $_fileStreamWrapperRegistered = false;
|
||||
|
||||
|
||||
/**
|
||||
* Sets default database name.
|
||||
* @param string $name default database name.
|
||||
*/
|
||||
public function setDefaultDatabaseName($name)
|
||||
{
|
||||
$this->_defaultDatabaseName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default database name, if it is not set,
|
||||
* attempts to determine it from [[dsn]] value.
|
||||
* @return string default database name
|
||||
* @throws \yii\base\InvalidConfigException if unable to determine default database name.
|
||||
*/
|
||||
public function getDefaultDatabaseName()
|
||||
{
|
||||
if ($this->_defaultDatabaseName === null) {
|
||||
if (preg_match('/^mongodb:\\/\\/.+\\/([^?&]+)/s', $this->dsn, $matches)) {
|
||||
$this->_defaultDatabaseName = $matches[1];
|
||||
} else {
|
||||
throw new InvalidConfigException("Unable to determine default database name from dsn.");
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_defaultDatabaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query builder for the this MongoDB connection.
|
||||
* @return QueryBuilder the query builder for the this MongoDB connection.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function getQueryBuilder()
|
||||
{
|
||||
if (!is_object($this->_queryBuilder)) {
|
||||
$this->_queryBuilder = Yii::createObject($this->_queryBuilder, [$this]);
|
||||
}
|
||||
return $this->_queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query builder for the this MongoDB connection.
|
||||
* @param QueryBuilder|array|string|null $queryBuilder the query builder for this MongoDB connection.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function setQueryBuilder($queryBuilder)
|
||||
{
|
||||
$this->_queryBuilder = $queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns log builder for this connection.
|
||||
* @return LogBuilder the log builder for this connection.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function getLogBuilder()
|
||||
{
|
||||
if (!is_object($this->_logBuilder)) {
|
||||
$this->_logBuilder = Yii::createObject($this->_logBuilder);
|
||||
}
|
||||
return $this->_logBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets log builder used for this connection.
|
||||
* @param array|string|LogBuilder $logBuilder the log builder for this connection.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function setLogBuilder($logBuilder)
|
||||
{
|
||||
$this->_logBuilder = $logBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MongoDB database with the given name.
|
||||
* @param string|null $name database name, if null default one will be used.
|
||||
* @param bool $refresh whether to reestablish the database connection even, if it is found in the cache.
|
||||
* @return Database database instance.
|
||||
*/
|
||||
public function getDatabase($name = null, $refresh = false)
|
||||
{
|
||||
if ($name === null) {
|
||||
$name = $this->getDefaultDatabaseName();
|
||||
}
|
||||
if ($refresh || !array_key_exists($name, $this->_databases)) {
|
||||
$this->_databases[$name] = $this->selectDatabase($name);
|
||||
}
|
||||
|
||||
return $this->_databases[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the database with given name.
|
||||
* @param string $name database name.
|
||||
* @return Database database instance.
|
||||
*/
|
||||
protected function selectDatabase($name)
|
||||
{
|
||||
return Yii::createObject([
|
||||
'class' => 'yii\mongodb\Database',
|
||||
'name' => $name,
|
||||
'connection' => $this,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MongoDB collection with the given name.
|
||||
* @param string|array $name collection name. If string considered as the name of the collection
|
||||
* inside the default database. If array - first element considered as the name of the database,
|
||||
* second - as name of collection inside that database
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return Collection Mongo collection instance.
|
||||
*/
|
||||
public function getCollection($name, $refresh = false)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
list ($dbName, $collectionName) = $name;
|
||||
return $this->getDatabase($dbName)->getCollection($collectionName, $refresh);
|
||||
}
|
||||
return $this->getDatabase()->getCollection($name, $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MongoDB GridFS collection.
|
||||
* @param string|array $prefix collection prefix. If string considered as the prefix of the GridFS
|
||||
* collection inside the default database. If array - first element considered as the name of the database,
|
||||
* second - as prefix of the GridFS collection inside that database, if no second element present
|
||||
* default "fs" prefix will be used.
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return file\Collection Mongo GridFS collection instance.
|
||||
*/
|
||||
public function getFileCollection($prefix = 'fs', $refresh = false)
|
||||
{
|
||||
if (is_array($prefix)) {
|
||||
list ($dbName, $collectionPrefix) = $prefix;
|
||||
if (!isset($collectionPrefix)) {
|
||||
$collectionPrefix = 'fs';
|
||||
}
|
||||
|
||||
return $this->getDatabase($dbName)->getFileCollection($collectionPrefix, $refresh);
|
||||
}
|
||||
return $this->getDatabase()->getFileCollection($prefix, $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the Mongo connection is established.
|
||||
* @return bool whether the Mongo connection is established
|
||||
*/
|
||||
public function getIsActive()
|
||||
{
|
||||
return is_object($this->manager) && $this->manager->getServers() !== [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a Mongo connection.
|
||||
* It does nothing if a MongoDB connection has already been established.
|
||||
* @throws Exception if connection fails
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
if ($this->manager === null) {
|
||||
if (empty($this->dsn)) {
|
||||
throw new InvalidConfigException($this->className() . '::dsn cannot be empty.');
|
||||
}
|
||||
$token = 'Opening MongoDB connection: ' . $this->dsn;
|
||||
try {
|
||||
Yii::trace($token, __METHOD__);
|
||||
Yii::beginProfile($token, __METHOD__);
|
||||
$options = $this->options;
|
||||
|
||||
$this->manager = new Manager($this->dsn, $options, $this->driverOptions);
|
||||
$this->manager->selectServer($this->manager->getReadPreference());
|
||||
|
||||
$this->initConnection();
|
||||
Yii::endProfile($token, __METHOD__);
|
||||
} catch (\Exception $e) {
|
||||
Yii::endProfile($token, __METHOD__);
|
||||
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$this->typeMap = array_merge(
|
||||
$this->typeMap,
|
||||
[
|
||||
'root' => 'array',
|
||||
'document' => 'array'
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the currently active DB connection.
|
||||
* It does nothing if the connection is already closed.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->manager !== null) {
|
||||
Yii::trace('Closing MongoDB connection: ' . $this->dsn, __METHOD__);
|
||||
$this->manager = null;
|
||||
foreach ($this->_databases as $database) {
|
||||
$database->clearCollections();
|
||||
}
|
||||
$this->_databases = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DB connection.
|
||||
* This method is invoked right after the DB connection is established.
|
||||
* The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
|
||||
*/
|
||||
protected function initConnection()
|
||||
{
|
||||
$this->trigger(self::EVENT_AFTER_OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MongoDB command.
|
||||
* @param array $document command document contents.
|
||||
* @param string|null $databaseName database name, if not set [[defaultDatabaseName]] will be used.
|
||||
* @return Command command instance.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createCommand($document = [], $databaseName = null)
|
||||
{
|
||||
return new Command([
|
||||
'db' => $this,
|
||||
'databaseName' => $databaseName,
|
||||
'document' => $document,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers GridFS stream wrapper for the [[fileStreamProtocol]] protocol.
|
||||
* @param bool $force whether to enforce registration even wrapper has been already registered.
|
||||
* @return string registered stream protocol name.
|
||||
*/
|
||||
public function registerFileStreamWrapper($force = false)
|
||||
{
|
||||
if ($force || !$this->_fileStreamWrapperRegistered) {
|
||||
/* @var $class \yii\mongodb\file\StreamWrapper */
|
||||
$class = $this->fileStreamWrapperClass;
|
||||
$class::register($this->fileStreamProtocol, $force);
|
||||
|
||||
$this->_fileStreamWrapperRegistered = true;
|
||||
}
|
||||
|
||||
return $this->fileStreamProtocol;
|
||||
}
|
||||
}
|
||||
158
vendor/yiisoft/yii2-mongodb/src/Database.php
vendored
Normal file
158
vendor/yiisoft/yii2-mongodb/src/Database.php
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use yii\base\BaseObject;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* Database represents the MongoDB database information.
|
||||
*
|
||||
* @property file\Collection $fileCollection Mongo GridFS collection. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Database extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Connection MongoDB connection.
|
||||
*/
|
||||
public $connection;
|
||||
/**
|
||||
* @var string name of this database.
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var Collection[] list of collections.
|
||||
*/
|
||||
private $_collections = [];
|
||||
/**
|
||||
* @var file\Collection[] list of GridFS collections.
|
||||
*/
|
||||
private $_fileCollections = [];
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Mongo collection with the given name.
|
||||
* @param string $name collection name
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return Collection Mongo collection instance.
|
||||
*/
|
||||
public function getCollection($name, $refresh = false)
|
||||
{
|
||||
if ($refresh || !array_key_exists($name, $this->_collections)) {
|
||||
$this->_collections[$name] = $this->selectCollection($name);
|
||||
}
|
||||
|
||||
return $this->_collections[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Mongo GridFS collection with given prefix.
|
||||
* @param string $prefix collection prefix.
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return file\Collection Mongo GridFS collection.
|
||||
*/
|
||||
public function getFileCollection($prefix = 'fs', $refresh = false)
|
||||
{
|
||||
if ($refresh || !array_key_exists($prefix, $this->_fileCollections)) {
|
||||
$this->_fileCollections[$prefix] = $this->selectFileCollection($prefix);
|
||||
}
|
||||
|
||||
return $this->_fileCollections[$prefix];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects collection with given name.
|
||||
* @param string $name collection name.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
protected function selectCollection($name)
|
||||
{
|
||||
return Yii::createObject([
|
||||
'class' => 'yii\mongodb\Collection',
|
||||
'database' => $this,
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects GridFS collection with given prefix.
|
||||
* @param string $prefix file collection prefix.
|
||||
* @return file\Collection file collection instance.
|
||||
*/
|
||||
protected function selectFileCollection($prefix)
|
||||
{
|
||||
return Yii::createObject([
|
||||
'class' => 'yii\mongodb\file\Collection',
|
||||
'database' => $this,
|
||||
'prefix' => $prefix,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates MongoDB command associated with this database.
|
||||
* @param array $document command document contents.
|
||||
* @return Command command instance.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createCommand($document = [])
|
||||
{
|
||||
return $this->connection->createCommand($document, $this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection.
|
||||
* Note: Mongo creates new collections automatically on the first demand,
|
||||
* this method makes sense only for the migration script or for the case
|
||||
* you need to create collection with the specific options.
|
||||
* @param string $name name of the collection
|
||||
* @param array $options collection options in format: "name" => "value"
|
||||
* @return bool whether operation was successful.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function createCollection($name, $options = [])
|
||||
{
|
||||
return $this->createCommand()->createCollection($name, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops specified collection.
|
||||
* @param string $name name of the collection
|
||||
* @return bool whether operation was successful.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function dropCollection($name)
|
||||
{
|
||||
return $this->createCommand()->dropCollection($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available collections in this database.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options options list.
|
||||
* @return array collections information.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
public function listCollections($condition = [], $options = [])
|
||||
{
|
||||
return $this->createCommand()->listCollections($condition, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears internal collection lists.
|
||||
* This method can be used to break cycle references between [[Database]] and [[Collection]] instances.
|
||||
*/
|
||||
public function clearCollections()
|
||||
{
|
||||
$this->_collections = [];
|
||||
$this->_fileCollections = [];
|
||||
}
|
||||
}
|
||||
25
vendor/yiisoft/yii2-mongodb/src/Exception.php
vendored
Normal file
25
vendor/yiisoft/yii2-mongodb/src/Exception.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
/**
|
||||
* Exception represents an exception that is caused by some Mongo-related operations.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Exception extends \yii\base\Exception
|
||||
{
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'MongoDB Exception';
|
||||
}
|
||||
}
|
||||
131
vendor/yiisoft/yii2-mongodb/src/LogBuilder.php
vendored
Normal file
131
vendor/yiisoft/yii2-mongodb/src/LogBuilder.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\Javascript;
|
||||
use MongoDB\BSON\MaxKey;
|
||||
use MongoDB\BSON\MinKey;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\BSON\Regex;
|
||||
use MongoDB\BSON\Timestamp;
|
||||
use MongoDB\BSON\Type;
|
||||
use MongoDB\BSON\UTCDatetime;
|
||||
use yii\base\BaseObject;
|
||||
|
||||
/**
|
||||
* LogBuilder allows composition and escaping of the log entries.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class LogBuilder extends BaseObject
|
||||
{
|
||||
/**
|
||||
* Generate log/profile token.
|
||||
* @param string|array $namespace command namespace
|
||||
* @param array $data command data.
|
||||
* @return string token.
|
||||
*/
|
||||
public function generateToken($namespace, $data = [])
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
$namespace = implode('.', $namespace);
|
||||
}
|
||||
return $namespace . '(' . $this->encodeData($data) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes complex log data into JSON format string.
|
||||
* @param mixed $data raw data.
|
||||
* @return string encoded data string.
|
||||
*/
|
||||
public function encodeData($data)
|
||||
{
|
||||
return json_encode($this->processData($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-processes the log data before sending it to `json_encode()`.
|
||||
* @param mixed $data raw data.
|
||||
* @return mixed the processed data.
|
||||
*/
|
||||
protected function processData($data)
|
||||
{
|
||||
if (is_object($data)) {
|
||||
if ($data instanceof ObjectID ||
|
||||
$data instanceof Regex ||
|
||||
$data instanceof UTCDateTime ||
|
||||
$data instanceof Timestamp
|
||||
) {
|
||||
$data = get_class($data) . '(' . $data->__toString() . ')';
|
||||
} elseif ($data instanceof Javascript) {
|
||||
$data = $this->processJavascript($data);
|
||||
} elseif ($data instanceof MinKey || $data instanceof MaxKey) {
|
||||
$data = get_class($data);
|
||||
} elseif ($data instanceof Binary) {
|
||||
if (in_array($data->getType(), [Binary::TYPE_MD5, Binary::TYPE_UUID, Binary::TYPE_OLD_UUID], true)) {
|
||||
$data = $data->getData();
|
||||
} else {
|
||||
$data = get_class($data) . '(...)';
|
||||
}
|
||||
} elseif ($data instanceof Type) {
|
||||
// Covers 'Binary', 'DBRef' and others
|
||||
$data = get_class($data) . '(...)';
|
||||
} else {
|
||||
$result = [];
|
||||
foreach ($data as $name => $value) {
|
||||
$result[$name] = $value;
|
||||
}
|
||||
$data = $result;
|
||||
}
|
||||
|
||||
if ($data === []) {
|
||||
return new \stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$data[$key] = $this->processData($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes [[Javascript]] composing recoverable value.
|
||||
* @param Javascript $javascript javascript BSON object.
|
||||
* @return string processed javascript.
|
||||
*/
|
||||
private function processJavascript(Javascript $javascript)
|
||||
{
|
||||
$dump = print_r($javascript, true);
|
||||
$beginPos = strpos($dump, '[javascript] => ');
|
||||
if ($beginPos === false) {
|
||||
$beginPos = strpos($dump, '[code] => ');
|
||||
if ($beginPos === false) {
|
||||
return $dump;
|
||||
}
|
||||
$beginPos += strlen('[code] => ');
|
||||
} else {
|
||||
$beginPos += strlen('[javascript] => ');
|
||||
}
|
||||
|
||||
$endPos = strrpos($dump, '[scope] => ');
|
||||
if ($endPos === false || $beginPos > $endPos) {
|
||||
return $dump;
|
||||
}
|
||||
$content = substr($dump, $beginPos, $endPos - $beginPos);
|
||||
|
||||
return get_class($javascript) . '(' . trim($content, " \n\t") . ')';
|
||||
}
|
||||
}
|
||||
293
vendor/yiisoft/yii2-mongodb/src/Migration.php
vendored
Normal file
293
vendor/yiisoft/yii2-mongodb/src/Migration.php
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\db\MigrationInterface;
|
||||
use yii\di\Instance;
|
||||
use yii\helpers\Json;
|
||||
|
||||
/**
|
||||
* Migration is the base class for representing a MongoDB migration.
|
||||
*
|
||||
* Each child class of Migration represents an individual MongoDB migration which
|
||||
* is identified by the child class name.
|
||||
*
|
||||
* Within each migration, the [[up()]] method should be overridden to contain the logic
|
||||
* for "upgrading" the database; while the [[down()]] method for the "downgrading"
|
||||
* logic.
|
||||
*
|
||||
* Migration provides a set of convenient methods for manipulating MongoDB data and schema.
|
||||
* For example, the [[createIndex()]] method can be used to create a collection index.
|
||||
* Compared with the same methods in [[Collection]], these methods will display extra
|
||||
* information showing the method parameters and execution time, which may be useful when
|
||||
* applying migrations.
|
||||
*
|
||||
* @author Klimov Paul <klimov@zfort.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class Migration extends Component implements MigrationInterface
|
||||
{
|
||||
/**
|
||||
* @var Connection|array|string the MongoDB connection object or the application component ID of the MongoDB connection
|
||||
* that this migration should work with.
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var bool indicates whether the log output should be compacted.
|
||||
* If this is set to true, the individual commands ran within the migration will not be output to the console log.
|
||||
* Default is `false`, in other words the output is fully verbose by default.
|
||||
* @since 2.1.5
|
||||
*/
|
||||
public $compact = false;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the migration.
|
||||
* This method will set [[db]] to be the 'db' application component, if it is null.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with the specified options.
|
||||
* @param string|array $collection name of the collection
|
||||
* @param array $options collection options in format: "name" => "value"
|
||||
*/
|
||||
public function createCollection($collection, $options = [])
|
||||
{
|
||||
if (is_array($collection)) {
|
||||
list($database, $collectionName) = $collection;
|
||||
} else {
|
||||
$database = null;
|
||||
$collectionName = $collection;
|
||||
}
|
||||
$this->beginProfile($token = " > create collection " . $this->composeCollectionLogName($collection) . " ...");
|
||||
$this->db->getDatabase($database)->createCollection($collectionName, $options);
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops existing collection.
|
||||
* @param string|array $collection name of the collection
|
||||
*/
|
||||
public function dropCollection($collection)
|
||||
{
|
||||
$this->beginProfile($token = " > drop collection " . $this->composeCollectionLogName($collection) . " ...");
|
||||
$this->db->getCollection($collection)->drop();
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexes in the collection.
|
||||
* @param string|array $collection name of the collection
|
||||
* @param array $indexes indexes specifications.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createIndexes($collection, $indexes)
|
||||
{
|
||||
$this->beginProfile($token = " > create indexes on " . $this->composeCollectionLogName($collection) . " (" . Json::encode($indexes) . ") ...");
|
||||
$this->db->getCollection($collection)->createIndexes($indexes);
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops collection indexes by name.
|
||||
* @param string|array $collection name of the collection
|
||||
* @param string $indexes wildcard for name of the indexes to be dropped.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function dropIndexes($collection, $indexes)
|
||||
{
|
||||
$this->beginProfile($token = " > drop indexes '{$indexes}' on " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$this->db->getCollection($collection)->dropIndexes($indexes);
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the collection and the specified fields.
|
||||
* @param string|array $collection name of the collection
|
||||
* @param array|string $columns column name or list of column names.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
*/
|
||||
public function createIndex($collection, $columns, $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > create index on " . $this->composeCollectionLogName($collection) . " (" . Json::encode((array) $columns) . empty($options) ? "" : ", " . Json::encode($options) . ") ...");
|
||||
$this->db->getCollection($collection)->createIndex($columns, $options);
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop indexes for specified column(s).
|
||||
* @param string|array $collection name of the collection
|
||||
* @param string|array $columns column name or list of column names.
|
||||
*/
|
||||
public function dropIndex($collection, $columns)
|
||||
{
|
||||
$this->beginProfile($token = " > drop index on " . $this->composeCollectionLogName($collection) . " (" . Json::encode((array) $columns) . ") ...");
|
||||
$this->db->getCollection($collection)->dropIndex($columns);
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all indexes for specified collection.
|
||||
* @param string|array $collection name of the collection.
|
||||
*/
|
||||
public function dropAllIndexes($collection)
|
||||
{
|
||||
$this->beginProfile($token = " > drop all indexes on " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$this->db->getCollection($collection)->dropAllIndexes();
|
||||
$this->endProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new data into collection.
|
||||
* @param array|string $collection collection name.
|
||||
* @param array|object $data data to be inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return \MongoDB\BSON\ObjectID new record id instance.
|
||||
*/
|
||||
public function insert($collection, $data, $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > insert into " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$id = $this->db->getCollection($collection)->insert($data, $options);
|
||||
$this->endProfile($token);
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts several new rows into collection.
|
||||
* @param array|string $collection collection name.
|
||||
* @param array $rows array of arrays or objects to be inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array inserted data, each row will have "_id" key assigned to it.
|
||||
*/
|
||||
public function batchInsert($collection, $rows, $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > insert into " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$rows = $this->db->getCollection($collection)->batchInsert($rows, $options);
|
||||
$this->endProfile($token);
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the rows, which matches given criteria by given data.
|
||||
* Note: for "multiple" mode Mongo requires explicit strategy "$set" or "$inc"
|
||||
* to be specified for the "newData". If no strategy is passed "$set" will be used.
|
||||
* @param array|string $collection collection name.
|
||||
* @param array $condition description of the objects to update.
|
||||
* @param array $newData the object with which to update the matching records.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int|bool number of updated documents or whether operation was successful.
|
||||
*/
|
||||
public function update($collection, $condition, $newData, $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > update " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$result = $this->db->getCollection($collection)->update($condition, $newData, $options);
|
||||
$this->endProfile($token);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the existing database data, otherwise insert this data
|
||||
* @param array|string $collection collection name.
|
||||
* @param array|object $data data to be updated/inserted.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return \MongoDB\BSON\ObjectID updated/new record id instance.
|
||||
*/
|
||||
public function save($collection, $data, $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > save " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$id = $this->db->getCollection($collection)->save($data, $options);
|
||||
$this->endProfile($token);
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes data from the collection.
|
||||
* @param array|string $collection collection name.
|
||||
* @param array $condition description of records to remove.
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return int|bool number of updated documents or whether operation was successful.
|
||||
*/
|
||||
public function remove($collection, $condition = [], $options = [])
|
||||
{
|
||||
$this->beginProfile($token = " > remove " . $this->composeCollectionLogName($collection) . ") ...");
|
||||
$result = $this->db->getCollection($collection)->remove($condition, $options);
|
||||
$this->endProfile($token);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes string representing collection name.
|
||||
* @param array|string $collection collection name.
|
||||
* @return string collection name.
|
||||
*/
|
||||
protected function composeCollectionLogName($collection)
|
||||
{
|
||||
if (is_array($collection)) {
|
||||
list($database, $collection) = $collection;
|
||||
return $database . '.' . $collection;
|
||||
}
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array opened profile tokens.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
private $profileTokens = [];
|
||||
|
||||
/**
|
||||
* Logs the incoming message.
|
||||
* By default this method sends message to 'stdout'.
|
||||
* @param string $string message to be logged.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
protected function log($string)
|
||||
{
|
||||
echo $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the beginning of a code block for profiling.
|
||||
* @param string $token token for the code block.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
protected function beginProfile($token)
|
||||
{
|
||||
$this->profileTokens[$token] = microtime(true);
|
||||
|
||||
if (!$this->compact) {
|
||||
$this->log($token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the end of a code block for profiling.
|
||||
* @param string $token token for the code block.
|
||||
* @since 2.1.1
|
||||
*/
|
||||
protected function endProfile($token)
|
||||
{
|
||||
if (isset($this->profileTokens[$token])) {
|
||||
$time = microtime(true) - $this->profileTokens[$token];
|
||||
unset($this->profileTokens[$token]);
|
||||
} else {
|
||||
$time = 0;
|
||||
}
|
||||
|
||||
if (!$this->compact) {
|
||||
$this->log(" done (time: " . sprintf('%.3f', $time) . "s)\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
635
vendor/yiisoft/yii2-mongodb/src/Query.php
vendored
Normal file
635
vendor/yiisoft/yii2-mongodb/src/Query.php
vendored
Normal file
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\db\QueryInterface;
|
||||
use yii\db\QueryTrait;
|
||||
use Yii;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Query represents Mongo "find" operation.
|
||||
*
|
||||
* Query provides a set of methods to facilitate the specification of "find" command.
|
||||
* These methods can be chained together.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* ```php
|
||||
* $query = new Query();
|
||||
* // compose the query
|
||||
* $query->select(['name', 'status'])
|
||||
* ->from('customer')
|
||||
* ->limit(10);
|
||||
* // execute the query
|
||||
* $rows = $query->all();
|
||||
* ```
|
||||
*
|
||||
* @property Collection $collection Collection instance. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Query extends Component implements QueryInterface
|
||||
{
|
||||
use QueryTrait;
|
||||
|
||||
/**
|
||||
* @var array the fields of the results to return. For example: `['name', 'group_id']`, `['name' => true, '_id' => false]`.
|
||||
* Unless directly excluded, the "_id" field is always returned. If not set, it means selecting all columns.
|
||||
* @see select()
|
||||
*/
|
||||
public $select = [];
|
||||
/**
|
||||
* @var string|array the collection to be selected from. If string considered as the name of the collection
|
||||
* inside the default database. If array - first element considered as the name of the database,
|
||||
* second - as name of collection inside that database
|
||||
* @see from()
|
||||
*/
|
||||
public $from;
|
||||
/**
|
||||
* @var array cursor options in format: optionKey => optionValue
|
||||
* @see \MongoDB\Driver\Cursor::addOption()
|
||||
* @see options()
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Mongo collection for this query.
|
||||
* @param Connection $db Mongo connection.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public function getCollection($db = null)
|
||||
{
|
||||
if ($db === null) {
|
||||
$db = Yii::$app->get('mongodb');
|
||||
}
|
||||
|
||||
return $db->getCollection($this->from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of fields of the results to return.
|
||||
* @param array $fields fields of the results to return.
|
||||
* @return $this the query object itself.
|
||||
*/
|
||||
public function select(array $fields)
|
||||
{
|
||||
$this->select = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collection to be selected from.
|
||||
* @param string|array the collection to be selected from. If string considered as the name of the collection
|
||||
* inside the default database. If array - first element considered as the name of the database,
|
||||
* second - as name of collection inside that database
|
||||
* @return $this the query object itself.
|
||||
*/
|
||||
public function from($collection)
|
||||
{
|
||||
$this->from = $collection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor options.
|
||||
* @param array $options cursor options in format: optionName => optionValue
|
||||
* @return $this the query object itself
|
||||
* @see addOptions()
|
||||
*/
|
||||
public function options($options)
|
||||
{
|
||||
$this->options = $options;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional cursor options.
|
||||
* @param array $options cursor options in format: optionName => optionValue
|
||||
* @return $this the query object itself
|
||||
* @see options()
|
||||
*/
|
||||
public function addOptions($options)
|
||||
{
|
||||
if (is_array($this->options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
} else {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for easy querying on values containing some common operators.
|
||||
*
|
||||
* The comparison operator is intelligently determined based on the first few characters in the given value and
|
||||
* internally translated to a MongoDB operator.
|
||||
* In particular, it recognizes the following operators if they appear as the leading characters in the given value:
|
||||
* <: the column must be less than the given value ($lt).
|
||||
* >: the column must be greater than the given value ($gt).
|
||||
* <=: the column must be less than or equal to the given value ($lte).
|
||||
* >=: the column must be greater than or equal to the given value ($gte).
|
||||
* <>: the column must not be the same as the given value ($ne). Note that when $partialMatch is true, this would mean the value must not be a substring of the column.
|
||||
* =: the column must be equal to the given value ($eq).
|
||||
* none of the above: use the $defaultOperator
|
||||
*
|
||||
* Note that when the value is empty, no comparison expression will be added to the search condition.
|
||||
*
|
||||
* @param string $name column name
|
||||
* @param string $value column value
|
||||
* @param string $defaultOperator Defaults to =, performing an exact match.
|
||||
* For example: use 'LIKE' or 'REGEX' for partial cq regex matching
|
||||
* @see Collection::buildCondition()
|
||||
* @return $this the query object itself.
|
||||
* @since 2.0.5
|
||||
*/
|
||||
public function andFilterCompare($name, $value, $defaultOperator = '=')
|
||||
{
|
||||
$matches = [];
|
||||
if (preg_match('/^(<>|>=|>|<=|<|=)/', $value, $matches)) {
|
||||
$op = $matches[1];
|
||||
$value = substr($value, strlen($op));
|
||||
} else {
|
||||
$op = $defaultOperator;
|
||||
}
|
||||
|
||||
return $this->andFilterWhere([$op, $name, $value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares for query building.
|
||||
* This method is called before actual query composition, e.g. building cursor, count etc.
|
||||
* You may override this method to do some final preparation work before query execution.
|
||||
* @return $this a prepared query instance.
|
||||
* @since 2.1.3
|
||||
*/
|
||||
public function prepare()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the MongoDB cursor for this query.
|
||||
* @param Connection $db the MongoDB connection used to execute the query.
|
||||
* @return \MongoDB\Driver\Cursor mongo cursor instance.
|
||||
*/
|
||||
public function buildCursor($db = null)
|
||||
{
|
||||
$this->prepare();
|
||||
|
||||
$options = $this->options;
|
||||
if (!empty($this->orderBy)) {
|
||||
$options['sort'] = $this->orderBy;
|
||||
}
|
||||
$options['limit'] = $this->limit;
|
||||
$options['skip'] = $this->offset;
|
||||
|
||||
$cursor = $this->getCollection($db)->find($this->composeCondition(), $this->select, $options);
|
||||
|
||||
return $cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches rows from the given Mongo cursor.
|
||||
* @param \MongoDB\Driver\Cursor $cursor Mongo cursor instance to fetch data from.
|
||||
* @param bool $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|bool result.
|
||||
*/
|
||||
protected function fetchRows($cursor, $all = true, $indexBy = null)
|
||||
{
|
||||
$token = 'fetch cursor id = ' . $cursor->getId();
|
||||
Yii::info($token, __METHOD__);
|
||||
try {
|
||||
Yii::beginProfile($token, __METHOD__);
|
||||
$result = $this->fetchRowsInternal($cursor, $all);
|
||||
Yii::endProfile($token, __METHOD__);
|
||||
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
Yii::endProfile($token, __METHOD__);
|
||||
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \MongoDB\Driver\Cursor $cursor Mongo cursor instance to fetch data from.
|
||||
* @param bool $all whether to fetch all rows or only first one.
|
||||
* @return array|bool result.
|
||||
* @see Query::fetchRows()
|
||||
*/
|
||||
protected function fetchRowsInternal($cursor, $all)
|
||||
{
|
||||
$result = [];
|
||||
if ($all) {
|
||||
foreach ($cursor as $row) {
|
||||
$result[] = $row;
|
||||
}
|
||||
} else {
|
||||
if ($row = current($cursor->toArray())) {
|
||||
$result = $row;
|
||||
} else {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a batch query.
|
||||
*
|
||||
* A batch query supports fetching data in batches, which can keep the memory usage under a limit.
|
||||
* This method will return a [[BatchQueryResult]] object which implements the `Iterator` interface
|
||||
* and can be traversed to retrieve the data in batches.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* ```php
|
||||
* $query = (new Query)->from('user');
|
||||
* foreach ($query->batch() as $rows) {
|
||||
* // $rows is an array of 10 or fewer rows from user collection
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param int $batchSize the number of records to be fetched in each batch.
|
||||
* @param Connection $db the MongoDB connection. If not set, the "mongodb" application component will be used.
|
||||
* @return BatchQueryResult the batch query result. It implements the `Iterator` interface
|
||||
* and can be traversed to retrieve the data in batches.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function batch($batchSize = 100, $db = null)
|
||||
{
|
||||
return Yii::createObject([
|
||||
'class' => BatchQueryResult::className(),
|
||||
'query' => $this,
|
||||
'batchSize' => $batchSize,
|
||||
'db' => $db,
|
||||
'each' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a batch query and retrieves data row by row.
|
||||
* This method is similar to [[batch()]] except that in each iteration of the result,
|
||||
* only one row of data is returned. For example,
|
||||
*
|
||||
* ```php
|
||||
* $query = (new Query)->from('user');
|
||||
* foreach ($query->each() as $row) {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param int $batchSize the number of records to be fetched in each batch.
|
||||
* @param Connection $db the MongoDB connection. If not set, the "mongodb" application component will be used.
|
||||
* @return BatchQueryResult the batch query result. It implements the `Iterator` interface
|
||||
* and can be traversed to retrieve the data in batches.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function each($batchSize = 100, $db = null)
|
||||
{
|
||||
return Yii::createObject([
|
||||
'class' => BatchQueryResult::className(),
|
||||
'query' => $this,
|
||||
'batchSize' => $batchSize,
|
||||
'db' => $db,
|
||||
'each' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 `mongodb` application component will be used.
|
||||
* @return array the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return [];
|
||||
}
|
||||
$cursor = $this->buildCursor($db);
|
||||
$rows = $this->fetchRows($cursor, true, $this->indexBy);
|
||||
return $this->populate($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the raw query results into the format as specified by this query.
|
||||
* This method is internally used to convert the data fetched from database
|
||||
* into the format as required by this query.
|
||||
* @param array $rows the raw query result from database
|
||||
* @return array the converted query result
|
||||
*/
|
||||
public function populate($rows)
|
||||
{
|
||||
if ($this->indexBy === null) {
|
||||
return $rows;
|
||||
}
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$result[ArrayHelper::getValue($row, $this->indexBy)] = $row;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns a single row of result.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return array|false the first row (in terms of an array) of the query result. `false` is returned if the query
|
||||
* results in nothing.
|
||||
*/
|
||||
public function one($db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return false;
|
||||
}
|
||||
$cursor = $this->buildCursor($db);
|
||||
return $this->fetchRows($cursor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query result as a scalar value.
|
||||
* The value returned will be the first column in the first row of the query results.
|
||||
* Column `_id` will be automatically excluded from select fields, if [[select]] is not empty and
|
||||
* `_id` is not selected explicitly.
|
||||
* @param Connection $db the MongoDB connection used to generate the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return string|null|false the value of the first column in the first row of the query result.
|
||||
* `false` is returned if the query result is empty.
|
||||
* @since 2.1.2
|
||||
*/
|
||||
public function scalar($db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$originSelect = (array)$this->select;
|
||||
if (!isset($originSelect['_id']) && array_search('_id', $originSelect, true) === false) {
|
||||
$this->select['_id'] = false;
|
||||
}
|
||||
|
||||
$cursor = $this->buildCursor($db);
|
||||
$row = $this->fetchRows($cursor, false);
|
||||
|
||||
if (empty($row)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return reset($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns the first column of the result.
|
||||
* Column `_id` will be automatically excluded from select fields, if [[select]] is not empty and
|
||||
* `_id` is not selected explicitly.
|
||||
* @param Connection $db the MongoDB connection used to generate the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return array the first column of the query result. An empty array is returned if the query results in nothing.
|
||||
* @since 2.1.2
|
||||
*/
|
||||
public function column($db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$originSelect = (array)$this->select;
|
||||
if (!isset($originSelect['_id']) && array_search('_id', $originSelect, true) === false) {
|
||||
$this->select['_id'] = false;
|
||||
}
|
||||
if (is_string($this->indexBy) && $originSelect && count($originSelect) === 1) {
|
||||
$this->select[] = $this->indexBy;
|
||||
}
|
||||
|
||||
$cursor = $this->buildCursor($db);
|
||||
$rows = $this->fetchRows($cursor, true);
|
||||
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($rows as $row) {
|
||||
$value = reset($row);
|
||||
|
||||
if ($this->indexBy === null) {
|
||||
$results[] = $value;
|
||||
} else {
|
||||
if ($this->indexBy instanceof \Closure) {
|
||||
$results[call_user_func($this->indexBy, $row)] = $value;
|
||||
} else {
|
||||
$results[$row[$this->indexBy]] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs 'findAndModify' query and returns a single row of result.
|
||||
* @param array $update update criteria
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* @return array|null the original document, or the modified document when $options['new'] is set.
|
||||
*/
|
||||
public function modify($update, $options = [], $db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->prepare();
|
||||
|
||||
$collection = $this->getCollection($db);
|
||||
if (!empty($this->orderBy)) {
|
||||
$options['sort'] = $this->orderBy;
|
||||
}
|
||||
|
||||
$options['fields'] = $this->select;
|
||||
return $collection->findAndModify($this->composeCondition(), $update, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records.
|
||||
* @param string $q kept to match [[QueryInterface]], its value is ignored.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return int number of records
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function count($q = '*', $db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return 0;
|
||||
}
|
||||
$this->prepare();
|
||||
$collection = $this->getCollection($db);
|
||||
return $collection->count($this->where, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the query result contains any row of data.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return bool whether the query result contains any row of data.
|
||||
*/
|
||||
public function exists($db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return false;
|
||||
}
|
||||
$cursor = $this->buildCursor($db);
|
||||
foreach ($cursor as $row) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the specified column values.
|
||||
* @param string $q the column name.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return int the sum of the specified column values
|
||||
*/
|
||||
public function sum($q, $db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return 0;
|
||||
}
|
||||
return $this->aggregate($q, 'sum', $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average of the specified column values.
|
||||
* @param string $q the column name.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the Mongo connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return int the average of the specified column values.
|
||||
*/
|
||||
public function average($q, $db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return 0;
|
||||
}
|
||||
return $this->aggregate($q, 'avg', $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum of the specified column values.
|
||||
* @param string $q the column name.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the MongoDB connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int the minimum of the specified column values.
|
||||
*/
|
||||
public function min($q, $db = null)
|
||||
{
|
||||
return $this->aggregate($q, 'min', $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum of the specified column values.
|
||||
* @param string $q the column name.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the MongoDB connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return int the maximum of the specified column values.
|
||||
*/
|
||||
public function max($q, $db = null)
|
||||
{
|
||||
return $this->aggregate($q, 'max', $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the aggregation for the given column.
|
||||
* @param string $column column name.
|
||||
* @param string $operator aggregation operator.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* @return int aggregation result.
|
||||
*/
|
||||
protected function aggregate($column, $operator, $db)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->prepare();
|
||||
$collection = $this->getCollection($db);
|
||||
$pipelines = [];
|
||||
if ($this->where !== null) {
|
||||
$pipelines[] = ['$match' => $this->where];
|
||||
}
|
||||
$pipelines[] = [
|
||||
'$group' => [
|
||||
'_id' => '1',
|
||||
'total' => [
|
||||
'$' . $operator => '$' . $column
|
||||
],
|
||||
]
|
||||
];
|
||||
$result = $collection->aggregate($pipelines);
|
||||
if (array_key_exists(0, $result)) {
|
||||
return $result[0]['total'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of distinct values for the given column across a collection.
|
||||
* @param string $q column to use.
|
||||
* @param Connection $db the MongoDB connection used to execute the query.
|
||||
* If this parameter is not given, the `mongodb` application component will be used.
|
||||
* @return array array of distinct values
|
||||
*/
|
||||
public function distinct($q, $db = null)
|
||||
{
|
||||
if (!empty($this->emulateExecution)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->prepare();
|
||||
$collection = $this->getCollection($db);
|
||||
if ($this->where !== null) {
|
||||
$condition = $this->where;
|
||||
} else {
|
||||
$condition = [];
|
||||
}
|
||||
$result = $collection->distinct($q, $condition);
|
||||
if ($result === false) {
|
||||
return [];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes condition from raw [[where]] value.
|
||||
* @return array conditions.
|
||||
*/
|
||||
private function composeCondition()
|
||||
{
|
||||
if ($this->where === null) {
|
||||
return [];
|
||||
}
|
||||
return $this->where;
|
||||
}
|
||||
}
|
||||
916
vendor/yiisoft/yii2-mongodb/src/QueryBuilder.php
vendored
Normal file
916
vendor/yiisoft/yii2-mongodb/src/QueryBuilder.php
vendored
Normal file
@@ -0,0 +1,916 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use MongoDB\BSON\Javascript;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\BSON\Regex;
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException;
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\base\BaseObject;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* QueryBuilder builds a MongoDB command statements.
|
||||
* It is used by [[Command]] for particular commands and queries composition.
|
||||
*
|
||||
* MongoDB uses JSON format to specify query conditions with quite specific syntax.
|
||||
* However [[buildCondition()]] method provides the ability of "translating" common condition format used "yii\db\*"
|
||||
* into MongoDB condition.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $condition = [
|
||||
* [
|
||||
* 'OR',
|
||||
* ['AND', ['first_name' => 'John'], ['last_name' => 'Smith']],
|
||||
* ['status' => [1, 2, 3]]
|
||||
* ],
|
||||
* ];
|
||||
* print_r(Yii::$app->mongodb->getQueryBuilder()->buildCondition($condition));
|
||||
* // outputs :
|
||||
* [
|
||||
* '$or' => [
|
||||
* [
|
||||
* 'first_name' => 'John',
|
||||
* 'last_name' => 'John',
|
||||
* ],
|
||||
* [
|
||||
* 'status' => ['$in' => [1, 2, 3]],
|
||||
* ]
|
||||
* ]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Note: condition values for the key '_id' will be automatically cast to [[\MongoDB\BSON\ObjectID]] instance,
|
||||
* even if they are plain strings. However, if you have other columns, containing [[\MongoDB\BSON\ObjectID]], you
|
||||
* should take care of possible typecast on your own.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class QueryBuilder extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Connection the MongoDB connection.
|
||||
*/
|
||||
public $db;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param Connection $connection the database connection.
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties
|
||||
*/
|
||||
public function __construct($connection, $config = [])
|
||||
{
|
||||
$this->db = $connection;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
// Commands :
|
||||
|
||||
/**
|
||||
* Generates 'create collection' command.
|
||||
* https://docs.mongodb.com/manual/reference/method/db.createCollection/
|
||||
* @param string $collectionName collection name.
|
||||
* @param array $options collection options in format: "name" => "value"
|
||||
* @return array command document.
|
||||
*/
|
||||
public function createCollection($collectionName, array $options = [])
|
||||
{
|
||||
$document = array_merge(['create' => $collectionName], $options);
|
||||
|
||||
if (isset($document['indexOptionDefaults'])) {
|
||||
$document['indexOptionDefaults'] = (object) $document['indexOptionDefaults'];
|
||||
}
|
||||
if (isset($document['storageEngine'])) {
|
||||
$document['storageEngine'] = (object) $document['storageEngine'];
|
||||
}
|
||||
if (isset($document['validator'])) {
|
||||
$document['validator'] = (object) $document['validator'];
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates drop database command.
|
||||
* https://docs.mongodb.com/manual/reference/method/db.dropDatabase/
|
||||
* @return array command document.
|
||||
*/
|
||||
public function dropDatabase()
|
||||
{
|
||||
return ['dropDatabase' => 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates drop collection command.
|
||||
* https://docs.mongodb.com/manual/reference/method/db.collection.drop/
|
||||
* @param string $collectionName name of the collection to be dropped.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function dropCollection($collectionName)
|
||||
{
|
||||
return ['drop' => $collectionName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates create indexes command.
|
||||
* @see https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/
|
||||
* @param string|null $databaseName database name.
|
||||
* @param string $collectionName collection name.
|
||||
* @param array[] $indexes indexes specification. Each specification should be an array in format: optionName => value
|
||||
* The main options are:
|
||||
*
|
||||
* - keys: array, column names with sort order, to be indexed. This option is mandatory.
|
||||
* - unique: bool, whether to create unique index.
|
||||
* - name: string, the name of the index, if not set it will be generated automatically.
|
||||
* - background: bool, whether to bind index in the background.
|
||||
* - sparse: bool, whether index should reference only documents with the specified field.
|
||||
*
|
||||
* See [[https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/#options-for-all-index-types]]
|
||||
* for the full list of options.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function createIndexes($databaseName, $collectionName, $indexes)
|
||||
{
|
||||
$normalizedIndexes = [];
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
if (!isset($index['key'])) {
|
||||
throw new InvalidParamException('"key" is required for index specification');
|
||||
}
|
||||
|
||||
$index['key'] = $this->buildSortFields($index['key']);
|
||||
|
||||
if (!isset($index['ns'])) {
|
||||
if ($databaseName === null) {
|
||||
$databaseName = $this->db->getDefaultDatabaseName();
|
||||
}
|
||||
$index['ns'] = $databaseName . '.' . $collectionName;
|
||||
}
|
||||
|
||||
if (!isset($index['name'])) {
|
||||
$index['name'] = $this->generateIndexName($index['key']);
|
||||
}
|
||||
|
||||
$normalizedIndexes[] = $index;
|
||||
}
|
||||
|
||||
return [
|
||||
'createIndexes' => $collectionName,
|
||||
'indexes' => $normalizedIndexes,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates index name for the given column orders.
|
||||
* Columns should be normalized using [[buildSortFields()]] before being passed to this method.
|
||||
* @param array $columns columns with sort order.
|
||||
* @return string index name.
|
||||
*/
|
||||
public function generateIndexName($columns)
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($columns as $column => $order) {
|
||||
$parts[] = $column . '_' . $order;
|
||||
}
|
||||
return implode('_', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates drop indexes command.
|
||||
* @param string $collectionName collection name
|
||||
* @param string $index index name or pattern, use `*` in order to drop all indexes.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function dropIndexes($collectionName, $index)
|
||||
{
|
||||
return [
|
||||
'dropIndexes' => $collectionName,
|
||||
'index' => $index,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates list indexes command.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $options command options.
|
||||
* Available options are:
|
||||
*
|
||||
* - maxTimeMS: int, max execution time in ms.
|
||||
*
|
||||
* @return array command document.
|
||||
*/
|
||||
public function listIndexes($collectionName, $options = [])
|
||||
{
|
||||
return array_merge(['listIndexes' => $collectionName], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates count command
|
||||
* @param string $collectionName
|
||||
* @param array $condition
|
||||
* @param array $options
|
||||
* @return array command document.
|
||||
*/
|
||||
public function count($collectionName, $condition = [], $options = [])
|
||||
{
|
||||
$document = ['count' => $collectionName];
|
||||
|
||||
if (!empty($condition)) {
|
||||
$document['query'] = (object) $this->buildCondition($condition);
|
||||
}
|
||||
|
||||
return array_merge($document, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'find and modify' command.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $condition filter condition
|
||||
* @param array $update update criteria
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function findAndModify($collectionName, $condition = [], $update = [], $options = [])
|
||||
{
|
||||
$document = array_merge(['findAndModify' => $collectionName], $options);
|
||||
|
||||
if (!empty($condition)) {
|
||||
$options['query'] = $this->buildCondition($condition);
|
||||
}
|
||||
|
||||
if (!empty($update)) {
|
||||
$options['update'] = $update;
|
||||
}
|
||||
|
||||
if (isset($options['fields'])) {
|
||||
$options['fields'] = $this->buildSelectFields($options['fields']);
|
||||
}
|
||||
|
||||
if (isset($options['sort'])) {
|
||||
$options['sort'] = $this->buildSortFields($options['sort']);
|
||||
}
|
||||
|
||||
foreach (['fields', 'query', 'sort', 'update'] as $name) {
|
||||
if (isset($options[$name])) {
|
||||
$document[$name] = (object) $options[$name];
|
||||
}
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'distinct' command.
|
||||
* @param string $collectionName collection name.
|
||||
* @param string $fieldName target field name.
|
||||
* @param array $condition filter condition
|
||||
* @param array $options list of options in format: optionName => optionValue.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function distinct($collectionName, $fieldName, $condition = [], $options = [])
|
||||
{
|
||||
$document = array_merge(
|
||||
[
|
||||
'distinct' => $collectionName,
|
||||
'key' => $fieldName,
|
||||
],
|
||||
$options
|
||||
);
|
||||
|
||||
if (!empty($condition)) {
|
||||
$document['query'] = $this->buildCondition($condition);
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'group' command.
|
||||
* @param string $collectionName
|
||||
* @@param mixed $keys fields to group by. If an array or non-code object is passed,
|
||||
* it will be the key used to group results. If instance of [[Javascript]] passed,
|
||||
* it will be treated as a function that returns the key to group by.
|
||||
* @param array $initial Initial value of the aggregation counter object.
|
||||
* @param Javascript|string $reduce function that takes two arguments (the current
|
||||
* document and the aggregation to this point) and does the aggregation.
|
||||
* Argument will be automatically cast to [[Javascript]].
|
||||
* @param array $options optional parameters to the group command. Valid options include:
|
||||
* - condition - criteria for including a document in the aggregation.
|
||||
* - finalize - function called once per unique key that takes the final output of the reduce function.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function group($collectionName, $keys, $initial, $reduce, $options = [])
|
||||
{
|
||||
if (!($reduce instanceof Javascript)) {
|
||||
$reduce = new Javascript((string) $reduce);
|
||||
}
|
||||
|
||||
if (isset($options['condition'])) {
|
||||
$options['cond'] = $this->buildCondition($options['condition']);
|
||||
unset($options['condition']);
|
||||
}
|
||||
|
||||
if (isset($options['finalize'])) {
|
||||
if (!($options['finalize'] instanceof Javascript)) {
|
||||
$options['finalize'] = new Javascript((string) $options['finalize']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['keyf'])) {
|
||||
$options['$keyf'] = $options['keyf'];
|
||||
unset($options['keyf']);
|
||||
}
|
||||
if (isset($options['$keyf'])) {
|
||||
if (!($options['$keyf'] instanceof Javascript)) {
|
||||
$options['$keyf'] = new Javascript((string) $options['$keyf']);
|
||||
}
|
||||
}
|
||||
|
||||
$document = [
|
||||
'group' => array_merge(
|
||||
[
|
||||
'ns' => $collectionName,
|
||||
'key' => $keys,
|
||||
'initial' => $initial,
|
||||
'$reduce' => $reduce,
|
||||
],
|
||||
$options
|
||||
)
|
||||
];
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'map-reduce' command.
|
||||
* @see https://docs.mongodb.com/manual/core/map-reduce/
|
||||
* @param string $collectionName collection name.
|
||||
* @param \MongoDB\BSON\Javascript|string $map function, which emits map data from collection.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param \MongoDB\BSON\Javascript|string $reduce function that takes two arguments (the map key
|
||||
* and the map values) and does the aggregation.
|
||||
* Argument will be automatically cast to [[\MongoDB\BSON\Javascript]].
|
||||
* @param string|array $out output collection name. It could be a string for simple output
|
||||
* ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
|
||||
* You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
|
||||
* @param array $condition filter condition for including a document in the aggregation.
|
||||
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
|
||||
*
|
||||
* - sort: array, key to sort the input documents. The sort key must be in an existing index for this collection.
|
||||
* - limit: int, the maximum number of documents to return in the collection.
|
||||
* - finalize: \MongoDB\BSON\Javascript|string, function, which follows the reduce method and modifies the output.
|
||||
* - scope: array, specifies global variables that are accessible in the map, reduce and finalize functions.
|
||||
* - jsMode: bool, specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
|
||||
* - verbose: bool, specifies whether to include the timing information in the result information.
|
||||
*
|
||||
* @return array command document.
|
||||
*/
|
||||
public function mapReduce($collectionName, $map, $reduce, $out, $condition = [], $options = [])
|
||||
{
|
||||
if (!($map instanceof Javascript)) {
|
||||
$map = new Javascript((string) $map);
|
||||
}
|
||||
if (!($reduce instanceof Javascript)) {
|
||||
$reduce = new Javascript((string) $reduce);
|
||||
}
|
||||
|
||||
$document = [
|
||||
'mapReduce' => $collectionName,
|
||||
'map' => $map,
|
||||
'reduce' => $reduce,
|
||||
'out' => $out
|
||||
];
|
||||
|
||||
if (!empty($condition)) {
|
||||
$document['query'] = $this->buildCondition($condition);
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$document = array_merge($document, $options);
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'aggregate' command.
|
||||
* @param string $collectionName collection name
|
||||
* @param array $pipelines list of pipeline operators.
|
||||
* @param array $options optional parameters.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function aggregate($collectionName, $pipelines, $options = [])
|
||||
{
|
||||
foreach ($pipelines as $key => $pipeline) {
|
||||
if (isset($pipeline['$match'])) {
|
||||
$pipelines[$key]['$match'] = $this->buildCondition($pipeline['$match']);
|
||||
}
|
||||
}
|
||||
|
||||
$document = array_merge(
|
||||
[
|
||||
'aggregate' => $collectionName,
|
||||
'pipeline' => $pipelines,
|
||||
'allowDiskUse' => false,
|
||||
],
|
||||
$options
|
||||
);
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'explain' command.
|
||||
* @param string $collectionName collection name.
|
||||
* @param array $query query options.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function explain($collectionName, $query)
|
||||
{
|
||||
$query = array_merge(
|
||||
['find' => $collectionName],
|
||||
$query
|
||||
);
|
||||
|
||||
if (isset($query['filter'])) {
|
||||
$query['filter'] = (object) $this->buildCondition($query['filter']);
|
||||
}
|
||||
if (isset($query['projection'])) {
|
||||
$query['projection'] = $this->buildSelectFields($query['projection']);
|
||||
}
|
||||
if (isset($query['sort'])) {
|
||||
$query['sort'] = $this->buildSortFields($query['sort']);
|
||||
}
|
||||
|
||||
return [
|
||||
'explain' => $query,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'listDatabases' command.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options command options.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function listDatabases($condition = [], $options = [])
|
||||
{
|
||||
$document = array_merge(['listDatabases' => 1], $options);
|
||||
if (!empty($condition)) {
|
||||
$document['filter'] = (object)$this->buildCondition($condition);
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 'listCollections' command.
|
||||
* @param array $condition filter condition.
|
||||
* @param array $options command options.
|
||||
* @return array command document.
|
||||
*/
|
||||
public function listCollections($condition = [], $options = [])
|
||||
{
|
||||
$document = array_merge(['listCollections' => 1], $options);
|
||||
if (!empty($condition)) {
|
||||
$document['filter'] = (object)$this->buildCondition($condition);
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
// Service :
|
||||
|
||||
/**
|
||||
* Normalizes fields list for the MongoDB select composition.
|
||||
* @param array|string $fields raw fields.
|
||||
* @return array normalized select fields.
|
||||
*/
|
||||
public function buildSelectFields($fields)
|
||||
{
|
||||
$selectFields = [];
|
||||
foreach ((array)$fields as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
$selectFields[$value] = true;
|
||||
} else {
|
||||
$selectFields[$key] = is_scalar($value) ? (bool)$value : $value;
|
||||
}
|
||||
}
|
||||
return $selectFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes fields list for the MongoDB sort composition.
|
||||
* @param array|string $fields raw fields.
|
||||
* @return array normalized sort fields.
|
||||
*/
|
||||
public function buildSortFields($fields)
|
||||
{
|
||||
$sortFields = [];
|
||||
foreach ((array)$fields as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
$sortFields[$value] = +1;
|
||||
} else {
|
||||
if ($value === SORT_ASC) {
|
||||
$value = +1;
|
||||
} elseif ($value === SORT_DESC) {
|
||||
$value = -1;
|
||||
}
|
||||
$sortFields[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $sortFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts "\yii\db\*" quick condition keyword into actual Mongo condition keyword.
|
||||
* @param string $key raw condition key.
|
||||
* @return string actual key.
|
||||
*/
|
||||
protected function normalizeConditionKeyword($key)
|
||||
{
|
||||
static $map = [
|
||||
'AND' => '$and',
|
||||
'OR' => '$or',
|
||||
'IN' => '$in',
|
||||
'NOT IN' => '$nin',
|
||||
];
|
||||
$matchKey = strtoupper($key);
|
||||
if (array_key_exists($matchKey, $map)) {
|
||||
return $map[$matchKey];
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given value into [[ObjectID]] instance.
|
||||
* If array given, each element of it will be processed.
|
||||
* @param mixed $rawId raw id(s).
|
||||
* @return array|ObjectID normalized id(s).
|
||||
*/
|
||||
protected function ensureMongoId($rawId)
|
||||
{
|
||||
if (is_array($rawId)) {
|
||||
$result = [];
|
||||
foreach ($rawId as $key => $value) {
|
||||
$result[$key] = $this->ensureMongoId($value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
} elseif (is_object($rawId)) {
|
||||
if ($rawId instanceof ObjectID) {
|
||||
return $rawId;
|
||||
} else {
|
||||
$rawId = (string) $rawId;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$mongoId = new ObjectID($rawId);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// invalid id format
|
||||
$mongoId = $rawId;
|
||||
}
|
||||
|
||||
return $mongoId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the condition specification and generates the corresponding Mongo condition.
|
||||
* @param array $condition the condition specification. Please refer to [[Query::where()]]
|
||||
* on how to specify a condition.
|
||||
* @return array the generated Mongo condition
|
||||
* @throws InvalidParamException if the condition is in bad format
|
||||
*/
|
||||
public function buildCondition($condition)
|
||||
{
|
||||
static $builders = [
|
||||
'NOT' => 'buildNotCondition',
|
||||
'AND' => 'buildAndCondition',
|
||||
'OR' => 'buildOrCondition',
|
||||
'BETWEEN' => 'buildBetweenCondition',
|
||||
'NOT BETWEEN' => 'buildBetweenCondition',
|
||||
'IN' => 'buildInCondition',
|
||||
'NOT IN' => 'buildInCondition',
|
||||
'REGEX' => 'buildRegexCondition',
|
||||
'LIKE' => 'buildLikeCondition',
|
||||
];
|
||||
|
||||
if (!is_array($condition)) {
|
||||
throw new InvalidParamException('Condition should be an array.');
|
||||
} elseif (empty($condition)) {
|
||||
return [];
|
||||
}
|
||||
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
|
||||
$operator = strtoupper($condition[0]);
|
||||
if (isset($builders[$operator])) {
|
||||
$method = $builders[$operator];
|
||||
} else {
|
||||
$operator = $condition[0];
|
||||
$method = 'buildSimpleCondition';
|
||||
}
|
||||
array_shift($condition);
|
||||
return $this->$method($operator, $condition);
|
||||
}
|
||||
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
|
||||
return $this->buildHashCondition($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition based on column-value pairs.
|
||||
* @param array $condition the condition specification.
|
||||
* @return array the generated Mongo condition.
|
||||
*/
|
||||
public function buildHashCondition($condition)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($condition as $name => $value) {
|
||||
if (strncmp('$', $name, 1) === 0) {
|
||||
// Native Mongo condition:
|
||||
$result[$name] = $value;
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
if (ArrayHelper::isIndexed($value)) {
|
||||
// Quick IN condition:
|
||||
$result = array_merge($result, $this->buildInCondition('IN', [$name, $value]));
|
||||
} else {
|
||||
// Mongo complex condition:
|
||||
$result[$name] = $value;
|
||||
}
|
||||
} else {
|
||||
// Direct match:
|
||||
if ($name == '_id') {
|
||||
$value = $this->ensureMongoId($value);
|
||||
}
|
||||
$result[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes `NOT` condition.
|
||||
* @param string $operator the operator to use for connecting the given operands
|
||||
* @param array $operands the Mongo conditions to connect.
|
||||
* @return array the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildNotCondition($operator, $operands)
|
||||
{
|
||||
if (count($operands) !== 2) {
|
||||
throw new InvalidParamException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
list($name, $value) = $operands;
|
||||
|
||||
$result = [];
|
||||
if (is_array($value)) {
|
||||
$result[$name] = ['$not' => $this->buildCondition($value)];
|
||||
} else {
|
||||
if ($name == '_id') {
|
||||
$value = $this->ensureMongoId($value);
|
||||
}
|
||||
$result[$name] = ['$ne' => $value];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects two or more conditions with the `AND` operator.
|
||||
* @param string $operator the operator to use for connecting the given operands
|
||||
* @param array $operands the Mongo conditions to connect.
|
||||
* @return array the generated Mongo condition.
|
||||
*/
|
||||
public function buildAndCondition($operator, $operands)
|
||||
{
|
||||
$operator = $this->normalizeConditionKeyword($operator);
|
||||
$parts = [];
|
||||
foreach ($operands as $operand) {
|
||||
$parts[] = $this->buildCondition($operand);
|
||||
}
|
||||
|
||||
return [$operator => $parts];
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects two or more conditions with the `OR` operator.
|
||||
* @param string $operator the operator to use for connecting the given operands
|
||||
* @param array $operands the Mongo conditions to connect.
|
||||
* @return array the generated Mongo condition.
|
||||
*/
|
||||
public function buildOrCondition($operator, $operands)
|
||||
{
|
||||
$operator = $this->normalizeConditionKeyword($operator);
|
||||
$parts = [];
|
||||
foreach ($operands as $operand) {
|
||||
$parts[] = $this->buildCondition($operand);
|
||||
}
|
||||
|
||||
return [$operator => $parts];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Mongo condition, which emulates the `BETWEEN` operator.
|
||||
* @param string $operator the operator to use
|
||||
* @param array $operands the first operand is the column name. The second and third operands
|
||||
* describe the interval that column value should be in.
|
||||
* @return array the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildBetweenCondition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1], $operands[2])) {
|
||||
throw new InvalidParamException("Operator '$operator' requires three operands.");
|
||||
}
|
||||
list($column, $value1, $value2) = $operands;
|
||||
|
||||
if (strncmp('NOT', $operator, 3) === 0) {
|
||||
return [
|
||||
$column => [
|
||||
'$lt' => $value1,
|
||||
'$gt' => $value2,
|
||||
]
|
||||
];
|
||||
}
|
||||
return [
|
||||
$column => [
|
||||
'$gte' => $value1,
|
||||
'$lte' => $value2,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Mongo condition with the `IN` operator.
|
||||
* @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
|
||||
* @param array $operands the first operand is the column name. If it is an array
|
||||
* a composite IN condition will be generated.
|
||||
* The second operand is an array of values that column value should be among.
|
||||
* @return array the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildInCondition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new InvalidParamException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
list($column, $values) = $operands;
|
||||
|
||||
$values = (array) $values;
|
||||
$operator = $this->normalizeConditionKeyword($operator);
|
||||
|
||||
if (!is_array($column)) {
|
||||
$columns = [$column];
|
||||
$values = [$column => $values];
|
||||
} elseif (count($column) > 1) {
|
||||
return $this->buildCompositeInCondition($operator, $column, $values);
|
||||
} else {
|
||||
$columns = $column;
|
||||
$values = [$column[0] => $values];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($columns as $column) {
|
||||
if ($column == '_id') {
|
||||
$inValues = $this->ensureMongoId($values[$column]);
|
||||
} else {
|
||||
$inValues = $values[$column];
|
||||
}
|
||||
|
||||
$inValues = array_values($inValues);
|
||||
if (count($inValues) === 1 && $operator === '$in') {
|
||||
$result[$column] = $inValues[0];
|
||||
} else {
|
||||
$result[$column][$operator] = $inValues;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $operator MongoDB the operator to use (`$in` OR `$nin`)
|
||||
* @param array $columns list of compare columns
|
||||
* @param array $values compare values in format: columnName => [values]
|
||||
* @return array the generated Mongo condition.
|
||||
*/
|
||||
private function buildCompositeInCondition($operator, $columns, $values)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$inValues = [];
|
||||
foreach ($values as $columnValues) {
|
||||
foreach ($columnValues as $column => $value) {
|
||||
if ($column == '_id') {
|
||||
$value = $this->ensureMongoId($value);
|
||||
}
|
||||
$inValues[$column][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$columnInValues = array_values($inValues[$column]);
|
||||
if (count($columnInValues) === 1 && $operator === '$in') {
|
||||
$result[$column] = $columnInValues[0];
|
||||
} else {
|
||||
$result[$column][$operator] = $columnInValues;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Mongo regular expression condition.
|
||||
* @param string $operator the operator to use
|
||||
* @param array $operands the first operand is the column name.
|
||||
* The second operand is a single value that column value should be compared with.
|
||||
* @return array the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildRegexCondition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new InvalidParamException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
list($column, $value) = $operands;
|
||||
if (!($value instanceof Regex)) {
|
||||
if (preg_match('~\/(.+)\/(.*)~', $value, $matches)) {
|
||||
$value = new Regex($matches[1], $matches[2]);
|
||||
} else {
|
||||
$value = new Regex($value, '');
|
||||
}
|
||||
}
|
||||
|
||||
return [$column => $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Mongo condition, which emulates the `LIKE` operator.
|
||||
* @param string $operator the operator to use
|
||||
* @param array $operands the first operand is the column name.
|
||||
* The second operand is a single value that column value should be compared with.
|
||||
* @return array the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildLikeCondition($operator, $operands)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new InvalidParamException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
list($column, $value) = $operands;
|
||||
if (!($value instanceof Regex)) {
|
||||
$value = new Regex(preg_quote($value), 'i');
|
||||
}
|
||||
|
||||
return [$column => $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Mongo condition like `{$operator:{field:value}}`.
|
||||
* @param string $operator the operator to use. Besides regular MongoDB operators, aliases like `>`, `<=`,
|
||||
* and so on, can be used here.
|
||||
* @param array $operands the first operand is the column name.
|
||||
* The second operand is a single value that column value should be compared with.
|
||||
* @return string the generated Mongo condition.
|
||||
* @throws InvalidParamException if wrong number of operands have been given.
|
||||
*/
|
||||
public function buildSimpleCondition($operator, $operands)
|
||||
{
|
||||
if (count($operands) !== 2) {
|
||||
throw new InvalidParamException("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
list($column, $value) = $operands;
|
||||
|
||||
if (strncmp('$', $operator, 1) !== 0) {
|
||||
static $operatorMap = [
|
||||
'>' => '$gt',
|
||||
'<' => '$lt',
|
||||
'>=' => '$gte',
|
||||
'<=' => '$lte',
|
||||
'!=' => '$ne',
|
||||
'<>' => '$ne',
|
||||
'=' => '$eq',
|
||||
'==' => '$eq',
|
||||
];
|
||||
if (isset($operatorMap[$operator])) {
|
||||
$operator = $operatorMap[$operator];
|
||||
} else {
|
||||
throw new InvalidParamException("Unsupported operator '{$operator}'");
|
||||
}
|
||||
}
|
||||
|
||||
return [$column => [$operator => $value]];
|
||||
}
|
||||
}
|
||||
184
vendor/yiisoft/yii2-mongodb/src/Session.php
vendored
Normal file
184
vendor/yiisoft/yii2-mongodb/src/Session.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb;
|
||||
|
||||
use Yii;
|
||||
use yii\base\ErrorHandler;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\di\Instance;
|
||||
use yii\web\MultiFieldSession;
|
||||
|
||||
/**
|
||||
* Session extends [[\yii\web\Session]] by using MongoDB as session data storage.
|
||||
*
|
||||
* By default, Session stores session data in a collection named 'session' inside the default database.
|
||||
* This collection is better to be pre-created with fields 'id' and 'expire' indexed.
|
||||
* The collection name can be changed by setting [[sessionCollection]].
|
||||
*
|
||||
* The following example shows how you can configure the application to use Session:
|
||||
* Add the following to your application config under `components`:
|
||||
*
|
||||
* ```php
|
||||
* 'session' => [
|
||||
* 'class' => 'yii\mongodb\Session',
|
||||
* // 'db' => 'mymongodb',
|
||||
* // 'sessionCollection' => 'my_session',
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Session extends [[MultiFieldSession]], thus it allows saving extra fields into the [[sessionCollection]].
|
||||
* Refer to [[MultiFieldSession]] for more details.
|
||||
*
|
||||
* Tip: you can use MongoDB [TTL index](https://docs.mongodb.com/manual/tutorial/expire-data/) for the session garbage
|
||||
* collection for performance saving, in this case you should set [[Session::gCProbability]] to `0`.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Session extends MultiFieldSession
|
||||
{
|
||||
/**
|
||||
* @var Connection|array|string the MongoDB connection object or the application component ID of the MongoDB connection.
|
||||
* After the Session object is created, if you want to change this property, you should only assign it
|
||||
* with a MongoDB connection object.
|
||||
* Starting from version 2.0.2, this can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var string|array the name of the MongoDB collection that stores the session data.
|
||||
* Please refer to [[Connection::getCollection()]] on how to specify this parameter.
|
||||
* This collection is better to be pre-created with fields 'id' and 'expire' indexed.
|
||||
*/
|
||||
public $sessionCollection = 'session';
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the Session component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid MongoDB connection.
|
||||
* @throws InvalidConfigException if [[db]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current session ID with a newly generated one.
|
||||
* Please refer to <http://php.net/session_regenerate_id> for more details.
|
||||
* @param bool $deleteOldSession Whether to delete the old associated session file or not.
|
||||
*/
|
||||
public function regenerateID($deleteOldSession = false)
|
||||
{
|
||||
$oldID = session_id();
|
||||
|
||||
// if no session is started, there is nothing to regenerate
|
||||
if (empty($oldID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::regenerateID(false);
|
||||
$newID = session_id();
|
||||
|
||||
$collection = $this->db->getCollection($this->sessionCollection);
|
||||
$row = $collection->findOne(['id' => $oldID]);
|
||||
if ($row !== null) {
|
||||
if ($deleteOldSession) {
|
||||
$collection->update(['id' => $oldID], ['id' => $newID]);
|
||||
} else {
|
||||
unset($row['_id']);
|
||||
$row['id'] = $newID;
|
||||
$collection->insert($row);
|
||||
}
|
||||
} else {
|
||||
// shouldn't reach here normally
|
||||
$collection->insert($this->composeFields($newID, ''));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Session read handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @return string the session data
|
||||
*/
|
||||
public function readSession($id)
|
||||
{
|
||||
$collection = $this->db->getCollection($this->sessionCollection);
|
||||
$condition = [
|
||||
'id' => $id,
|
||||
'expire' => ['$gt' => time()],
|
||||
];
|
||||
|
||||
if (isset($this->readCallback)) {
|
||||
$doc = $collection->findOne($condition);
|
||||
return $doc === null ? '' : $this->extractData($doc);
|
||||
}
|
||||
|
||||
$doc = $collection->findOne(
|
||||
$condition,
|
||||
['data' => 1, '_id' => 0]
|
||||
);
|
||||
return isset($doc['data']) ? $doc['data'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Session write handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @param string $data session data
|
||||
* @return bool whether session write is successful
|
||||
*/
|
||||
public function writeSession($id, $data)
|
||||
{
|
||||
// exception must be caught in session write handler
|
||||
// http://us.php.net/manual/en/function.session-set-save-handler.php
|
||||
try {
|
||||
$this->db->getCollection($this->sessionCollection)->update(
|
||||
['id' => $id],
|
||||
$this->composeFields($id, $data),
|
||||
['upsert' => true]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
Yii::$app->errorHandler->handleException($e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session destroy handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @return bool whether session is destroyed successfully
|
||||
*/
|
||||
public function destroySession($id)
|
||||
{
|
||||
$this->db->getCollection($this->sessionCollection)->remove(
|
||||
['id' => $id],
|
||||
['justOne' => true]
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session GC (garbage collection) handler.
|
||||
* Do not call this method directly.
|
||||
* @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
|
||||
* @return bool whether session is GCed successfully
|
||||
*/
|
||||
public function gcSession($maxLifetime)
|
||||
{
|
||||
$this->db->getCollection($this->sessionCollection)
|
||||
->remove(['expire' => ['$lt' => time()]]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
271
vendor/yiisoft/yii2-mongodb/src/console/controllers/MigrateController.php
vendored
Normal file
271
vendor/yiisoft/yii2-mongodb/src/console/controllers/MigrateController.php
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\console\controllers;
|
||||
|
||||
use Yii;
|
||||
use yii\console\controllers\BaseMigrateController;
|
||||
use yii\console\Exception;
|
||||
use yii\mongodb\Connection;
|
||||
use yii\mongodb\Query;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* Manages application MongoDB migrations.
|
||||
*
|
||||
* This is an analog of [[\yii\console\controllers\MigrateController]] for MongoDB.
|
||||
*
|
||||
* This command provides support for tracking the migration history, upgrading
|
||||
* or downloading with migrations, and creating new migration skeletons.
|
||||
*
|
||||
* The migration history is stored in a MongoDB collection named
|
||||
* as [[migrationCollection]]. This collection will be automatically created the first time
|
||||
* this command is executed, if it does not exist.
|
||||
*
|
||||
* In order to enable this command you should adjust the configuration of your console application:
|
||||
*
|
||||
* ```php
|
||||
* return [
|
||||
* // ...
|
||||
* 'controllerMap' => [
|
||||
* 'mongodb-migrate' => 'yii\mongodb\console\controllers\MigrateController'
|
||||
* ],
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* Below are some common usages of this command:
|
||||
*
|
||||
* ```php
|
||||
* # creates a new migration named 'create_user_collection'
|
||||
* yii mongodb-migrate/create create_user_collection
|
||||
*
|
||||
* # applies ALL new migrations
|
||||
* yii mongodb-migrate
|
||||
*
|
||||
* # reverts the last applied migration
|
||||
* yii mongodb-migrate/down
|
||||
* ```
|
||||
*
|
||||
* Since 2.1.2, in case of usage Yii version >= 2.0.10, you can use namespaced migrations. In order to enable this
|
||||
* feature you should configure [[migrationNamespaces]] property for the controller at application configuration:
|
||||
*
|
||||
* ```php
|
||||
* return [
|
||||
* 'controllerMap' => [
|
||||
* 'mongodb-migrate' => [
|
||||
* 'class' => 'yii\mongodb\console\controllers\MigrateController',
|
||||
* 'migrationNamespaces' => [
|
||||
* 'app\migrations',
|
||||
* 'some\extension\migrations',
|
||||
* ],
|
||||
* //'migrationPath' => null, // allows to disable not namespaced migration completely
|
||||
* ],
|
||||
* ],
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @author Klimov Paul <klimov@zfort.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class MigrateController extends BaseMigrateController
|
||||
{
|
||||
/**
|
||||
* @var string|array the name of the collection for keeping applied migration information.
|
||||
*/
|
||||
public $migrationCollection = 'migration';
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $templateFile = '@yii/mongodb/views/migration.php';
|
||||
/**
|
||||
* @var Connection|string the DB connection object or the application
|
||||
* component ID of the DB connection.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function options($actionID)
|
||||
{
|
||||
return array_merge(
|
||||
parent::options($actionID),
|
||||
['migrationCollection', 'db'] // global for all actions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked right before an action is to be executed (after all possible filters.)
|
||||
* It checks the existence of the [[migrationPath]].
|
||||
* @param \yii\base\Action $action the action to be executed.
|
||||
* @throws Exception if db component isn't configured
|
||||
* @return bool whether the action should continue to be executed.
|
||||
*/
|
||||
public function beforeAction($action)
|
||||
{
|
||||
if (parent::beforeAction($action)) {
|
||||
if ($action->id !== 'create') {
|
||||
if (is_string($this->db)) {
|
||||
$this->db = Yii::$app->get($this->db);
|
||||
}
|
||||
if (!$this->db instanceof Connection) {
|
||||
throw new Exception("The 'db' option must refer to the application component ID of a MongoDB connection.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new migration instance.
|
||||
* @param string $class the migration class name
|
||||
* @return \yii\mongodb\Migration the migration instance
|
||||
*/
|
||||
protected function createMigration($class)
|
||||
{
|
||||
// since Yii 2.0.12 includeMigrationFile() exists, which replaced the code below
|
||||
// remove this construct when composer requirement raises above 2.0.12
|
||||
if (method_exists($this, 'includeMigrationFile')) {
|
||||
$this->includeMigrationFile($class);
|
||||
} else {
|
||||
$class = trim($class, '\\');
|
||||
if (strpos($class, '\\') === false) {
|
||||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
|
||||
require_once($file);
|
||||
}
|
||||
}
|
||||
|
||||
return new $class(['db' => $this->db, 'compact' => isset($this->compact) ? $this->compact : false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getMigrationHistory($limit)
|
||||
{
|
||||
$this->ensureBaseMigrationHistory();
|
||||
|
||||
$query = (new Query())
|
||||
->select(['version', 'apply_time'])
|
||||
->from($this->migrationCollection)
|
||||
->orderBy(['apply_time' => SORT_DESC, 'version' => SORT_DESC]);
|
||||
|
||||
if (empty($this->migrationNamespaces)) {
|
||||
$query->limit($limit);
|
||||
$rows = $query->all($this->db);
|
||||
$history = ArrayHelper::map($rows, 'version', 'apply_time');
|
||||
unset($history[self::BASE_MIGRATION]);
|
||||
return $history;
|
||||
}
|
||||
|
||||
$rows = $query->all($this->db);
|
||||
|
||||
$history = [];
|
||||
foreach ($rows as $key => $row) {
|
||||
if ($row['version'] === self::BASE_MIGRATION) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/m?(\d{6}_?\d{6})(\D.*)?$/is', $row['version'], $matches)) {
|
||||
$time = str_replace('_', '', $matches[1]);
|
||||
$row['canonicalVersion'] = $time;
|
||||
} else {
|
||||
$row['canonicalVersion'] = $row['version'];
|
||||
}
|
||||
$row['apply_time'] = (int)$row['apply_time'];
|
||||
$history[] = $row;
|
||||
}
|
||||
|
||||
usort($history, function ($a, $b) {
|
||||
if ($a['apply_time'] === $b['apply_time']) {
|
||||
if (($compareResult = strcasecmp($b['canonicalVersion'], $a['canonicalVersion'])) !== 0) {
|
||||
return $compareResult;
|
||||
}
|
||||
return strcasecmp($b['version'], $a['version']);
|
||||
}
|
||||
return ($a['apply_time'] > $b['apply_time']) ? -1 : +1;
|
||||
});
|
||||
|
||||
$history = array_slice($history, 0, $limit);
|
||||
|
||||
$history = ArrayHelper::map($history, 'version', 'apply_time');
|
||||
|
||||
return $history;
|
||||
}
|
||||
|
||||
private $baseMigrationEnsured = false;
|
||||
|
||||
/**
|
||||
* Ensures migration history contains at least base migration entry.
|
||||
*/
|
||||
protected function ensureBaseMigrationHistory()
|
||||
{
|
||||
if (!$this->baseMigrationEnsured) {
|
||||
$query = new Query;
|
||||
$row = $query->select(['version'])
|
||||
->from($this->migrationCollection)
|
||||
->andWhere(['version' => self::BASE_MIGRATION])
|
||||
->limit(1)
|
||||
->one($this->db);
|
||||
if (empty($row)) {
|
||||
$this->addMigrationHistory(self::BASE_MIGRATION);
|
||||
}
|
||||
$this->baseMigrationEnsured = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function addMigrationHistory($version)
|
||||
{
|
||||
$this->db->getCollection($this->migrationCollection)->insert([
|
||||
'version' => $version,
|
||||
'apply_time' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function removeMigrationHistory($version)
|
||||
{
|
||||
$this->db->getCollection($this->migrationCollection)->remove([
|
||||
'version' => $version,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @since 2.1.5
|
||||
*/
|
||||
protected function truncateDatabase()
|
||||
{
|
||||
$collections = $this->db->getDatabase()->createCommand()->listCollections();
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
if (in_array($collection['name'], ['system.roles', 'system.users', 'system.indexes'])) {
|
||||
// prevent deleting database auth data
|
||||
// access to 'system.indexes' is more likely to be restricted, thus indexes will be dropped manually per collection
|
||||
$this->stdout("System collection {$collection['name']} skipped.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($collection['name'], ['system.profile', 'system.js'])) {
|
||||
// dropping of system collection is unlikely to be permitted, attempt to clear them out instead
|
||||
$this->db->getDatabase()->createCommand()->delete($collection['name'], []);
|
||||
$this->stdout("System collection {$collection['name']} truncated.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->getDatabase()->createCommand()->dropIndexes($collection['name'], '*');
|
||||
$this->db->getDatabase()->dropCollection($collection['name']);
|
||||
$this->stdout("Collection {$collection['name']} dropped.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
115
vendor/yiisoft/yii2-mongodb/src/debug/ExplainAction.php
vendored
Normal file
115
vendor/yiisoft/yii2-mongodb/src/debug/ExplainAction.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\debug;
|
||||
|
||||
use yii\base\Action;
|
||||
use yii\helpers\Json;
|
||||
use yii\web\HttpException;
|
||||
|
||||
/**
|
||||
* ExplainAction provides EXPLAIN information for MongoDB queries
|
||||
*
|
||||
* @author Sergey Smirnov <webdevsega@yandex.ru>
|
||||
* @author Klimov Paul <klimov@zfort.com>
|
||||
* @since 2.0.5
|
||||
*/
|
||||
class ExplainAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var MongoDbPanel related debug toolbar panel
|
||||
*/
|
||||
public $panel;
|
||||
|
||||
|
||||
/**
|
||||
* Runs the explain action
|
||||
* @param int $seq
|
||||
* @param string $tag
|
||||
* @return string explain result content
|
||||
* @throws HttpException if requested log not found
|
||||
*/
|
||||
public function run($seq, $tag)
|
||||
{
|
||||
$this->controller->loadData($tag);
|
||||
|
||||
$timings = $this->panel->calculateTimings();
|
||||
|
||||
if (!isset($timings[$seq])) {
|
||||
throw new HttpException(404, 'Log message not found.');
|
||||
}
|
||||
|
||||
$query = $timings[$seq]['info'];
|
||||
|
||||
if (strpos($query, 'find({') !== 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$query = substr($query, strlen('find('), -1);
|
||||
$result = $this->explainQuery($query);
|
||||
if (!$result) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Json::encode($result, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs explain command over the query
|
||||
*
|
||||
* @param string $queryString query log string.
|
||||
* @return array|false explain results, `false` on failure.
|
||||
*/
|
||||
protected function explainQuery($queryString)
|
||||
{
|
||||
/* @var $connection \yii\mongodb\Connection */
|
||||
$connection = $this->panel->getDb();
|
||||
|
||||
$queryInfo = Json::decode($queryString);
|
||||
if (!isset($queryInfo['ns'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($databaseName, $collectionName) = explode('.', $queryInfo['ns'], 2);
|
||||
unset($queryInfo['ns']);
|
||||
|
||||
if (!empty($queryInfo['filer'])) {
|
||||
$queryInfo['filer'] = $this->prepareQueryFiler($queryInfo['filer']);
|
||||
}
|
||||
|
||||
return $connection->createCommand($databaseName)->explain($collectionName, $queryInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare query filer for explain.
|
||||
* Converts BSON object log entries into actual objects.
|
||||
*
|
||||
* @param array $query raw query filter.
|
||||
* @return array|string prepared query
|
||||
*/
|
||||
private function prepareQueryFiler($query)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($query as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$result[$key] = $this->prepareQueryFiler($value);
|
||||
} elseif (is_string($value) && preg_match('#^(MongoDB\\\\BSON\\\\[A-Za-z]+)\\((.*)\\)$#s', $value, $matches)) {
|
||||
$class = $matches[1];
|
||||
$objectValue = $matches[1];
|
||||
|
||||
try {
|
||||
$result[$key] = new $class($objectValue);
|
||||
} catch (\Exception $e) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
121
vendor/yiisoft/yii2-mongodb/src/debug/MongoDbPanel.php
vendored
Normal file
121
vendor/yiisoft/yii2-mongodb/src/debug/MongoDbPanel.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\debug;
|
||||
|
||||
use Yii;
|
||||
use yii\debug\models\search\Db;
|
||||
use yii\debug\panels\DbPanel;
|
||||
use yii\log\Logger;
|
||||
|
||||
/**
|
||||
* MongoDbPanel panel that collects and displays MongoDB queries performed.
|
||||
*
|
||||
* @property array $profileLogs This property is read-only.
|
||||
*
|
||||
* @author Klimov Paul <klimov@zfort.com>
|
||||
* @since 2.0.1
|
||||
*/
|
||||
class MongoDbPanel extends DbPanel
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->actions['mongodb-explain'] = [
|
||||
'class' => 'yii\\mongodb\\debug\\ExplainAction',
|
||||
'panel' => $this,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'MongoDB';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummaryName()
|
||||
{
|
||||
return 'MongoDB';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDetail()
|
||||
{
|
||||
$searchModel = new Db();
|
||||
|
||||
if (!$searchModel->load(Yii::$app->request->getQueryParams())) {
|
||||
$searchModel->load($this->defaultFilter, '');
|
||||
}
|
||||
|
||||
$dataProvider = $searchModel->search($this->getModels());
|
||||
$dataProvider->getSort()->defaultOrder = $this->defaultOrder;
|
||||
|
||||
return Yii::$app->view->render('@yii/mongodb/debug/views/detail', [
|
||||
'panel' => $this,
|
||||
'dataProvider' => $dataProvider,
|
||||
'searchModel' => $searchModel,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all profile logs of the current request for this panel.
|
||||
* @return array
|
||||
*/
|
||||
public function getProfileLogs()
|
||||
{
|
||||
$target = $this->module->logTarget;
|
||||
|
||||
return $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, [
|
||||
'yii\mongodb\Command::*',
|
||||
'yii\mongodb\Query::*',
|
||||
'yii\mongodb\BatchQueryResult::*',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function hasExplain()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getQueryType($timing)
|
||||
{
|
||||
$timing = ltrim($timing);
|
||||
$timing = mb_substr($timing, 0, mb_strpos($timing, '('), 'utf8');
|
||||
$matches = explode('.', $timing);
|
||||
|
||||
return count($matches) ? array_pop($matches) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function canBeExplained($type)
|
||||
{
|
||||
return $type === 'find';
|
||||
}
|
||||
}
|
||||
116
vendor/yiisoft/yii2-mongodb/src/debug/views/detail.php
vendored
Normal file
116
vendor/yiisoft/yii2-mongodb/src/debug/views/detail.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/* @var $panel yii\mongodb\debug\MongoDbPanel */
|
||||
/* @var $searchModel yii\debug\models\search\Db */
|
||||
/* @var $dataProvider yii\data\ArrayDataProvider */
|
||||
|
||||
use yii\helpers\Html;
|
||||
use yii\grid\GridView;
|
||||
use yii\web\View;
|
||||
|
||||
echo Html::tag('h1', $panel->getName() . ' Queries');
|
||||
|
||||
echo GridView::widget([
|
||||
'dataProvider' => $dataProvider,
|
||||
'id' => 'db-panel-detailed-grid',
|
||||
'options' => ['class' => 'detail-grid-view table-responsive'],
|
||||
'filterModel' => $searchModel,
|
||||
'filterUrl' => $panel->getUrl(),
|
||||
'columns' => [
|
||||
[
|
||||
'attribute' => 'seq',
|
||||
'label' => 'Time',
|
||||
'value' => function ($data) {
|
||||
$timeInSeconds = $data['timestamp'] / 1000;
|
||||
$millisecondsDiff = (int) (($timeInSeconds - (int) $timeInSeconds) * 1000);
|
||||
|
||||
return date('H:i:s.', $timeInSeconds) . sprintf('%03d', $millisecondsDiff);
|
||||
},
|
||||
'headerOptions' => [
|
||||
'class' => 'sort-numerical'
|
||||
]
|
||||
],
|
||||
[
|
||||
'attribute' => 'duration',
|
||||
'value' => function ($data) {
|
||||
return sprintf('%.1f ms', $data['duration']);
|
||||
},
|
||||
'options' => [
|
||||
'width' => '10%',
|
||||
],
|
||||
'headerOptions' => [
|
||||
'class' => 'sort-numerical'
|
||||
]
|
||||
],
|
||||
[
|
||||
'attribute' => 'type',
|
||||
'value' => function ($data) {
|
||||
return Html::encode($data['type']);
|
||||
},
|
||||
'filter' => $panel->getTypes(),
|
||||
],
|
||||
[
|
||||
'attribute' => 'query',
|
||||
'value' => function ($data) use ($panel) {
|
||||
$query = Html::encode($data['query']);
|
||||
|
||||
if (!empty($data['trace'])) {
|
||||
$query .= Html::ul($data['trace'], [
|
||||
'class' => 'trace',
|
||||
'item' => function ($trace) use ($panel) {
|
||||
return '<li>' . $panel->getTraceLine($trace) . '</li>';
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if ($panel->canBeExplained($data['type'])) {
|
||||
$query .= Html::tag('p', '', ['class' => 'db-explain-text']);
|
||||
|
||||
$query .= Html::tag(
|
||||
'div',
|
||||
Html::a('[+] Explain', (['mongodb-explain', 'seq' => $data['seq'], 'tag' => Yii::$app->controller->summary['tag']])),
|
||||
['class' => 'db-explain']
|
||||
);
|
||||
}
|
||||
|
||||
return $query;
|
||||
},
|
||||
'format' => 'raw',
|
||||
'options' => [
|
||||
'width' => '60%',
|
||||
],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
echo Html::tag(
|
||||
'div',
|
||||
Html::a('[+] Explain all', '#'),
|
||||
['id' => 'db-explain-all']
|
||||
);
|
||||
|
||||
$this->registerJs('debug_db_detail();', View::POS_READY);
|
||||
?>
|
||||
|
||||
<script>
|
||||
function debug_db_detail() {
|
||||
$('.db-explain a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $explain = $('.db-explain-text', $(this).parent().parent());
|
||||
|
||||
if ($explain.is(':visible')) {
|
||||
$explain.hide();
|
||||
$(this).text('[+] Explain');
|
||||
} else {
|
||||
$explain.load($(this).attr('href')).show();
|
||||
$(this).text('[-] Explain');
|
||||
}
|
||||
});
|
||||
|
||||
$('#db-explain-all a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
$('.db-explain a').click();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
183
vendor/yiisoft/yii2-mongodb/src/file/ActiveQuery.php
vendored
Normal file
183
vendor/yiisoft/yii2-mongodb/src/file/ActiveQuery.php
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\ActiveQueryTrait;
|
||||
use yii\db\ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* ActiveQuery represents a Mongo query associated with an file Active Record class.
|
||||
*
|
||||
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
|
||||
*
|
||||
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
|
||||
* [[orderBy()]] to customize the query options.
|
||||
*
|
||||
* ActiveQuery also provides the following additional query options:
|
||||
*
|
||||
* - [[with()]]: list of relations that this query should be performed with.
|
||||
* - [[asArray()]]: whether to return each record as an array.
|
||||
*
|
||||
* These options can be configured using methods of the same name. For example:
|
||||
*
|
||||
* ```php
|
||||
* $images = ImageFile::find()->with('tags')->asArray()->all();
|
||||
* ```
|
||||
*
|
||||
* @property Collection $collection Collection instance. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveQuery extends Query implements ActiveQueryInterface
|
||||
{
|
||||
use ActiveQueryTrait;
|
||||
use ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* @event Event an event that is triggered when the query is initialized via [[init()]].
|
||||
*/
|
||||
const EVENT_INIT = 'init';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param array $modelClass the model class associated with this query
|
||||
* @param array $config configurations to be applied to the newly created query object
|
||||
*/
|
||||
public function __construct($modelClass, $config = [])
|
||||
{
|
||||
$this->modelClass = $modelClass;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
* This method is called at the end of the constructor. The default implementation will trigger
|
||||
* an [[EVENT_INIT]] event. If you override this method, make sure you call the parent implementation at the end
|
||||
* to ensure triggering of the event.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->trigger(self::EVENT_INIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildCursor($db = null)
|
||||
{
|
||||
if ($this->primaryModel !== null) {
|
||||
// lazy loading
|
||||
if ($this->via instanceof self) {
|
||||
// via pivot collection
|
||||
$viaModels = $this->via->findJunctionRows([$this->primaryModel]);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/* @var $viaQuery ActiveQuery */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
if ($viaQuery->multiple) {
|
||||
$viaModels = $viaQuery->all();
|
||||
$this->primaryModel->populateRelation($viaName, $viaModels);
|
||||
} else {
|
||||
$model = $viaQuery->one();
|
||||
$this->primaryModel->populateRelation($viaName, $model);
|
||||
$viaModels = $model === null ? [] : [$model];
|
||||
}
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels([$this->primaryModel]);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::buildCursor($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns all results as an array.
|
||||
* @param \yii\mongodb\Connection $db the Mongo connection used to execute the query.
|
||||
* If null, the Mongo connection returned by [[modelClass]] will be used.
|
||||
* @return array|ActiveRecord the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null)
|
||||
{
|
||||
return parent::all($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns a single row of result.
|
||||
* @param \yii\mongodb\Connection $db the Mongo connection used to execute the query.
|
||||
* If null, the Mongo connection returned by [[modelClass]] will be used.
|
||||
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
|
||||
* the query result may be either an array or an ActiveRecord object. Null will be returned
|
||||
* if the query results in nothing.
|
||||
*/
|
||||
public function one($db = null)
|
||||
{
|
||||
$row = parent::one($db);
|
||||
if ($row !== false) {
|
||||
$models = $this->populate([$row]);
|
||||
return reset($models) ?: null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Mongo collection for this query.
|
||||
* @param \yii\mongodb\Connection $db Mongo connection.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public function getCollection($db = null)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
if ($this->from === null) {
|
||||
$this->from = $modelClass::collectionName();
|
||||
}
|
||||
|
||||
return $db->getFileCollection($this->from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the raw query results into the format as specified by this query.
|
||||
* This method is internally used to convert the data fetched from MongoDB
|
||||
* into the format as required by this query.
|
||||
* @param array $rows the raw query result from MongoDB
|
||||
* @return array the converted query result
|
||||
*/
|
||||
public function populate($rows)
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$indexBy = $this->indexBy;
|
||||
$this->indexBy = null;
|
||||
$rows = parent::populate($rows);
|
||||
$this->indexBy = $indexBy;
|
||||
|
||||
$models = $this->createModels($rows);
|
||||
|
||||
if (!empty($this->with)) {
|
||||
$this->findWith($this->with, $models);
|
||||
}
|
||||
if (!$this->asArray) {
|
||||
foreach ($models as $model) {
|
||||
$model->afterFind();
|
||||
}
|
||||
}
|
||||
|
||||
return parent::populate($models);
|
||||
}
|
||||
}
|
||||
335
vendor/yiisoft/yii2-mongodb/src/file/ActiveRecord.php
vendored
Normal file
335
vendor/yiisoft/yii2-mongodb/src/file/ActiveRecord.php
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use Yii;
|
||||
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.
|
||||
*
|
||||
* To specify source file use the [[file]] attribute. It can be specified in one of the following ways:
|
||||
* - string - full name of the file, which content should be stored in GridFS
|
||||
* - \yii\web\UploadedFile - uploaded file instance, which content should be stored in GridFS
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* $record = new ImageFile();
|
||||
* $record->file = '/path/to/some/file.jpg';
|
||||
* $record->save();
|
||||
* ```
|
||||
*
|
||||
* You can also specify file content via [[newFileContent]] attribute:
|
||||
*
|
||||
* ```php
|
||||
* $record = new ImageFile();
|
||||
* $record->newFileContent = 'New file content';
|
||||
* $record->save();
|
||||
* ```
|
||||
*
|
||||
* Note: [[newFileContent]] always takes precedence over [[file]].
|
||||
*
|
||||
* @property null|string $fileContent File content. This property is read-only.
|
||||
* @property resource $fileResource File stream resource. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
|
||||
*/
|
||||
public static function find()
|
||||
{
|
||||
return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Mongo GridFS collection instance for this AR class.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public static function getCollection()
|
||||
{
|
||||
return static::getDb()->getFileCollection(static::collectionName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all attribute names of the model.
|
||||
* This method could be overridden by child classes to define available attributes.
|
||||
* Note: all attributes defined in base Active Record class should be always present
|
||||
* in returned array.
|
||||
* For example:
|
||||
*
|
||||
* ```php
|
||||
* public function attributes()
|
||||
* {
|
||||
* return array_merge(
|
||||
* parent::attributes(),
|
||||
* ['tags', 'status']
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @return array list of attribute names.
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
'_id',
|
||||
'filename',
|
||||
'uploadDate',
|
||||
'length',
|
||||
'chunkSize',
|
||||
'md5',
|
||||
'file',
|
||||
'newFileContent'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ActiveRecord::insert()
|
||||
*/
|
||||
protected function insertInternal($attributes = null)
|
||||
{
|
||||
if (!$this->beforeSave(true)) {
|
||||
return false;
|
||||
}
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
if (empty($values)) {
|
||||
$currentAttributes = $this->getAttributes();
|
||||
foreach ($this->primaryKey() as $key) {
|
||||
$values[$key] = isset($currentAttributes[$key]) ? $currentAttributes[$key] : null;
|
||||
}
|
||||
}
|
||||
$collection = static::getCollection();
|
||||
if (isset($values['newFileContent'])) {
|
||||
$newFileContent = $values['newFileContent'];
|
||||
unset($values['newFileContent']);
|
||||
}
|
||||
if (isset($values['file'])) {
|
||||
$newFile = $values['file'];
|
||||
unset($values['file']);
|
||||
}
|
||||
if (isset($newFileContent)) {
|
||||
$newId = $collection->insertFileContent($newFileContent, $values);
|
||||
} elseif (isset($newFile)) {
|
||||
$fileName = $this->extractFileName($newFile);
|
||||
$newId = $collection->insertFile($fileName, $values);
|
||||
} else {
|
||||
$newId = $collection->insert($values);
|
||||
}
|
||||
if ($newId !== null) {
|
||||
$this->setAttribute('_id', $newId);
|
||||
$values['_id'] = $newId;
|
||||
}
|
||||
|
||||
$changedAttributes = array_fill_keys(array_keys($values), null);
|
||||
$this->setOldAttributes($values);
|
||||
$this->afterSave(true, $changedAttributes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ActiveRecord::update()
|
||||
* @throws StaleObjectException
|
||||
*/
|
||||
protected function updateInternal($attributes = null)
|
||||
{
|
||||
if (!$this->beforeSave(false)) {
|
||||
return false;
|
||||
}
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
if (empty($values)) {
|
||||
$this->afterSave(false, $values);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$collection = static::getCollection();
|
||||
if (isset($values['newFileContent'])) {
|
||||
$newFileContent = $values['newFileContent'];
|
||||
unset($values['newFileContent']);
|
||||
}
|
||||
if (isset($values['file'])) {
|
||||
$newFile = $values['file'];
|
||||
unset($values['file']);
|
||||
}
|
||||
if (isset($newFileContent) || isset($newFile)) {
|
||||
$fileAssociatedAttributeNames = [
|
||||
'filename',
|
||||
'uploadDate',
|
||||
'length',
|
||||
'chunkSize',
|
||||
'md5',
|
||||
'file',
|
||||
'newFileContent'
|
||||
];
|
||||
$values = array_merge($this->getAttributes(null, $fileAssociatedAttributeNames), $values);
|
||||
$rows = $this->deleteInternal();
|
||||
$insertValues = $values;
|
||||
$insertValues['_id'] = $this->getAttribute('_id');
|
||||
if (isset($newFileContent)) {
|
||||
$collection->insertFileContent($newFileContent, $insertValues);
|
||||
} else {
|
||||
$fileName = $this->extractFileName($newFile);
|
||||
$collection->insertFile($fileName, $insertValues);
|
||||
}
|
||||
$this->setAttribute('newFileContent', null);
|
||||
$this->setAttribute('file', null);
|
||||
} 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.');
|
||||
}
|
||||
}
|
||||
|
||||
$changedAttributes = [];
|
||||
foreach ($values as $name => $value) {
|
||||
$changedAttributes[$name] = $this->getOldAttribute($name);
|
||||
$this->setOldAttribute($name, $value);
|
||||
}
|
||||
$this->afterSave(false, $changedAttributes);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts filename from given raw file value.
|
||||
* @param mixed $file raw file value.
|
||||
* @return string file name.
|
||||
* @throws \yii\base\InvalidParamException on invalid file value.
|
||||
*/
|
||||
protected function extractFileName($file)
|
||||
{
|
||||
if ($file instanceof UploadedFile) {
|
||||
return $file->tempName;
|
||||
} elseif (is_string($file)) {
|
||||
if (file_exists($file)) {
|
||||
return $file;
|
||||
}
|
||||
throw new InvalidParamException("File '{$file}' does not exist.");
|
||||
}
|
||||
|
||||
throw new InvalidParamException('Unsupported type of "file" attribute.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the [[file]] attribute from file collection, using current primary key.
|
||||
* @return \MongoGridFSFile|null refreshed file value.
|
||||
*/
|
||||
public function refreshFile()
|
||||
{
|
||||
$mongoFile = $this->getCollection()->get($this->getPrimaryKey());
|
||||
$this->setAttribute('file', $mongoFile);
|
||||
|
||||
return $mongoFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the associated file content.
|
||||
* @return null|string file content.
|
||||
* @throws \yii\base\InvalidParamException on invalid file attribute value.
|
||||
*/
|
||||
public function getFileContent()
|
||||
{
|
||||
$file = $this->getAttribute('file');
|
||||
if (empty($file) && !$this->getIsNewRecord()) {
|
||||
$file = $this->refreshFile();
|
||||
}
|
||||
|
||||
if (empty($file)) {
|
||||
return null;
|
||||
} elseif ($file instanceof Download) {
|
||||
$fileSize = $file->getSize();
|
||||
return empty($fileSize) ? null : $file->toString();
|
||||
} elseif ($file instanceof UploadedFile) {
|
||||
return file_get_contents($file->tempName);
|
||||
} elseif (is_string($file)) {
|
||||
if (file_exists($file)) {
|
||||
return file_get_contents($file);
|
||||
}
|
||||
throw new InvalidParamException("File '{$file}' does not exist.");
|
||||
}
|
||||
|
||||
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 bool 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 Download) {
|
||||
return ($file->toFile($filename) == $file->getSize());
|
||||
} elseif ($file instanceof UploadedFile) {
|
||||
return copy($file->tempName, $filename);
|
||||
} elseif (is_string($file)) {
|
||||
if (file_exists($file)) {
|
||||
return copy($file, $filename);
|
||||
}
|
||||
throw new InvalidParamException("File '{$file}' does not exist.");
|
||||
}
|
||||
|
||||
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 Download) {
|
||||
return $file->getResource();
|
||||
} elseif ($file instanceof UploadedFile) {
|
||||
return fopen($file->tempName, 'r');
|
||||
} elseif (is_string($file)) {
|
||||
if (file_exists($file)) {
|
||||
return fopen($file, 'r');
|
||||
}
|
||||
throw new InvalidParamException("File '{$file}' does not exist.");
|
||||
}
|
||||
|
||||
throw new InvalidParamException('Unsupported type of "file" attribute.');
|
||||
}
|
||||
}
|
||||
327
vendor/yiisoft/yii2-mongodb/src/file/Collection.php
vendored
Normal file
327
vendor/yiisoft/yii2-mongodb/src/file/Collection.php
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use yii\mongodb\Exception;
|
||||
use Yii;
|
||||
use yii\web\UploadedFile;
|
||||
|
||||
/**
|
||||
* Collection represents the Mongo GridFS collection information.
|
||||
*
|
||||
* A file collection object is usually created by calling [[Database::getFileCollection()]] or [[Connection::getFileCollection()]].
|
||||
*
|
||||
* File collection inherits all interface from regular [[\yii\mongo\Collection]], adding methods to store files.
|
||||
*
|
||||
* @property \yii\mongodb\Collection $chunkCollection Mongo collection instance. This property is read-only.
|
||||
* @property \yii\mongodb\Collection $fileCollection Mongo collection instance. This property is read-only.
|
||||
* @property string $prefix Prefix of this file collection.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Collection extends \yii\mongodb\Collection
|
||||
{
|
||||
/**
|
||||
* @var \yii\mongodb\Database MongoDB database instance.
|
||||
*/
|
||||
public $database;
|
||||
|
||||
/**
|
||||
* @var string prefix of this file collection.
|
||||
*/
|
||||
private $_prefix;
|
||||
/**
|
||||
* @var \yii\mongodb\Collection file chunks MongoDB collection.
|
||||
*/
|
||||
private $_chunkCollection;
|
||||
/**
|
||||
* @var \yii\mongodb\Collection files MongoDB collection.
|
||||
*/
|
||||
private $_fileCollection;
|
||||
/**
|
||||
* @var bool whether file related fields indexes are ensured for this collection.
|
||||
*/
|
||||
private $indexesEnsured = false;
|
||||
|
||||
|
||||
/**
|
||||
* @return string prefix of this file collection.
|
||||
*/
|
||||
public function getPrefix()
|
||||
{
|
||||
return $this->_prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $prefix prefix of this file collection.
|
||||
*/
|
||||
public function setPrefix($prefix)
|
||||
{
|
||||
$this->_prefix = $prefix;
|
||||
$this->name = $prefix . '.files';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates upload command.
|
||||
* @param array $options upload options.
|
||||
* @return Upload file upload instance.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createUpload($options = [])
|
||||
{
|
||||
$config = $options;
|
||||
$config['collection'] = $this;
|
||||
return new Upload($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates download command.
|
||||
* @param array|ObjectID $document file document ot be downloaded.
|
||||
* @return Download file download instance.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function createDownload($document)
|
||||
{
|
||||
return new Download([
|
||||
'collection' => $this,
|
||||
'document' => $document,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MongoDB collection for the file chunks.
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return \yii\mongodb\Collection mongo collection instance.
|
||||
*/
|
||||
public function getChunkCollection($refresh = false)
|
||||
{
|
||||
if ($refresh || !is_object($this->_chunkCollection)) {
|
||||
$this->_chunkCollection = Yii::createObject([
|
||||
'class' => 'yii\mongodb\Collection',
|
||||
'database' => $this->database,
|
||||
'name' => $this->getPrefix() . '.chunks'
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->_chunkCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MongoDB collection for the files.
|
||||
* @param bool $refresh whether to reload the collection instance even if it is found in the cache.
|
||||
* @return \yii\mongodb\Collection mongo collection instance.
|
||||
* @since 2.1
|
||||
*/
|
||||
public function getFileCollection($refresh = false)
|
||||
{
|
||||
if ($refresh || !is_object($this->_fileCollection)) {
|
||||
$this->_fileCollection = Yii::createObject([
|
||||
'class' => 'yii\mongodb\Collection',
|
||||
'database' => $this->database,
|
||||
'name' => $this->name
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->_fileCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function drop()
|
||||
{
|
||||
return parent::drop() && $this->database->dropCollection($this->getChunkCollection()->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return Cursor cursor for the search results
|
||||
*/
|
||||
public function find($condition = [], $fields = [], $options = [])
|
||||
{
|
||||
return new Cursor($this, parent::find($condition, $fields, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($condition = [], $options = [])
|
||||
{
|
||||
$fileCollection = $this->getFileCollection();
|
||||
$chunkCollection = $this->getChunkCollection();
|
||||
|
||||
if (empty($condition) && empty($options['limit'])) {
|
||||
// truncate :
|
||||
$deleteCount = $fileCollection->remove([], $options);
|
||||
$chunkCollection->remove([], $options);
|
||||
return $deleteCount;
|
||||
}
|
||||
|
||||
$batchSize = 200;
|
||||
$options['batchSize'] = $batchSize;
|
||||
$cursor = $fileCollection->find($condition, ['_id'], $options);
|
||||
unset($options['limit']);
|
||||
$deleteCount = 0;
|
||||
$deleteCallback = function ($ids) use ($fileCollection, $chunkCollection, $options) {
|
||||
$chunkCollection->remove(['files_id' => ['$in' => $ids]], $options);
|
||||
return $fileCollection->remove(['_id' => ['$in' => $ids]], $options);
|
||||
};
|
||||
|
||||
$ids = [];
|
||||
$idsCount = 0;
|
||||
foreach ($cursor as $row) {
|
||||
$ids[] = $row['_id'];
|
||||
$idsCount++;
|
||||
if ($idsCount >= $batchSize) {
|
||||
$deleteCount += $deleteCallback($ids);
|
||||
$ids = [];
|
||||
$idsCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($ids)) {
|
||||
$deleteCount += $deleteCallback($ids);
|
||||
}
|
||||
|
||||
return $deleteCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new file in GridFS collection from given local filesystem file.
|
||||
* Additional attributes can be added file document using $metadata.
|
||||
* @param string $filename name of the file to store.
|
||||
* @param array $metadata other metadata fields to include in the file document.
|
||||
* @param array $options list of options in format: optionName => optionValue
|
||||
* @return mixed the "_id" of the saved file document. This will be a generated [[\MongoId]]
|
||||
* unless an "_id" was explicitly specified in the metadata.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function insertFile($filename, $metadata = [], $options = [])
|
||||
{
|
||||
$options['document'] = $metadata;
|
||||
$document = $this->createUpload($options)->addFile($filename)->complete();
|
||||
return $document['_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new file in GridFS collection with specified content.
|
||||
* Additional attributes can be added file document using $metadata.
|
||||
* @param string $bytes string of bytes to store.
|
||||
* @param array $metadata other metadata fields to include in the file document.
|
||||
* @param array $options list of options in format: optionName => optionValue
|
||||
* @return mixed the "_id" of the saved file document. This will be a generated [[\MongoId]]
|
||||
* unless an "_id" was explicitly specified in the metadata.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function insertFileContent($bytes, $metadata = [], $options = [])
|
||||
{
|
||||
$options['document'] = $metadata;
|
||||
$document = $this->createUpload($options)->addContent($bytes)->complete();
|
||||
return $document['_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new file in GridFS collection from uploaded file.
|
||||
* Additional attributes can be added file document using $metadata.
|
||||
* @param string $name name of the uploaded file to store. This should correspond to
|
||||
* the file field's name attribute in the HTML form.
|
||||
* @param array $metadata other metadata fields to include in the file document.
|
||||
* @param array $options list of options in format: optionName => optionValue
|
||||
* @return mixed the "_id" of the saved file document. This will be a generated [[\MongoId]]
|
||||
* unless an "_id" was explicitly specified in the metadata.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function insertUploads($name, $metadata = [], $options = [])
|
||||
{
|
||||
$uploadedFile = UploadedFile::getInstanceByName($name);
|
||||
if ($uploadedFile === null) {
|
||||
throw new Exception("Uploaded file '{$name}' does not exist.");
|
||||
}
|
||||
|
||||
$options['filename'] = $uploadedFile->name;
|
||||
$options['document'] = $metadata;
|
||||
$document = $this->createUpload($options)->addFile($uploadedFile->tempName)->complete();
|
||||
return $document['_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the file with given _id.
|
||||
* @param mixed $id _id of the file to find.
|
||||
* @return Download|null found file, or null if file does not exist
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
$document = $this->getFileCollection()->findOne(['_id' => $id]);
|
||||
return empty($document) ? null : $this->createDownload($document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the file with given _id.
|
||||
* @param mixed $id _id of the file to find.
|
||||
* @return bool whether the operation was successful.
|
||||
* @throws Exception on failure.
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$this->remove(['_id' => $id], ['limit' => 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that indexes, which are crucial for the file processing,
|
||||
* exist at this collection and [[chunkCollection]].
|
||||
* The check result is cached per collection instance.
|
||||
* @param bool $force whether to ignore internal collection instance cache.
|
||||
* @return $this self reference.
|
||||
*/
|
||||
public function ensureIndexes($force = false)
|
||||
{
|
||||
if (!$force && $this->indexesEnsured) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->ensureFileIndexes();
|
||||
$this->ensureChunkIndexes();
|
||||
|
||||
$this->indexesEnsured = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures indexes at file collection.
|
||||
*/
|
||||
private function ensureFileIndexes()
|
||||
{
|
||||
$indexKey = ['filename' => 1, 'uploadDate' => 1];
|
||||
foreach ($this->listIndexes() as $index) {
|
||||
if ($index['key'] === $indexKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createIndex($indexKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures indexes at chunk collection.
|
||||
*/
|
||||
private function ensureChunkIndexes()
|
||||
{
|
||||
$chunkCollection = $this->getChunkCollection();
|
||||
$indexKey = ['files_id' => 1, 'n' => 1];
|
||||
foreach ($chunkCollection->listIndexes() as $index) {
|
||||
if (!empty($index['unique']) && $index['key'] === $indexKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$chunkCollection->createIndex($indexKey, ['unique' => true]);
|
||||
}
|
||||
}
|
||||
149
vendor/yiisoft/yii2-mongodb/src/file/Cursor.php
vendored
Normal file
149
vendor/yiisoft/yii2-mongodb/src/file/Cursor.php
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
/**
|
||||
* Cursor is a wrapper around [[\MongoDB\Driver\Cursor]], which allows returning of the
|
||||
* record with [[Download]] instance attached.
|
||||
*
|
||||
* @method \MongoDB\Driver\Cursor getInnerIterator()
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class Cursor extends \IteratorIterator implements \Countable
|
||||
{
|
||||
/**
|
||||
* @var Collection related GridFS collection instance.
|
||||
*/
|
||||
public $collection;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param Collection $collection
|
||||
* @param \MongoDB\Driver\Cursor $cursor
|
||||
*/
|
||||
public function __construct($collection, $cursor)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
parent::__construct($cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* This method is required by the interface [[\Iterator]].
|
||||
* @return mixed current row
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$value = parent::current();
|
||||
if (!isset($value['file'])) {
|
||||
$value['file'] = $this->collection->createDownload(array_intersect_key($value, ['_id' => true, 'filename' => true, 'length' => true, 'chunkSize' => true]));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of this cursor.
|
||||
* This method is required by the interface [[\Countable]].
|
||||
* @return int elements count.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->cursor);
|
||||
}
|
||||
|
||||
// Mock up original cursor interface :
|
||||
|
||||
/**
|
||||
* Returns an array containing all results for this cursor
|
||||
* @return array containing all results for this cursor.
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this as $key => $value) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID for this cursor.
|
||||
* @return \MongoDB\Driver\CursorId cursor ID.
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->getInnerIterator()->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a type map to use for BSON unserialization.
|
||||
* @param array $typemap type map.
|
||||
*/
|
||||
public function setTypeMap($typemap)
|
||||
{
|
||||
$this->getInnerIterator()->setTypeMap($typemap);
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP magic method, which is invoked on attempt of invocation not existing method.
|
||||
* It redirects method call to inner iterator.
|
||||
* @param string $name method name.
|
||||
* @param array $arguments method arguments
|
||||
* @return mixed method result.
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return call_user_func_array([$this->getInnerIterator(), $name], $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP magic method, which is invoked on attempt of setting not existing property.
|
||||
* It passes value to the inner iterator.
|
||||
* @param string $name field name.
|
||||
* @param mixed $value field value.
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->getInnerIterator()->{$name} = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP magic method, which is invoked on attempt of getting not existing property.
|
||||
* It returns value from the inner iterator.
|
||||
* @param string $name field name.
|
||||
* @return mixed field value.
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->getInnerIterator()->{$name};
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP magic method, which is invoked on attempt of checking if a property is set.
|
||||
* @param string $name field name.
|
||||
* @return bool whether field exists or not.
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
$cursor = $this->getInnerIterator();
|
||||
return isset($cursor->$name);
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP magic method, which is invoked on attempt of unsetting of property.
|
||||
* @param string $name field name.
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
$cursor = $this->getInnerIterator();
|
||||
unset($cursor->$name);
|
||||
}
|
||||
}
|
||||
319
vendor/yiisoft/yii2-mongodb/src/file/Download.php
vendored
Normal file
319
vendor/yiisoft/yii2-mongodb/src/file/Download.php
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\BaseObject;
|
||||
use yii\helpers\FileHelper;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* Download represents the GridFS download operation.
|
||||
*
|
||||
* A `Download` object is usually created by calling [[Collection::get()]] or [[Collection::createDownload()]].
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* Yii::$app->mongodb->getFileCollection()->createDownload($document['_id'])->toFile('/path/to/file.dat');
|
||||
* ```
|
||||
*
|
||||
* You can use `Download::substr()` to read a specific part of the file:
|
||||
*
|
||||
* ```php
|
||||
* $filePart = Yii::$app->mongodb->getFileCollection()->createDownload($document['_id'])->substr(256, 1024);
|
||||
* ```
|
||||
*
|
||||
* @property string $bytes File content. This property is read-only.
|
||||
* @property \MongoDB\Driver\Cursor $chunkCursor Chuck list cursor. This property is read-only.
|
||||
* @property \Iterator $chunkIterator Chuck cursor iterator. This property is read-only.
|
||||
* @property array $document Document to be downloaded. Note that the type of this property differs in getter
|
||||
* and setter. See [[getDocument()]] and [[setDocument()]] for details.
|
||||
* @property string|null $filename File name. This property is read-only.
|
||||
* @property resource $resource File stream resource. This property is read-only.
|
||||
* @property int $size File size. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class Download extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Collection file collection to be used.
|
||||
*/
|
||||
public $collection;
|
||||
|
||||
/**
|
||||
* @var array|ObjectID document to be downloaded.
|
||||
*/
|
||||
private $_document;
|
||||
/**
|
||||
* @var \MongoDB\Driver\Cursor cursor for the file chunks.
|
||||
*/
|
||||
private $_chunkCursor;
|
||||
/**
|
||||
* @var \Iterator iterator for [[chunkCursor]].
|
||||
*/
|
||||
private $_chunkIterator;
|
||||
/**
|
||||
* @var resource|null
|
||||
*/
|
||||
private $_resource;
|
||||
|
||||
|
||||
/**
|
||||
* @return array document to be downloaded.
|
||||
* @throws InvalidConfigException on invalid document configuration.
|
||||
*/
|
||||
public function getDocument()
|
||||
{
|
||||
if (!is_array($this->_document)) {
|
||||
if (is_scalar($this->_document) || $this->_document instanceof ObjectID) {
|
||||
$document = $this->collection->findOne(['_id' => $this->_document]);
|
||||
if (empty($document)) {
|
||||
throw new InvalidConfigException('Document id=' . $this->_document . ' does not exist at collection "' . $this->collection->getFullName() . '"');
|
||||
}
|
||||
$this->_document = $document;
|
||||
} else {
|
||||
$this->_document = (array)$this->_document;
|
||||
}
|
||||
}
|
||||
return $this->_document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data of the document to be downloaded.
|
||||
* Document can be specified by its ID, in this case its data will be fetched automatically
|
||||
* via extra query.
|
||||
* @param array|ObjectID $document document raw data or document ID.
|
||||
*/
|
||||
public function setDocument($document)
|
||||
{
|
||||
$this->_document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the associated file.
|
||||
* @return int file size.
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
$document = $this->getDocument();
|
||||
return isset($document['length']) ? $document['length'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns associated file's filename.
|
||||
* @return string|null file name.
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
$document = $this->getDocument();
|
||||
return isset($document['filename']) ? $document['filename'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns file chunks read cursor.
|
||||
* @param bool $refresh whether to recreate cursor, if it is already exist.
|
||||
* @return \MongoDB\Driver\Cursor chuck list cursor.
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function getChunkCursor($refresh = false)
|
||||
{
|
||||
if ($refresh || $this->_chunkCursor === null) {
|
||||
$file = $this->getDocument();
|
||||
$this->_chunkCursor = $this->collection->getChunkCollection()->find(
|
||||
['files_id' => $file['_id']],
|
||||
[],
|
||||
['sort' => ['n' => 1]]
|
||||
);
|
||||
}
|
||||
return $this->_chunkCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns iterator for the file chunks cursor.
|
||||
* @param bool $refresh whether to recreate iterator, if it is already exist.
|
||||
* @return \Iterator chuck cursor iterator.
|
||||
*/
|
||||
public function getChunkIterator($refresh = false)
|
||||
{
|
||||
if ($refresh || $this->_chunkIterator === null) {
|
||||
$this->_chunkIterator = new \IteratorIterator($this->getChunkCursor($refresh));
|
||||
$this->_chunkIterator->rewind();
|
||||
}
|
||||
return $this->_chunkIterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves file into the given stream.
|
||||
* @param resource $stream stream, which file should be saved to.
|
||||
* @return int number of written bytes.
|
||||
*/
|
||||
public function toStream($stream)
|
||||
{
|
||||
$bytesWritten = 0;
|
||||
foreach ($this->getChunkCursor() as $chunk) {
|
||||
$bytesWritten += fwrite($stream, $chunk['data']->getData());
|
||||
}
|
||||
return $bytesWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves download to the physical file.
|
||||
* @param string $filename name of the physical file.
|
||||
* @return int number of written bytes.
|
||||
*/
|
||||
public function toFile($filename)
|
||||
{
|
||||
$filename = Yii::getAlias($filename);
|
||||
FileHelper::createDirectory(dirname($filename));
|
||||
return $this->toStream(fopen($filename, 'w+'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of the bytes in the associated file.
|
||||
* @return string file content.
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
$result = '';
|
||||
foreach ($this->getChunkCursor() as $chunk) {
|
||||
$result .= $chunk['data']->getData();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an opened stream resource, which can be used to read file.
|
||||
* Note: each invocation of this method will create new file resource.
|
||||
* @return resource stream resource.
|
||||
*/
|
||||
public function toResource()
|
||||
{
|
||||
$protocol = $this->collection->database->connection->registerFileStreamWrapper();
|
||||
|
||||
$context = stream_context_create([
|
||||
$protocol => [
|
||||
'download' => $this,
|
||||
]
|
||||
]);
|
||||
|
||||
$document = $this->getDocument();
|
||||
$url = "{$protocol}://{$this->collection->database->name}.{$this->collection->prefix}?_id={$document['_id']}";
|
||||
return fopen($url, 'r', false, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return part of a file.
|
||||
* @param int $start reading start position.
|
||||
* If non-negative, the returned string will start at the start'th position in file, counting from zero.
|
||||
* If negative, the returned string will start at the start'th character from the end of file.
|
||||
* @param int $length number of bytes to read.
|
||||
* If given and is positive, the string returned will contain at most length characters beginning from start (depending on the length of file).
|
||||
* If given and is negative, then that many characters will be omitted from the end of file (after the start position has been calculated when a start is negative).
|
||||
* @return string|false the extracted part of file or `false` on failure
|
||||
*/
|
||||
public function substr($start, $length)
|
||||
{
|
||||
$document = $this->getDocument();
|
||||
|
||||
if ($start < 0) {
|
||||
$start = max($document['length'] + $start, 0);
|
||||
}
|
||||
|
||||
if ($start > $document['length']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($length < 0) {
|
||||
$length = $document['length'] - $start + $length;
|
||||
if ($length < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$chunkSize = $document['chunkSize'];
|
||||
|
||||
$startChunkNumber = floor($start / $chunkSize);
|
||||
|
||||
$chunkIterator = $this->getChunkIterator();
|
||||
|
||||
if (!$chunkIterator->valid()) {
|
||||
// invalid iterator state - recreate iterator
|
||||
// unable to use `rewind` due to error "Cursors cannot rewind after starting iteration"
|
||||
$chunkIterator = $this->getChunkIterator(true);
|
||||
}
|
||||
|
||||
if ($chunkIterator->key() > $startChunkNumber) {
|
||||
// unable to go back by iterator
|
||||
// unable to use `rewind` due to error "Cursors cannot rewind after starting iteration"
|
||||
$chunkIterator = $this->getChunkIterator(true);
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
$chunkDataOffset = $start - $startChunkNumber * $chunkSize;
|
||||
while ($chunkIterator->valid()) {
|
||||
if ($chunkIterator->key() >= $startChunkNumber) {
|
||||
$chunk = $chunkIterator->current();
|
||||
$data = $chunk['data']->getData();
|
||||
|
||||
$readLength = min($chunkSize - $chunkDataOffset, $length);
|
||||
|
||||
$result .= StringHelper::byteSubstr($data, $chunkDataOffset, $readLength);
|
||||
|
||||
$length -= $readLength;
|
||||
if ($length <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$chunkDataOffset = 0;
|
||||
}
|
||||
|
||||
$chunkIterator->next();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Compatibility with `MongoGridFSFile` :
|
||||
|
||||
/**
|
||||
* Alias of [[toString()]] method.
|
||||
* @return string file content.
|
||||
*/
|
||||
public function getBytes()
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of [[toFile()]] method.
|
||||
* @param string $filename name of the physical file.
|
||||
* @return int number of written bytes.
|
||||
*/
|
||||
public function write($filename)
|
||||
{
|
||||
return $this->toFile($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns persistent stream resource, which can be used to read file.
|
||||
* @return resource file stream resource.
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
if ($this->_resource === null) {
|
||||
$this->_resource = $this->toResource();
|
||||
}
|
||||
return $this->_resource;
|
||||
}
|
||||
}
|
||||
39
vendor/yiisoft/yii2-mongodb/src/file/Query.php
vendored
Normal file
39
vendor/yiisoft/yii2-mongodb/src/file/Query.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* Query represents Mongo "find" operation for GridFS collection.
|
||||
*
|
||||
* Query behaves exactly as regular [[\yii\mongodb\Query]].
|
||||
* Found files will be represented as arrays of file document attributes with
|
||||
* additional 'file' key, which stores [[\MongoGridFSFile]] instance.
|
||||
*
|
||||
* @property Collection $collection Collection instance. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Query extends \yii\mongodb\Query
|
||||
{
|
||||
/**
|
||||
* Returns the Mongo collection for this query.
|
||||
* @param \yii\mongodb\Connection $db Mongo connection.
|
||||
* @return Collection collection instance.
|
||||
*/
|
||||
public function getCollection($db = null)
|
||||
{
|
||||
if ($db === null) {
|
||||
$db = Yii::$app->get('mongodb');
|
||||
}
|
||||
|
||||
return $db->getFileCollection($this->from);
|
||||
}
|
||||
}
|
||||
415
vendor/yiisoft/yii2-mongodb/src/file/StreamWrapper.php
vendored
Normal file
415
vendor/yiisoft/yii2-mongodb/src/file/StreamWrapper.php
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\BaseObject;
|
||||
use yii\di\Instance;
|
||||
use yii\helpers\StringHelper;
|
||||
use yii\mongodb\Connection;
|
||||
|
||||
/**
|
||||
* StreamWrapper provides stream wrapper for MongoDB GridFS, allowing file operations via
|
||||
* regular PHP stream resources.
|
||||
*
|
||||
* Before feature can be used this wrapper should be registered via [[register()]] method.
|
||||
* It is usually performed via [[yii\mongodb\Connection::registerFileStreamWrapper()]].
|
||||
*
|
||||
* Note: do not use this class directly - its instance will be created and maintained by PHP internally
|
||||
* once corresponding stream resource is created.
|
||||
*
|
||||
* Resource path should be specified in following format:
|
||||
*
|
||||
* ```
|
||||
* 'protocol://databaseName.fileCollectionPrefix?file_attribute=value'
|
||||
* ```
|
||||
*
|
||||
* Write example:
|
||||
*
|
||||
* ```php
|
||||
* $resource = fopen('gridfs://mydatabase.fs?filename=new_file.txt', 'w');
|
||||
* fwrite($resource, 'some content');
|
||||
* // ...
|
||||
* fclose($resource);
|
||||
* ```
|
||||
*
|
||||
* Read example:
|
||||
*
|
||||
* ```php
|
||||
* $resource = fopen('gridfs://mydatabase.fs?filename=my_file.txt', 'r');
|
||||
* $fileContent = stream_get_contents($resource);
|
||||
* ```
|
||||
*
|
||||
* @see http://php.net/manual/en/function.stream-wrapper-register.php
|
||||
*
|
||||
* @property array $contextOptions Context options. This property is read-only.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class StreamWrapper extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var resource associated stream resource context.
|
||||
* This property is set automatically by PHP once wrapper is instantiated.
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* @var array context options associated with [[context]].
|
||||
*/
|
||||
private $_contextOptions;
|
||||
/**
|
||||
* @var string protocol associated with stream
|
||||
*/
|
||||
private $_protocol;
|
||||
/**
|
||||
* @var string namespace in format 'databaseName.collectionName' associated with stream.
|
||||
*/
|
||||
private $_namespace;
|
||||
/**
|
||||
* @var array query parameters passed for the stream.
|
||||
*/
|
||||
private $_queryParams = [];
|
||||
/**
|
||||
* @var Upload file upload instance
|
||||
*/
|
||||
private $_upload;
|
||||
/**
|
||||
* @var Download file upload instance
|
||||
*/
|
||||
private $_download;
|
||||
/**
|
||||
* @var int file pointer offset.
|
||||
*/
|
||||
private $_pointerOffset = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Registers this steam wrapper.
|
||||
* @param string $protocol name of the protocol to be used.
|
||||
* @param bool $force whether to register wrapper, even if protocol is already taken.
|
||||
*/
|
||||
public static function register($protocol = 'gridfs', $force = false)
|
||||
{
|
||||
if (in_array($protocol, stream_get_wrappers())) {
|
||||
if (!$force) {
|
||||
return;
|
||||
}
|
||||
stream_wrapper_unregister($protocol);
|
||||
}
|
||||
|
||||
stream_wrapper_register($protocol, get_called_class(), STREAM_IS_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns options associated with [[context]].
|
||||
* @return array context options.
|
||||
*/
|
||||
public function getContextOptions()
|
||||
{
|
||||
if ($this->_contextOptions === null) {
|
||||
$this->_contextOptions = stream_context_get_options($this->context);
|
||||
}
|
||||
return $this->_contextOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses stream open path, initializes internal parameters.
|
||||
* @param string $path stream open path.
|
||||
*/
|
||||
private function parsePath($path)
|
||||
{
|
||||
$pathInfo = parse_url($path);
|
||||
|
||||
$this->_protocol = $pathInfo['scheme'];
|
||||
$this->_namespace = $pathInfo['host'];
|
||||
parse_str($pathInfo['query'], $this->_queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares [[Download]] instance for the read operations.
|
||||
* @return bool success.
|
||||
* @throws InvalidConfigException on invalid context configuration.
|
||||
*/
|
||||
private function prepareDownload()
|
||||
{
|
||||
$contextOptions = $this->getContextOptions();
|
||||
if (isset($contextOptions[$this->_protocol]['download'])) {
|
||||
$download = $contextOptions[$this->_protocol]['download'];
|
||||
if (!$download instanceof Download) {
|
||||
throw new InvalidConfigException('"download" context option should be an instance of "' . Download::className() . '"');
|
||||
}
|
||||
$this->_download = $download;
|
||||
return true;
|
||||
}
|
||||
|
||||
$collection = $this->fetchCollection();
|
||||
if (empty($this->_queryParams)) {
|
||||
return false;
|
||||
}
|
||||
$file = $collection->findOne($this->_queryParams);
|
||||
if (empty($file)) {
|
||||
throw new InvalidConfigException('Requested file does not exits.');
|
||||
}
|
||||
|
||||
$this->_download = $file['file'];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares [[Upload]] instance for the write operations.
|
||||
* @return bool success.
|
||||
* @throws InvalidConfigException on invalid context configuration.
|
||||
*/
|
||||
private function prepareUpload()
|
||||
{
|
||||
$contextOptions = $this->getContextOptions();
|
||||
if (isset($contextOptions[$this->_protocol]['upload'])) {
|
||||
$upload = $contextOptions[$this->_protocol]['upload'];
|
||||
if (!$upload instanceof Upload) {
|
||||
throw new InvalidConfigException('"upload" context option should be an instance of "' . Upload::className() . '"');
|
||||
}
|
||||
$this->_upload = $upload;
|
||||
return true;
|
||||
}
|
||||
|
||||
$collection = $this->fetchCollection();
|
||||
$this->_upload = $collection->createUpload(['document' => $this->_queryParams]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches associated file collection from stream options.
|
||||
* @return Collection file collection instance.
|
||||
* @throws InvalidConfigException on invalid stream options.
|
||||
*/
|
||||
private function fetchCollection()
|
||||
{
|
||||
$contextOptions = $this->getContextOptions();
|
||||
|
||||
if (isset($contextOptions[$this->_protocol]['collection'])) {
|
||||
$collection = $contextOptions[$this->_protocol]['collection'];
|
||||
if ($collection instanceof Collection) {
|
||||
throw new InvalidConfigException('"collection" context option should be an instance of "' . Collection::className() . '"');
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
$connection = isset($contextOptions[$this->_protocol]['db'])
|
||||
? $contextOptions[$this->_protocol]['db']
|
||||
: 'mongodb';
|
||||
|
||||
/* @var $connection Connection */
|
||||
$connection = Instance::ensure($connection, Connection::className());
|
||||
|
||||
list($databaseName, $collectionPrefix) = explode('.', $this->_namespace, 2);
|
||||
return $connection->getDatabase($databaseName)->getFileCollection($collectionPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default template for file statistic data set.
|
||||
* @see stat()
|
||||
* @return array statistic information.
|
||||
*/
|
||||
private function fileStatisticsTemplate()
|
||||
{
|
||||
return [
|
||||
0 => 0, 'dev' => 0,
|
||||
1 => 0, 'ino' => 0,
|
||||
2 => 0, 'mode' => 0,
|
||||
3 => 0, 'nlink' => 0,
|
||||
4 => 0, 'uid' => 0,
|
||||
5 => 0, 'gid' => 0,
|
||||
6 => -1, 'rdev' => -1,
|
||||
7 => 0, 'size' => 0,
|
||||
8 => 0, 'atime' => 0,
|
||||
9 => 0, 'mtime' => 0,
|
||||
10 => 0, 'ctime' => 0,
|
||||
11 => -1, 'blksize' => -1,
|
||||
12 => -1, 'blocks' => -1,
|
||||
];
|
||||
}
|
||||
|
||||
// Stream Interface :
|
||||
|
||||
/**
|
||||
* Closes a resource.
|
||||
* This method is called in response to `fclose()`.
|
||||
* @see fclose()
|
||||
*/
|
||||
public function stream_close()
|
||||
{
|
||||
if ($this->_upload !== null) {
|
||||
$this->_upload->complete();
|
||||
$this->_upload = null;
|
||||
}
|
||||
if ($this->_download !== null) {
|
||||
$this->_download = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for end-of-file on a file pointer.
|
||||
* This method is called in response to `feof()`.
|
||||
* @see feof()
|
||||
* @return bool `true` if the read/write position is at the end of the stream and
|
||||
* if no more data is available to be read, or `false` otherwise.
|
||||
*/
|
||||
public function stream_eof()
|
||||
{
|
||||
return $this->_download !== null
|
||||
? ($this->_pointerOffset >= $this->_download->getSize())
|
||||
: true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens file.
|
||||
* This method is called immediately after the wrapper is initialized (f.e. by `fopen()` and `file_get_contents()`).
|
||||
* @see fopen()
|
||||
* @param string $path specifies the URL that was passed to the original function.
|
||||
* @param string $mode mode used to open the file, as detailed for `fopen()`.
|
||||
* @param int $options additional flags set by the streams API.
|
||||
* @param string $openedPath real opened path.
|
||||
* @return bool whether operation is successful.
|
||||
*/
|
||||
public function stream_open($path, $mode, $options, &$openedPath)
|
||||
{
|
||||
if ($options & STREAM_USE_PATH) {
|
||||
$openedPath = $path;
|
||||
}
|
||||
|
||||
$this->parsePath($path);
|
||||
|
||||
switch ($mode) {
|
||||
case 'r':
|
||||
return $this->prepareDownload();
|
||||
case 'w':
|
||||
return $this->prepareUpload();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from stream.
|
||||
* This method is called in response to `fread()` and `fgets()`.
|
||||
* @see fread()
|
||||
* @param int $count count of bytes of data from the current position should be returned.
|
||||
* @return string|false if there are less than count bytes available, return as many as are available.
|
||||
* If no more data is available, return `false`.
|
||||
*/
|
||||
public function stream_read($count)
|
||||
{
|
||||
if ($this->_download === null) {
|
||||
return false;
|
||||
}
|
||||
$result = $this->_download->substr($this->_pointerOffset, $count);
|
||||
$this->_pointerOffset += $count;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to stream.
|
||||
* This method is called in response to `fwrite()`.
|
||||
* @see fwrite()
|
||||
* @param string $data string to be stored into the underlying stream.
|
||||
* @return int the number of bytes that were successfully stored.
|
||||
*/
|
||||
public function stream_write($data)
|
||||
{
|
||||
if ($this->_upload === null) {
|
||||
return false;
|
||||
}
|
||||
$this->_upload->addContent($data);
|
||||
$result = StringHelper::byteLength($data);
|
||||
$this->_pointerOffset += $result;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called in response to `fflush()` and when the stream is being closed
|
||||
* while any unflushed data has been written to it before.
|
||||
* @see fflush()
|
||||
* @return bool whether cached data was successfully stored.
|
||||
*/
|
||||
public function stream_flush()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about a file resource.
|
||||
* This method is called in response to `stat()`.
|
||||
* @see stat()
|
||||
* @return array file statistic information.
|
||||
*/
|
||||
public function stream_stat()
|
||||
{
|
||||
$statistics = $this->fileStatisticsTemplate();
|
||||
|
||||
if ($this->_download !== null) {
|
||||
$statistics[7] = $statistics['size'] = $this->_download->getSize();
|
||||
}
|
||||
if ($this->_upload !== null) {
|
||||
$statistics[7] = $statistics['size'] = $this->_pointerOffset;
|
||||
}
|
||||
|
||||
return $statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to specific location in a stream.
|
||||
* This method is called in response to `fseek()`.
|
||||
* @see fseek()
|
||||
* @param int $offset The stream offset to seek to.
|
||||
* @param int $whence
|
||||
* Possible values:
|
||||
*
|
||||
* - SEEK_SET - Set position equal to offset bytes.
|
||||
* - SEEK_CUR - Set position to current location plus offset.
|
||||
* - SEEK_END - Set position to end-of-file plus offset.
|
||||
*
|
||||
* @return bool Return true if the position was updated, false otherwise.
|
||||
*/
|
||||
public function stream_seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
switch ($whence) {
|
||||
case SEEK_SET:
|
||||
if ($offset < $this->_download->getSize() && $offset >= 0) {
|
||||
$this->_pointerOffset = $offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case SEEK_CUR:
|
||||
if ($offset >= 0) {
|
||||
$this->_pointerOffset += $offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case SEEK_END:
|
||||
if ($this->_download->getSize() + $offset >= 0) {
|
||||
$this->_pointerOffset = $this->_download->getSize() + $offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current position of a stream.
|
||||
* This method is called in response to `fseek()` to determine the current position.
|
||||
* @see fseek()
|
||||
* @return int Should return the current position of the stream.
|
||||
*/
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->_pointerOffset;
|
||||
}
|
||||
}
|
||||
280
vendor/yiisoft/yii2-mongodb/src/file/Upload.php
vendored
Normal file
280
vendor/yiisoft/yii2-mongodb/src/file/Upload.php
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\file;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use MongoDB\BSON\UTCDatetime;
|
||||
use MongoDB\Driver\Exception\InvalidArgumentException;
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\base\BaseObject;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* Upload represents the GridFS upload operation.
|
||||
*
|
||||
* An `Upload` object is usually created by calling [[Collection::createUpload()]].
|
||||
*
|
||||
* Note: instance of this class is 'single use' only. Do not attempt to use same `Upload` instance for
|
||||
* multiple file upload.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* $document = Yii::$app->mongodb->getFileCollection()->createUpload()
|
||||
* ->addContent('Part 1')
|
||||
* ->addContent('Part 2')
|
||||
* // ...
|
||||
* ->complete();
|
||||
* ```
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.1
|
||||
*/
|
||||
class Upload extends BaseObject
|
||||
{
|
||||
/**
|
||||
* @var Collection file collection to be used.
|
||||
*/
|
||||
public $collection;
|
||||
/**
|
||||
* @var string filename to be used for file storage.
|
||||
*/
|
||||
public $filename;
|
||||
/**
|
||||
* @var array additional file document contents.
|
||||
* Common GridFS columns:
|
||||
*
|
||||
* - metadata: array, additional data associated with the file.
|
||||
* - aliases: array, an array of aliases.
|
||||
* - contentType: string, content type to be stored with the file.
|
||||
*/
|
||||
public $document = [];
|
||||
/**
|
||||
* @var int chunk size in bytes.
|
||||
*/
|
||||
public $chunkSize = 261120;
|
||||
/**
|
||||
* @var int total upload length in bytes.
|
||||
*/
|
||||
public $length = 0;
|
||||
/**
|
||||
* @var int file chunk counts.
|
||||
*/
|
||||
public $chunkCount = 0;
|
||||
|
||||
/**
|
||||
* @var ObjectID file document ID.
|
||||
*/
|
||||
private $_documentId;
|
||||
/**
|
||||
* @var resource has context for collecting md5 hash
|
||||
*/
|
||||
private $_hashContext;
|
||||
/**
|
||||
* @var string internal data buffer
|
||||
*/
|
||||
private $_buffer;
|
||||
/**
|
||||
* @var bool indicates whether upload is complete or not.
|
||||
*/
|
||||
private $_isComplete = false;
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Makes sure abandoned upload is cancelled.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (!$this->_isComplete) {
|
||||
$this->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->_hashContext = hash_init('md5');
|
||||
|
||||
if (isset($this->document['_id'])) {
|
||||
if ($this->document['_id'] instanceof ObjectID) {
|
||||
$this->_documentId = $this->document['_id'];
|
||||
} else {
|
||||
try {
|
||||
$this->_documentId = new ObjectID($this->document['_id']);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
// invalid id format
|
||||
$this->_documentId = $this->document['_id'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->_documentId = new ObjectID();
|
||||
}
|
||||
|
||||
$this->collection->ensureIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds string content to the upload.
|
||||
* This method can invoked several times before [[complete()]] is called.
|
||||
* @param string $content binary content.
|
||||
* @return $this self reference.
|
||||
*/
|
||||
public function addContent($content)
|
||||
{
|
||||
$freeBufferLength = $this->chunkSize - StringHelper::byteLength($this->_buffer);
|
||||
$contentLength = StringHelper::byteLength($content);
|
||||
if ($contentLength > $freeBufferLength) {
|
||||
$this->_buffer .= StringHelper::byteSubstr($content, 0, $freeBufferLength);
|
||||
$this->flushBuffer(true);
|
||||
return $this->addContent(StringHelper::byteSubstr($content, $freeBufferLength));
|
||||
} else {
|
||||
$this->_buffer .= $content;
|
||||
$this->flushBuffer();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds stream content to the upload.
|
||||
* This method can invoked several times before [[complete()]] is called.
|
||||
* @param resource $stream data source stream.
|
||||
* @return $this self reference.
|
||||
*/
|
||||
public function addStream($stream)
|
||||
{
|
||||
while (!feof($stream)) {
|
||||
$freeBufferLength = $this->chunkSize - StringHelper::byteLength($this->_buffer);
|
||||
|
||||
$streamChunk = fread($stream, $freeBufferLength);
|
||||
if ($streamChunk === false) {
|
||||
break;
|
||||
}
|
||||
$this->_buffer .= $streamChunk;
|
||||
$this->flushBuffer();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file content to the upload.
|
||||
* This method can invoked several times before [[complete()]] is called.
|
||||
* @param string $filename source file name.
|
||||
* @return $this self reference.
|
||||
*/
|
||||
public function addFile($filename)
|
||||
{
|
||||
if ($this->filename === null) {
|
||||
$this->filename = basename($filename);
|
||||
}
|
||||
|
||||
$stream = fopen($filename, 'r+');
|
||||
if ($stream === false) {
|
||||
throw new InvalidParamException("Unable to read file '{$filename}'");
|
||||
}
|
||||
return $this->addStream($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes upload.
|
||||
* @return array saved document.
|
||||
*/
|
||||
public function complete()
|
||||
{
|
||||
$this->flushBuffer(true);
|
||||
|
||||
$document = $this->insertFile();
|
||||
|
||||
$this->_isComplete = true;
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the upload.
|
||||
*/
|
||||
public function cancel()
|
||||
{
|
||||
$this->_buffer = null;
|
||||
|
||||
$this->collection->getChunkCollection()->remove(['files_id' => $this->_documentId], ['limit' => 0]);
|
||||
$this->collection->remove(['_id' => $this->_documentId], ['limit' => 1]);
|
||||
|
||||
$this->_isComplete = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes [[buffer]] to the chunk if it is full.
|
||||
* @param bool $force whether to enforce flushing.
|
||||
*/
|
||||
private function flushBuffer($force = false)
|
||||
{
|
||||
if ($this->_buffer === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($force || StringHelper::byteLength($this->_buffer) == $this->chunkSize) {
|
||||
$this->insertChunk($this->_buffer);
|
||||
$this->_buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts file chunk.
|
||||
* @param string $data chunk binary content.
|
||||
*/
|
||||
private function insertChunk($data)
|
||||
{
|
||||
$chunkDocument = [
|
||||
'files_id' => $this->_documentId,
|
||||
'n' => $this->chunkCount,
|
||||
'data' => new Binary($data, Binary::TYPE_GENERIC),
|
||||
];
|
||||
|
||||
hash_update($this->_hashContext, $data);
|
||||
|
||||
$this->collection->getChunkCollection()->insert($chunkDocument);
|
||||
$this->length += StringHelper::byteLength($data);
|
||||
$this->chunkCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts [[document]] into file collection.
|
||||
* @return array inserted file document data.
|
||||
*/
|
||||
private function insertFile()
|
||||
{
|
||||
$fileDocument = [
|
||||
'_id' => $this->_documentId,
|
||||
'uploadDate' => new UTCDateTime(round(microtime(true) * 1000)),
|
||||
];
|
||||
if ($this->filename === null) {
|
||||
$fileDocument['filename'] = $this->_documentId . '.dat';
|
||||
} else {
|
||||
$fileDocument['filename'] = $this->filename;
|
||||
}
|
||||
|
||||
$fileDocument = array_merge(
|
||||
$fileDocument,
|
||||
$this->document,
|
||||
[
|
||||
'chunkSize' => $this->chunkSize,
|
||||
'length' => $this->length,
|
||||
'md5' => hash_final($this->_hashContext),
|
||||
]
|
||||
);
|
||||
|
||||
$this->collection->insert($fileDocument);
|
||||
return $fileDocument;
|
||||
}
|
||||
}
|
||||
283
vendor/yiisoft/yii2-mongodb/src/gii/model/Generator.php
vendored
Normal file
283
vendor/yiisoft/yii2-mongodb/src/gii/model/Generator.php
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\gii\model;
|
||||
|
||||
use Yii;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\mongodb\ActiveRecord;
|
||||
use yii\mongodb\Connection;
|
||||
use yii\gii\CodeFile;
|
||||
use yii\helpers\Inflector;
|
||||
|
||||
/**
|
||||
* This generator will generate ActiveRecord class for the specified MongoDB collection.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Generator extends \yii\gii\Generator
|
||||
{
|
||||
public $db = 'mongodb';
|
||||
public $ns = 'app\models';
|
||||
public $collectionName;
|
||||
public $databaseName;
|
||||
public $attributeList;
|
||||
public $modelClass;
|
||||
public $baseClass = 'yii\mongodb\ActiveRecord';
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'MongoDB Model Generator';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return 'This generator generates an ActiveRecord class for the specified MongoDB collection.';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return array_merge(parent::rules(), [
|
||||
[['db', 'ns', 'collectionName', 'databaseName', 'attributeList', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'],
|
||||
[['ns'], 'filter', 'filter' => function($value) { return trim($value, '\\'); }],
|
||||
|
||||
[['db', 'ns', 'collectionName', 'baseClass'], 'required'],
|
||||
[['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'],
|
||||
[['ns', 'baseClass'], 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'],
|
||||
[['collectionName'], 'match', 'pattern' => '/^[^$ ]+$/', 'message' => 'Collection name can not contain spaces or "$" symbols.'],
|
||||
[['databaseName'], 'match', 'pattern' => '/^[^\\/\\\\\\. "*:?\\|<>]+$/', 'message' => 'Database name can not contain spaces or any of "/\."*<>:|?" symbols.'],
|
||||
[['db'], 'validateDb'],
|
||||
[['ns'], 'validateNamespace'],
|
||||
[['collectionName'], 'validateCollectionName'],
|
||||
[['attributeList'], 'match', 'pattern' => '/^(\w+\,[ ]*)*([\w]+)$/', 'message' => 'Attributes should contain only word characters, and should be separated by coma.'],
|
||||
[['modelClass'], 'validateModelClass', 'skipOnEmpty' => false],
|
||||
[['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]],
|
||||
[['enableI18N'], 'boolean'],
|
||||
[['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return array_merge(parent::attributeLabels(), [
|
||||
'ns' => 'Namespace',
|
||||
'db' => 'MongoDB Connection ID',
|
||||
'collectionName' => 'Collection Name',
|
||||
'databaseName' => 'Database Name',
|
||||
'modelClass' => 'Model Class',
|
||||
'baseClass' => 'Base Class',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hints()
|
||||
{
|
||||
return array_merge(parent::hints(), [
|
||||
'ns' => 'This is the namespace of the ActiveRecord class to be generated, e.g., <code>app\models</code>',
|
||||
'db' => 'This is the ID of the MongoDB application component.',
|
||||
'collectionName' => 'This is the name of the MongoDB collection that the new ActiveRecord class is associated with, e.g. <code>post</code>.',
|
||||
'databaseName' => 'This is the name of the MongoDB database, which contains the collection that the new ActiveRecord class is associated with.
|
||||
You may leave this field blank, if your application uses single MongoDB database.',
|
||||
'attributeList' => 'List of the collection attribute names separated by coma.
|
||||
You do not need to specify "_id" attribute here - it will be added automatically.',
|
||||
'modelClass' => 'This is the name of the ActiveRecord class to be generated. The class name should not contain
|
||||
the namespace part as it is specified in "Namespace". You may leave this field blank - in this case class name
|
||||
will be generated automatically.',
|
||||
'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function autoCompleteData()
|
||||
{
|
||||
$db = $this->getDbConnection();
|
||||
if ($db !== null) {
|
||||
return [
|
||||
'collectionName' => function () use ($db) {
|
||||
$collections = $db->getDatabase()->createCommand()->listCollections();
|
||||
return ArrayHelper::getColumn($collections, 'name');
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requiredTemplates()
|
||||
{
|
||||
return ['model.php'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function stickyAttributes()
|
||||
{
|
||||
return array_merge(parent::stickyAttributes(), ['ns', 'db', 'baseClass']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate()
|
||||
{
|
||||
$files = [];
|
||||
$collectionName = $this->collectionName;
|
||||
|
||||
$attributes = ['_id'];
|
||||
if (!empty($this->attributeList)) {
|
||||
$customAttributes = explode(',', $this->attributeList);
|
||||
$customAttributes = array_map('trim', $customAttributes);
|
||||
$attributes = array_merge(['_id'], $customAttributes);
|
||||
}
|
||||
|
||||
$className = $this->generateClassName($collectionName);
|
||||
$params = [
|
||||
'collectionName' => $collectionName,
|
||||
'className' => $className,
|
||||
'attributes' => $attributes,
|
||||
'labels' => $this->generateLabels($attributes),
|
||||
'rules' => $this->generateRules($attributes),
|
||||
];
|
||||
$files[] = new CodeFile(
|
||||
Yii::getAlias('@' . str_replace('\\', '/', $this->ns)) . '/' . $className . '.php',
|
||||
$this->render('model.php', $params)
|
||||
);
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the attribute labels for the specified attributes list.
|
||||
* @param array $attributes the list of attributes
|
||||
* @return array the generated attribute labels (name => label)
|
||||
*/
|
||||
public function generateLabels($attributes)
|
||||
{
|
||||
$labels = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
if (!strcasecmp($attribute, '_id')) {
|
||||
$label = 'ID';
|
||||
} else {
|
||||
$label = Inflector::camel2words($attribute);
|
||||
if (substr_compare($label, ' id', -3, 3, true) === 0) {
|
||||
$label = substr($label, 0, -3) . ' ID';
|
||||
}
|
||||
}
|
||||
$labels[$attribute] = $label;
|
||||
}
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates validation rules for the specified collection.
|
||||
* @param array $attributes the list of attributes
|
||||
* @return array the generated validation rules
|
||||
*/
|
||||
public function generateRules($attributes)
|
||||
{
|
||||
$rules = [];
|
||||
$safeAttributes = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute == '_id') {
|
||||
continue;
|
||||
}
|
||||
$safeAttributes[] = $attribute;
|
||||
}
|
||||
if (!empty($safeAttributes)) {
|
||||
$rules[] = "[['" . implode("', '", $safeAttributes) . "'], 'safe']";
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the [[db]] attribute.
|
||||
*/
|
||||
public function validateDb()
|
||||
{
|
||||
if (!Yii::$app->has($this->db)) {
|
||||
$this->addError('db', 'There is no application component named "' . $this->db . '".');
|
||||
} elseif (!Yii::$app->get($this->db) instanceof Connection) {
|
||||
$this->addError('db', 'The "' . $this->db . '" application component must be a MongoDB connection instance.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the [[ns]] attribute.
|
||||
*/
|
||||
public function validateNamespace()
|
||||
{
|
||||
$this->ns = ltrim($this->ns, '\\');
|
||||
$path = Yii::getAlias('@' . str_replace('\\', '/', $this->ns), false);
|
||||
if ($path === false) {
|
||||
$this->addError('ns', 'Namespace must be associated with an existing directory.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the [[modelClass]] attribute.
|
||||
*/
|
||||
public function validateModelClass()
|
||||
{
|
||||
if ($this->isReservedKeyword($this->modelClass)) {
|
||||
$this->addError('modelClass', 'Class name cannot be a reserved PHP keyword.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the [[collectionName]] attribute.
|
||||
*/
|
||||
public function validateCollectionName()
|
||||
{
|
||||
if (empty($this->modelClass)) {
|
||||
$class = $this->generateClassName($this->collectionName);
|
||||
if ($this->isReservedKeyword($class)) {
|
||||
$this->addError('collectionName', "Collection '{$this->collectionName}' will generate a class which is a reserved PHP keyword.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class name from the specified collection name.
|
||||
* @param string $collectionName the collection name (which may contain schema prefix)
|
||||
* @return string the generated class name
|
||||
*/
|
||||
protected function generateClassName($collectionName)
|
||||
{
|
||||
$className = preg_replace('/[^\\w]+/is', '_', $collectionName);
|
||||
return Inflector::id2camel($className, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Connection the DB connection as specified by [[db]].
|
||||
*/
|
||||
protected function getDbConnection()
|
||||
{
|
||||
return Yii::$app->get($this->db, false);
|
||||
}
|
||||
}
|
||||
83
vendor/yiisoft/yii2-mongodb/src/gii/model/default/model.php
vendored
Normal file
83
vendor/yiisoft/yii2-mongodb/src/gii/model/default/model.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* This is the template for generating the model class of a specified collection.
|
||||
*/
|
||||
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $generator yii\mongodb\gii\model\Generator */
|
||||
/* @var $collectionName string full collection name */
|
||||
/* @var $attributes array list of attribute names */
|
||||
/* @var $className string class name */
|
||||
/* @var $labels string[] list of attribute labels (name => label) */
|
||||
/* @var $rules string[] list of validation rules */
|
||||
|
||||
echo "<?php\n";
|
||||
?>
|
||||
|
||||
namespace <?= $generator->ns ?>;
|
||||
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* This is the model class for collection "<?= $collectionName ?>".
|
||||
*
|
||||
<?php foreach ($attributes as $attribute): ?>
|
||||
* @property <?= $attribute == '_id' ? '\MongoDB\BSON\ObjectID|string' : 'mixed' ?> <?= "\${$attribute}\n" ?>
|
||||
<?php endforeach; ?>
|
||||
*/
|
||||
class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') . "\n" ?>
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function collectionName()
|
||||
{
|
||||
<?php if (empty($generator->databaseName)): ?>
|
||||
return '<?= $collectionName ?>';
|
||||
<?php else: ?>
|
||||
return ['<?= $generator->databaseName ?>', '<?= $collectionName ?>'];
|
||||
<?php endif; ?>
|
||||
}
|
||||
<?php if ($generator->db !== 'mongodb'): ?>
|
||||
|
||||
/**
|
||||
* @return \yii\mongodb\Connection the MongoDB connection used by this AR class.
|
||||
*/
|
||||
public static function getDb()
|
||||
{
|
||||
return Yii::$app->get('<?= $generator->db ?>');
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
return [
|
||||
<?php foreach ($attributes as $attribute): ?>
|
||||
<?= "'$attribute',\n" ?>
|
||||
<?php endforeach; ?>
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [<?= "\n " . implode(",\n ", $rules) . "\n " ?>];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return [
|
||||
<?php foreach ($labels as $name => $label): ?>
|
||||
<?= "'$name' => " . $generator->generateString($label) . ",\n" ?>
|
||||
<?php endforeach; ?>
|
||||
];
|
||||
}
|
||||
}
|
||||
14
vendor/yiisoft/yii2-mongodb/src/gii/model/form.php
vendored
Normal file
14
vendor/yiisoft/yii2-mongodb/src/gii/model/form.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/* @var $this yii\web\View */
|
||||
/* @var $form yii\widgets\ActiveForm */
|
||||
/* @var $generator yii\mongodb\gii\model\Generator */
|
||||
|
||||
echo $form->field($generator, 'collectionName');
|
||||
echo $form->field($generator, 'databaseName');
|
||||
echo $form->field($generator, 'attributeList');
|
||||
echo $form->field($generator, 'modelClass');
|
||||
echo $form->field($generator, 'ns');
|
||||
echo $form->field($generator, 'baseClass');
|
||||
echo $form->field($generator, 'db');
|
||||
echo $form->field($generator, 'enableI18N')->checkbox();
|
||||
echo $form->field($generator, 'messageCategory');
|
||||
215
vendor/yiisoft/yii2-mongodb/src/i18n/MongoDbMessageSource.php
vendored
Normal file
215
vendor/yiisoft/yii2-mongodb/src/i18n/MongoDbMessageSource.php
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\i18n;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\caching\Cache;
|
||||
use yii\di\Instance;
|
||||
use yii\i18n\MessageSource;
|
||||
use yii\mongodb\Connection;
|
||||
use yii\mongodb\Query;
|
||||
|
||||
/**
|
||||
* MongoDbMessageSource extends [[MessageSource]] and represents a message source that stores translated
|
||||
* messages in MongoDB collection.
|
||||
*
|
||||
* This message source uses single collection for the message translations storage, defined via [[collection]].
|
||||
* Each entry in this collection should have 3 fields:
|
||||
*
|
||||
* - language: string, translation language
|
||||
* - category: string, name translation category
|
||||
* - messages: array, list of actual message translations, in each element: the 'message' key is raw message name
|
||||
* and 'translation' key - message translation.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "category": "app",
|
||||
* "language": "de",
|
||||
* "messages": {
|
||||
* {
|
||||
* "message": "Hello world!",
|
||||
* "translation": "Hallo Welt!"
|
||||
* },
|
||||
* {
|
||||
* "message": "The dog runs fast.",
|
||||
* "translation": "Der Hund rennt schnell.",
|
||||
* },
|
||||
* ...
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* You also can specify 'messages' using source message as a direct BSON key, while its value holds the translation.
|
||||
* For example:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "category": "app",
|
||||
* "language": "de",
|
||||
* "messages": {
|
||||
* "Hello world!": "Hallo Welt!",
|
||||
* "See more": "Mehr sehen",
|
||||
* ...
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* However such approach is not recommended as BSON keys can not contain symbols like `.` or `$`.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.5
|
||||
*/
|
||||
class MongoDbMessageSource extends MessageSource
|
||||
{
|
||||
/**
|
||||
* @var Connection|array|string the MongoDB connection object or the application component ID of the MongoDB connection.
|
||||
*
|
||||
* After the MongoDbMessageSource object is created, if you want to change this property, you should only assign
|
||||
* it with a MongoDB connection object.
|
||||
*
|
||||
* This can also be a configuration array for creating the object.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var Cache|array|string the cache object or the application component ID of the cache object.
|
||||
* The messages data will be cached using this cache object.
|
||||
* Note, that to enable caching you have to set [[enableCaching]] to `true`, otherwise setting this property has no effect.
|
||||
*
|
||||
* After the MongoDbMessageSource object is created, if you want to change this property, you should only assign
|
||||
* it with a cache object.
|
||||
*
|
||||
* This can also be a configuration array for creating the object.
|
||||
* @see cachingDuration
|
||||
* @see enableCaching
|
||||
*/
|
||||
public $cache = 'cache';
|
||||
/**
|
||||
* @var string|array the name of the MongoDB collection, which stores translated messages.
|
||||
* This collection is better to be pre-created with fields 'category' and 'language' indexed.
|
||||
*/
|
||||
public $collection = 'message';
|
||||
/**
|
||||
* @var int the time in seconds that the messages can remain valid in cache.
|
||||
* Use 0 to indicate that the cached data will never expire.
|
||||
* @see enableCaching
|
||||
*/
|
||||
public $cachingDuration = 0;
|
||||
/**
|
||||
* @var bool whether to enable caching translated messages
|
||||
*/
|
||||
public $enableCaching = false;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the DbMessageSource component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
|
||||
* Configured [[cache]] component would also be initialized.
|
||||
* @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
if ($this->enableCaching) {
|
||||
$this->cache = Instance::ensure($this->cache, Cache::className());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the message translation for the specified language and category.
|
||||
* If translation for specific locale code such as `en-US` isn't found it
|
||||
* tries more generic `en`.
|
||||
*
|
||||
* @param string $category the message category
|
||||
* @param string $language the target language
|
||||
* @return array the loaded messages. The keys are original messages, and the values
|
||||
* are translated messages.
|
||||
*/
|
||||
protected function loadMessages($category, $language)
|
||||
{
|
||||
if ($this->enableCaching) {
|
||||
$key = [
|
||||
__CLASS__,
|
||||
$category,
|
||||
$language,
|
||||
];
|
||||
$messages = $this->cache->get($key);
|
||||
if ($messages === false) {
|
||||
$messages = $this->loadMessagesFromDb($category, $language);
|
||||
$this->cache->set($key, $messages, $this->cachingDuration);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
return $this->loadMessagesFromDb($category, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the messages from MongoDB.
|
||||
* You may override this method to customize the message storage in the MongoDB.
|
||||
* @param string $category the message category.
|
||||
* @param string $language the target language.
|
||||
* @return array the messages loaded from database.
|
||||
*/
|
||||
protected function loadMessagesFromDb($category, $language)
|
||||
{
|
||||
$fallbackLanguage = substr($language, 0, 2);
|
||||
$fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
|
||||
|
||||
$languages = [
|
||||
$language,
|
||||
$fallbackLanguage,
|
||||
$fallbackSourceLanguage
|
||||
];
|
||||
|
||||
$rows = (new Query())
|
||||
->select(['language', 'messages'])
|
||||
->from($this->collection)
|
||||
->andWhere(['category' => $category])
|
||||
->andWhere(['language' => array_unique($languages)])
|
||||
->all($this->db);
|
||||
|
||||
if (count($rows) > 1) {
|
||||
$languagePriorities = [
|
||||
$language => 1
|
||||
];
|
||||
$languagePriorities[$fallbackLanguage] = 2; // language key may be already taken
|
||||
$languagePriorities[$fallbackSourceLanguage] = 3; // language key may be already taken
|
||||
|
||||
usort($rows, function ($a, $b) use ($languagePriorities) {
|
||||
$languageA = $a['language'];
|
||||
$languageB = $b['language'];
|
||||
|
||||
if ($languageA === $languageB) {
|
||||
return 0;
|
||||
}
|
||||
if ($languagePriorities[$languageA] < $languagePriorities[$languageB]) {
|
||||
return +1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
foreach ($rows as $row) {
|
||||
foreach ($row['messages'] as $key => $value) {
|
||||
// @todo drop message as key specification at 2.2
|
||||
if (is_array($value)) {
|
||||
$messages[$value['message']] = $value['translation'];
|
||||
} else {
|
||||
$messages[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
||||
79
vendor/yiisoft/yii2-mongodb/src/log/MongoDbTarget.php
vendored
Normal file
79
vendor/yiisoft/yii2-mongodb/src/log/MongoDbTarget.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\log;
|
||||
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\di\Instance;
|
||||
use yii\helpers\VarDumper;
|
||||
use yii\log\Target;
|
||||
use yii\mongodb\Connection;
|
||||
|
||||
/**
|
||||
* MongoDbTarget stores log messages in a MongoDB collection.
|
||||
*
|
||||
* By default, MongoDbTarget stores the log messages in a MongoDB collection named 'log'.
|
||||
* The collection can be changed by setting the [[logCollection]] property.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class MongoDbTarget extends Target
|
||||
{
|
||||
/**
|
||||
* @var Connection|string the MongoDB connection object or the application component ID of the MongoDB connection.
|
||||
* After the MongoDbTarget object is created, if you want to change this property, you should only assign it
|
||||
* with a MongoDB connection object.
|
||||
*/
|
||||
public $db = 'mongodb';
|
||||
/**
|
||||
* @var string|array the name of the MongoDB collection that stores the session data.
|
||||
* Please refer to [[Connection::getCollection()]] on how to specify this parameter.
|
||||
* This collection is better to be pre-created with fields 'id' and 'expire' indexed.
|
||||
*/
|
||||
public $logCollection = 'log';
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the MongoDbTarget component.
|
||||
* This method will initialize the [[db]] property to make sure it refers to a valid MongoDB connection.
|
||||
* @throws InvalidConfigException if [[db]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->db = Instance::ensure($this->db, Connection::className());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores log messages to MongoDB collection.
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$rows = [];
|
||||
foreach ($this->messages as $message) {
|
||||
list($text, $level, $category, $timestamp) = $message;
|
||||
if (!is_string($text)) {
|
||||
// exceptions may not be serializable if in the call stack somewhere is a Closure
|
||||
if ($text instanceof \Throwable || $text instanceof \Exception) {
|
||||
$text = (string) $text;
|
||||
} else {
|
||||
$text = VarDumper::export($text);
|
||||
}
|
||||
}
|
||||
$rows[] = [
|
||||
'level' => $level,
|
||||
'category' => $category,
|
||||
'log_time' => $timestamp,
|
||||
'prefix' => $this->getMessagePrefix($message),
|
||||
'message' => $text,
|
||||
];
|
||||
}
|
||||
|
||||
$this->db->getCollection($this->logCollection)->batchInsert($rows);
|
||||
}
|
||||
}
|
||||
1078
vendor/yiisoft/yii2-mongodb/src/rbac/MongoDbManager.php
vendored
Normal file
1078
vendor/yiisoft/yii2-mongodb/src/rbac/MongoDbManager.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
vendor/yiisoft/yii2-mongodb/src/rbac/Permission.php
vendored
Normal file
22
vendor/yiisoft/yii2-mongodb/src/rbac/Permission.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\rbac;
|
||||
|
||||
/**
|
||||
* Permission is a special version of [[\yii\rbac\Permission]] dedicated to MongoDB RBAC implementation.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.5
|
||||
*/
|
||||
class Permission extends \yii\rbac\Permission
|
||||
{
|
||||
/**
|
||||
* @var array|null list of parent item names.
|
||||
*/
|
||||
public $parents;
|
||||
}
|
||||
22
vendor/yiisoft/yii2-mongodb/src/rbac/Role.php
vendored
Normal file
22
vendor/yiisoft/yii2-mongodb/src/rbac/Role.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\rbac;
|
||||
|
||||
/**
|
||||
* Role is a special version of [[\yii\rbac\Role]] dedicated to MongoDB RBAC implementation.
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.5
|
||||
*/
|
||||
class Role extends \yii\rbac\Role
|
||||
{
|
||||
/**
|
||||
* @var array|null list of parent item names.
|
||||
*/
|
||||
public $parents;
|
||||
}
|
||||
84
vendor/yiisoft/yii2-mongodb/src/validators/MongoDateValidator.php
vendored
Normal file
84
vendor/yiisoft/yii2-mongodb/src/validators/MongoDateValidator.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\validators;
|
||||
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use yii\validators\DateValidator;
|
||||
|
||||
/**
|
||||
* MongoDateValidator is an enhanced version of [[DateValidator]], which supports [[\MongoDate]] values.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* class Customer extends yii\mongodb\ActiveRecord
|
||||
* {
|
||||
* ...
|
||||
* public function rules()
|
||||
* {
|
||||
* return [
|
||||
* ['date', 'yii\mongodb\validators\MongoDateValidator', 'format' => 'MM/dd/yyyy']
|
||||
* ];
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see DateValidator
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.4
|
||||
*/
|
||||
class MongoDateValidator extends DateValidator
|
||||
{
|
||||
/**
|
||||
* @var string the name of the attribute to receive the parsing result as [[\MongoDate]] instance.
|
||||
* When this property is not null and the validation is successful, the named attribute will
|
||||
* receive the parsing result as [[\MongoDate]] instance.
|
||||
*
|
||||
* This can be the same attribute as the one being validated. If this is the case,
|
||||
* the original value will be overwritten with the value after successful validation.
|
||||
*/
|
||||
public $mongoDateAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAttribute($model, $attribute)
|
||||
{
|
||||
$mongoDateAttribute = $this->mongoDateAttribute;
|
||||
if ($this->timestampAttribute === null) {
|
||||
$this->timestampAttribute = $mongoDateAttribute;
|
||||
}
|
||||
|
||||
$originalErrorCount = count($model->getErrors($attribute));
|
||||
parent::validateAttribute($model, $attribute);
|
||||
$afterValidateErrorCount = count($model->getErrors($attribute));
|
||||
|
||||
if ($originalErrorCount === $afterValidateErrorCount) {
|
||||
if ($this->mongoDateAttribute !== null) {
|
||||
$timestamp = $model->{$this->timestampAttribute};
|
||||
$mongoDateAttributeValue = $model->{$this->mongoDateAttribute};
|
||||
// ensure "dirty attributes" support :
|
||||
if (!($mongoDateAttributeValue instanceof UTCDateTime) || $mongoDateAttributeValue->sec !== $timestamp) {
|
||||
$model->{$this->mongoDateAttribute} = new UTCDateTime($timestamp * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function parseDateValue($value)
|
||||
{
|
||||
return $value instanceof UTCDateTime
|
||||
? $value->toDateTime()->getTimestamp()
|
||||
: parent::parseDateValue($value);
|
||||
}
|
||||
}
|
||||
115
vendor/yiisoft/yii2-mongodb/src/validators/MongoIdValidator.php
vendored
Normal file
115
vendor/yiisoft/yii2-mongodb/src/validators/MongoIdValidator.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\mongodb\validators;
|
||||
|
||||
use MongoDB\BSON\ObjectID;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\validators\Validator;
|
||||
use Yii;
|
||||
|
||||
/**
|
||||
* MongoIdValidator verifies if the attribute is a valid Mongo ID.
|
||||
* Attribute will be considered as valid, if it is an instance of [[\MongoId]] or a its string value.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* class Customer extends yii\mongodb\ActiveRecord
|
||||
* {
|
||||
* ...
|
||||
* public function rules()
|
||||
* {
|
||||
* return [
|
||||
* ['_id', 'yii\mongodb\validators\MongoIdValidator']
|
||||
* ];
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This validator may also serve as a filter, allowing conversion of Mongo ID value either to the plain string
|
||||
* or to [[\MongoId]] instance. You can enable this feature via [[forceFormat]].
|
||||
*
|
||||
* @author Paul Klimov <klimov.paul@gmail.com>
|
||||
* @since 2.0.4
|
||||
*/
|
||||
class MongoIdValidator extends Validator
|
||||
{
|
||||
/**
|
||||
* @var string|null specifies the format, which validated attribute value should be converted to
|
||||
* in case validation was successful.
|
||||
* valid values are:
|
||||
* - 'string' - enforce value converted to plain string.
|
||||
* - 'object' - enforce value converted to [[\MongoId]] instance.
|
||||
* If not set - no conversion will be performed, leaving attribute value intact.
|
||||
*/
|
||||
public $forceFormat;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->message === null) {
|
||||
$this->message = Yii::t('yii', '{attribute} is invalid.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAttribute($model, $attribute)
|
||||
{
|
||||
$value = $model->$attribute;
|
||||
$mongoId = $this->parseMongoId($value);
|
||||
if (is_object($mongoId)) {
|
||||
if ($this->forceFormat !== null) {
|
||||
switch ($this->forceFormat) {
|
||||
case 'string' : {
|
||||
$model->$attribute = $mongoId->__toString();
|
||||
break;
|
||||
}
|
||||
case 'object' : {
|
||||
$model->$attribute = $mongoId;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new InvalidConfigException("Unrecognized format '{$this->forceFormat}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->addError($model, $attribute, $this->message, []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validateValue($value)
|
||||
{
|
||||
return is_object($this->parseMongoId($value)) ? null : [$this->message, []];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return ObjectID|null
|
||||
*/
|
||||
private function parseMongoId($value)
|
||||
{
|
||||
if ($value instanceof ObjectID) {
|
||||
return $value;
|
||||
}
|
||||
try {
|
||||
return new ObjectID($value);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/yiisoft/yii2-mongodb/src/views/migration.php
vendored
Normal file
27
vendor/yiisoft/yii2-mongodb/src/views/migration.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* This view is used by console/controllers/MigrateController.php
|
||||
* The following variables are available in this view:
|
||||
*/
|
||||
|
||||
/* @var $className string the new migration class name */
|
||||
echo "<?php\n";
|
||||
if (!empty($namespace)) {
|
||||
echo "\nnamespace {$namespace};\n";
|
||||
}
|
||||
?>
|
||||
|
||||
class <?= $className ?> extends \yii\mongodb\Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
echo "<?= $className ?> cannot be reverted.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user