Commit 5cb24302 by Alexander Makarov

Merge branch 'master'

parents b1d94e5c 8077ea3f
...@@ -69,7 +69,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -69,7 +69,7 @@ class User extends ActiveRecord implements IdentityInterface
/** /**
* @inheritdoc * @inheritdoc
*/ */
public static function findIdentityByAccessToken($token) public static function findIdentityByAccessToken($token, $type = null)
{ {
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
} }
......
...@@ -38,7 +38,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface ...@@ -38,7 +38,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
/** /**
* @inheritdoc * @inheritdoc
*/ */
public static function findIdentityByAccessToken($token) public static function findIdentityByAccessToken($token, $type = null)
{ {
foreach (self::$users as $user) { foreach (self::$users as $user) {
if ($user['accessToken'] === $token) { if ($user['accessToken'] === $token) {
......
...@@ -679,7 +679,7 @@ use yii\web\IdentityInterface; ...@@ -679,7 +679,7 @@ use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface class User extends ActiveRecord implements IdentityInterface
{ {
public static function findIdentityByAccessToken($token) public static function findIdentityByAccessToken($token, $type = null)
{ {
return static::findOne(['access_token' => $token]); return static::findOne(['access_token' => $token]);
} }
......
...@@ -32,7 +32,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -32,7 +32,7 @@ class User extends ActiveRecord implements IdentityInterface
* @param string $token the token to be looked for * @param string $token the token to be looked for
* @return IdentityInterface|null the identity object that matches the given token. * @return IdentityInterface|null the identity object that matches the given token.
*/ */
public static function findIdentityByAccessToken($token) public static function findIdentityByAccessToken($token, $type = null)
{ {
return static::findOne(['access_token' => $token]); return static::findOne(['access_token' => $token]);
} }
......
...@@ -460,7 +460,7 @@ of the rule class, you need to respect this hierarchy as well. That is why when ...@@ -460,7 +460,7 @@ of the rule class, you need to respect this hierarchy as well. That is why when
the `execute()` method will return true if the user group is either 1 or 2 (meaning the user is in either "admin" the `execute()` method will return true if the user group is either 1 or 2 (meaning the user is in either "admin"
group or "author" group). group or "author" group).
Next, configure `authManager` by listing the two roles in [[yii\rbac\ManagerInterface::defaultRoles]]: Next, configure `authManager` by listing the two roles in [[yii\rbac\BaseManager::$defaultRoles]]:
```php ```php
return [ return [
......
Basic application template Installing Yii
========================== ==============
There are two ways to install Yii:
* Using [Composer](http://getcomposer.org/);
* Downloading an archive file from [yiiframework.com](http://www.yiiframework.com/download/).
The first approach is highly recommended, as it allows you to automatically install updates
Installing via Composer
-----------------------
The recommended way to install Yii is to use the [Composer](http://getcomposer.org/) package manager. If you do not already
have Composer installed, you may download it from [http://getcomposer.org/](http://getcomposer.org/), or run the following command to download and install it:
```
curl -s http://getcomposer.org/installer | php
```
(It is strongly recommended to perform a [global Composer installation](https://getcomposer.org/doc/00-intro.md#globally)).
For problems with, or more information on, installing Composer, see the official Composer guide:
* [Linux](http://getcomposer.org/doc/00-intro.md#installation-nix)
* [Windows](http://getcomposer.org/doc/00-intro.md#installation-windows)
With Composer installed, you can create a new Yii site using one of Yii's ready-to-use application templates. Based on your needs, choosing the right template can help bootstrap your project.
Currently, there are two Yii application templates available:
- [Basic Application Template](https://github.com/yiisoft/yii2-app-basic), a basic frontend application template
- [Advanced Application Template](https://github.com/yiisoft/yii2-app-advanced), consisting of a frontend, a backend, console resources, common (shared code), and support for environments
For template installation instructions, see the above linked pages.
To read more about the ideas behind these application templates and the proposed usage,
refer to the [basic application template](apps-basic.md) and [advanced application template](apps-advanced.md) documents.
If you do not want to use a template, rather starting from scratch, you'll find information in the [creating your own application structure](apps-own.md) document. This approach is only recommended for advanced users.
Installing from zip
-------------------
Installation from a zip file involves two steps:
1. Downloading an application template from [yiiframework.com](http://www.yiiframework.com/download/).
2. Unpacking the downloaded file.
If you only want the Yii Framework files you can download a zip file directly from [github](https://github.com/yiisoft/yii2-framework/releases).
To create your application you might want to follow the steps described in [creating your own application structure](apps-own.md).
This is only recommended for advanced users.
> Tip: The Yii framework itself does not need to be installed under a web-accessible directory (in fact, it should not be).
A Yii application has one entry script, which is usually the only file that absolutely must be
exposed to web users (i.e., placed within the web directory). Other PHP scripts, including those
in the Yii Framework, should be protected from web access to prevent possible exploitation by hackers.
Requirements
------------
Yii 2 requires PHP 5.4.0 or higher. Yii has been tested with the [Apache HTTP server](http://httpd.apache.org/) and
[Nginx HTTP server](http://nginx.org/) on both Windows and Linux.
Yii may also be usable on other web servers and platforms, provided that PHP 5.4 or higher is present.
After installing Yii, you may want to verify that your server satisfies
Yii's requirements. You can do so by running the requirement checker
script in a web browser or from the command line.
If you have installed a Yii application template via the downloaded zip file or Composer, you'll find a `requirements.php` file in the
base directory of your application.
In order to run this script on the command line use the following command (after navigating to the directory where `requirements.php` can be found):
```
php requirements.php
```
In order to run this script in your browser, you must make sure it's within a web directory, and then
access `http://hostname/path/to/yii-app/requirements.php` in your browser.
> Note: This section is under development. > Note: This section is under development.
......
...@@ -42,6 +42,26 @@ class BaseDoc extends Object ...@@ -42,6 +42,26 @@ class BaseDoc extends Object
*/ */
public $tags = []; public $tags = [];
public function hasTag($name)
{
foreach ($this->tags as $tag) {
if (strtolower($tag->getName()) == $name) {
return true;
}
}
return false;
}
public function removeTag($name)
{
foreach ($this->tags as $i => $tag) {
if (strtolower($tag->getName()) == $name) {
unset($this->tags[$i]);
}
}
}
/** /**
* @param \phpDocumentor\Reflection\BaseReflector $reflector * @param \phpDocumentor\Reflection\BaseReflector $reflector
* @param Context $context * @param Context $context
...@@ -70,7 +90,7 @@ class BaseDoc extends Object ...@@ -70,7 +90,7 @@ class BaseDoc extends Object
'message' => "No short description for " . substr(StringHelper::basename(get_class($this)), 0, -3) . " '{$this->name}'", 'message' => "No short description for " . substr(StringHelper::basename(get_class($this)), 0, -3) . " '{$this->name}'",
]; ];
} }
$this->description = $docblock->getLongDescription(); $this->description = $docblock->getLongDescription()->getContents();
$this->phpDocContext = $docblock->getContext(); $this->phpDocContext = $docblock->getContext();
......
...@@ -164,11 +164,33 @@ class Context extends Component ...@@ -164,11 +164,33 @@ class Context extends Component
{ {
// TODO also for properties? // TODO also for properties?
foreach ($class->methods as $m) { foreach ($class->methods as $m) {
$inheritedMethod = $this->inheritMethodRecursive($m, $class); if ($m->hasTag('inheritdoc')) {
foreach (['shortDescription', 'description', 'params', 'return', 'returnType', 'returnTypes', 'exceptions'] as $property) { $inheritedMethod = $this->inheritMethodRecursive($m, $class);
if (empty($m->$property)) { foreach (['shortDescription', 'description', 'return', 'returnType', 'returnTypes', 'exceptions'] as $property) {
$m->$property = $inheritedMethod->$property; if (empty($m->$property) || is_string($m->$property) && trim($m->$property) === '') {
$m->$property = $inheritedMethod->$property;
}
} }
foreach ($m->params as $i => $param) {
if (!isset($inheritedMethod->params[$i])) {
$this->errors[] = [
'line' => $m->startLine,
'file' => $class->sourceFile,
'message' => "Method param $i does not exist in parent method, @inheritdoc not possible in {$m->name} in {$class->name}.",
];
continue;
}
if (empty($param->description) || trim($param->description) === '') {
$param->description = $inheritedMethod->params[$i]->description;
}
if (empty($param->type) || trim($param->type) === '') {
$param->type = $inheritedMethod->params[$i]->type;
}
if (empty($param->types) || trim($param->types) === '') {
$param->types = $inheritedMethod->params[$i]->types;
}
}
$m->removeTag('inheritdoc');
} }
} }
} }
...@@ -179,20 +201,50 @@ class Context extends Component ...@@ -179,20 +201,50 @@ class Context extends Component
*/ */
private function inheritMethodRecursive($method, $class) private function inheritMethodRecursive($method, $class)
{ {
if (!isset($this->classes[$class->parentClass])) { $inheritanceCandidates = array_merge(
return $method; $this->getParents($class),
} $this->getInterfaces($class)
$parent = $this->classes[$class->parentClass]; );
foreach ($method->tags as $tag) {
if (strtolower($tag->getName()) == 'inheritdoc') { $methods = [];
if (isset($parent->methods[$method->name])) { foreach($inheritanceCandidates as $candidate) {
$method = $parent->methods[$method->name]; if (isset($candidate->methods[$method->name])) {
$cmethod = $candidate->methods[$method->name];
if ($cmethod->hasTag('inheritdoc')) {
$this->inheritDocs($candidate);
} }
$methods[] = $cmethod;
}
}
return $this->inheritMethodRecursive($method, $parent); return reset($methods);
}
/**
* @param ClassDoc $class
* @return array
*/
private function getParents($class)
{
if ($class->parentClass === null || !isset($this->classes[$class->parentClass])) {
return [];
}
return array_merge([$this->classes[$class->parentClass]], $this->getParents($this->classes[$class->parentClass]));
}
/**
* @param ClassDoc $class
* @return array
*/
private function getInterfaces($class)
{
$interfaces = [];
foreach($class->interfaces as $interface) {
if (isset($this->interfaces[$interface])) {
$interfaces[] = $this->interfaces[$interface];
} }
} }
return $method; return $interfaces;
} }
/** /**
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace yii\elasticsearch; namespace yii\elasticsearch;
use yii\base\NotSupportedException;
use yii\db\ActiveQueryInterface; use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait; use yii\db\ActiveQueryTrait;
use yii\db\ActiveRelationTrait; use yii\db\ActiveRelationTrait;
...@@ -143,47 +144,21 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -143,47 +144,21 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/ */
public function all($db = null) public function all($db = null)
{ {
if ($this->asArray) {
// TODO implement with
return parent::all($db);
}
$result = $this->createCommand($db)->search(); $result = $this->createCommand($db)->search();
if (empty($result['hits']['hits'])) { if (empty($result['hits']['hits'])) {
return []; return [];
} }
if ($this->fields !== null) {
foreach ($result['hits']['hits'] as &$row) {
$row['_source'] = isset($row['fields']) ? $row['fields'] : [];
unset($row['fields']);
}
unset($row);
}
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
$pk = $modelClass::primaryKey()[0];
if ($this->asArray && $this->indexBy) {
foreach ($result['hits']['hits'] as &$row) {
if ($pk === '_id') {
$row['_source']['_id'] = $row['_id'];
}
$row['_source']['_score'] = $row['_score'];
$row = $row['_source'];
}
unset($row);
}
$models = $this->createModels($result['hits']['hits']); $models = $this->createModels($result['hits']['hits']);
if ($this->asArray && !$this->indexBy) {
foreach ($models as $key => $model) {
if ($pk === '_id') {
$model['_source']['_id'] = $model['_id'];
}
$model['_source']['_score'] = $model['_score'];
$models[$key] = $model['_source'];
}
}
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) { foreach ($models as $model) {
foreach ($models as $model) { $model->afterFind();
$model->afterFind();
}
} }
return $models; return $models;
...@@ -203,30 +178,34 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -203,30 +178,34 @@ class ActiveQuery extends Query implements ActiveQueryInterface
return null; return null;
} }
if ($this->asArray) { if ($this->asArray) {
/** @var ActiveRecord $modelClass */ // TODO implement with
$modelClass = $this->modelClass; // /** @var ActiveRecord $modelClass */
$model = $result['_source']; // $modelClass = $this->modelClass;
$pk = $modelClass::primaryKey()[0]; // $model = $result['_source'];
if ($pk === '_id') { // $pk = $modelClass::primaryKey()[0];
$model['_id'] = $result['_id']; // if ($pk === '_id') {
} // $model['_id'] = $result['_id'];
$model['_score'] = $result['_score']; // }
// $model['_score'] = $result['_score'];
// if (!empty($this->with)) {
// $models = [$model];
// $this->findWith($this->with, $models);
// $model = $models[0];
// }
return $result;
} else { } else {
/** @var ActiveRecord $class */ /** @var ActiveRecord $class */
$class = $this->modelClass; $class = $this->modelClass;
$model = $class::instantiate($result); $model = $class::instantiate($result);
$class::populateRecord($model, $result); $class::populateRecord($model, $result);
} if (!empty($this->with)) {
if (!empty($this->with)) { $models = [$model];
$models = [$model]; $this->findWith($this->with, $models);
$this->findWith($this->with, $models); $model = $models[0];
$model = $models[0]; }
}
if (!$this->asArray) {
$model->afterFind(); $model->afterFind();
return $model;
} }
return $model;
} }
/** /**
...@@ -235,27 +214,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -235,27 +214,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface
public function search($db = null, $options = []) public function search($db = null, $options = [])
{ {
$result = $this->createCommand($db)->search($options); $result = $this->createCommand($db)->search($options);
if (!empty($result['hits']['hits'])) { // TODO implement with for asArray
if (!empty($result['hits']['hits']) && !$this->asArray) {
$models = $this->createModels($result['hits']['hits']); $models = $this->createModels($result['hits']['hits']);
if ($this->asArray) {
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
$pk = $modelClass::primaryKey()[0];
foreach ($models as $key => $model) {
if ($pk === '_id') {
$model['_source']['_id'] = $model['_id'];
}
$model['_source']['_score'] = $model['_score'];
$models[$key] = $model['_source'];
}
}
if (!empty($this->with)) { if (!empty($this->with)) {
$this->findWith($this->with, $models); $this->findWith($this->with, $models);
} }
if (!$this->asArray) { foreach ($models as $model) {
foreach ($models as $model) { $model->afterFind();
$model->afterFind();
}
} }
$result['hits']['hits'] = $models; $result['hits']['hits'] = $models;
} }
...@@ -266,28 +232,12 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -266,28 +232,12 @@ class ActiveQuery extends Query implements ActiveQueryInterface
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function scalar($field, $db = null)
{
$record = parent::one($db);
if ($record !== false) {
if ($field == '_id') {
return $record['_id'];
} elseif (isset($record['_source'][$field])) {
return $record['_source'][$field];
}
}
return null;
}
/**
* @inheritdoc
*/
public function column($field, $db = null) public function column($field, $db = null)
{ {
if ($field == '_id') { if ($field == '_id') {
$command = $this->createCommand($db); $command = $this->createCommand($db);
$command->queryParts['fields'] = []; $command->queryParts['fields'] = [];
$command->queryParts['_source'] = false;
$result = $command->search(); $result = $command->search();
if (empty($result['hits']['hits'])) { if (empty($result['hits']['hits'])) {
return []; return [];
......
...@@ -114,7 +114,7 @@ class ActiveRecord extends BaseActiveRecord ...@@ -114,7 +114,7 @@ class ActiveRecord extends BaseActiveRecord
} }
$command = static::getDb()->createCommand(); $command = static::getDb()->createCommand();
$result = $command->get(static::index(), static::type(), $primaryKey, $options); $result = $command->get(static::index(), static::type(), $primaryKey, $options);
if ($result['exists']) { if ($result['found']) {
$model = static::instantiate($result); $model = static::instantiate($result);
static::populateRecord($model, $result); static::populateRecord($model, $result);
$model->afterFind(); $model->afterFind();
...@@ -150,7 +150,7 @@ class ActiveRecord extends BaseActiveRecord ...@@ -150,7 +150,7 @@ class ActiveRecord extends BaseActiveRecord
$result = $command->mget(static::index(), static::type(), $primaryKeys, $options); $result = $command->mget(static::index(), static::type(), $primaryKeys, $options);
$models = []; $models = [];
foreach ($result['docs'] as $doc) { foreach ($result['docs'] as $doc) {
if ($doc['exists']) { if ($doc['found']) {
$model = static::instantiate($doc); $model = static::instantiate($doc);
static::populateRecord($model, $doc); static::populateRecord($model, $doc);
$model->afterFind(); $model->afterFind();
...@@ -282,8 +282,23 @@ class ActiveRecord extends BaseActiveRecord ...@@ -282,8 +282,23 @@ class ActiveRecord extends BaseActiveRecord
*/ */
public static function populateRecord($record, $row) public static function populateRecord($record, $row)
{ {
parent::populateRecord($record, $row['_source']); $attributes = [];
$pk = static::primaryKey()[0]; if (isset($row['_source'])) {
$attributes = $row['_source'];
}
if (isset($row['fields'])) {
// reset fields in case it is scalar value TODO use field metadata for this
foreach($row['fields'] as $key => $value) {
if (count($value) == 1) {
$row['fields'][$key] = reset($value);
}
}
$attributes = array_merge($attributes, $row['fields']);
}
parent::populateRecord($record, $attributes);
$pk = static::primaryKey()[0];//TODO should always set ID in case of fields are not returned
if ($pk === '_id') { if ($pk === '_id') {
$record->_id = $row['_id']; $record->_id = $row['_id'];
} }
...@@ -379,9 +394,9 @@ class ActiveRecord extends BaseActiveRecord ...@@ -379,9 +394,9 @@ class ActiveRecord extends BaseActiveRecord
$options $options
); );
if (!isset($response['ok'])) { // if (!isset($response['ok'])) {
return false; // return false;
} // }
$pk = static::primaryKey()[0]; $pk = static::primaryKey()[0];
$this->$pk = $response['_id']; $this->$pk = $response['_id'];
if ($pk != '_id') { if ($pk != '_id') {
...@@ -444,13 +459,13 @@ class ActiveRecord extends BaseActiveRecord ...@@ -444,13 +459,13 @@ class ActiveRecord extends BaseActiveRecord
$n = 0; $n = 0;
$errors = []; $errors = [];
foreach ($response['items'] as $item) { foreach ($response['items'] as $item) {
if (isset($item['update']['error'])) { if (isset($item['update']['status']) && $item['update']['status'] == 200) {
$errors[] = $item['update'];
} elseif ($item['update']['ok']) {
$n++; $n++;
} else {
$errors[] = $item['update'];
} }
} }
if (!empty($errors)) { if (!empty($errors) || isset($response['errors']) && $response['errors']) {
throw new Exception(__METHOD__ . ' failed updating records.', $errors); throw new Exception(__METHOD__ . ' failed updating records.', $errors);
} }
...@@ -508,13 +523,13 @@ class ActiveRecord extends BaseActiveRecord ...@@ -508,13 +523,13 @@ class ActiveRecord extends BaseActiveRecord
$n = 0; $n = 0;
$errors = []; $errors = [];
foreach ($response['items'] as $item) { foreach ($response['items'] as $item) {
if (isset($item['update']['error'])) { if (isset($item['update']['status']) && $item['update']['status'] == 200) {
$errors[] = $item['update'];
} elseif ($item['update']['ok']) {
$n++; $n++;
} else {
$errors[] = $item['update'];
} }
} }
if (!empty($errors)) { if (!empty($errors) || isset($response['errors']) && $response['errors']) {
throw new Exception(__METHOD__ . ' failed updating records counters.', $errors); throw new Exception(__METHOD__ . ' failed updating records counters.', $errors);
} }
...@@ -563,13 +578,15 @@ class ActiveRecord extends BaseActiveRecord ...@@ -563,13 +578,15 @@ class ActiveRecord extends BaseActiveRecord
$n = 0; $n = 0;
$errors = []; $errors = [];
foreach ($response['items'] as $item) { foreach ($response['items'] as $item) {
if (isset($item['delete']['error'])) { if (isset($item['delete']['status']) && $item['delete']['status'] == 200) {
if (isset($item['delete']['found']) && $item['delete']['found']) {
$n++;
}
} else {
$errors[] = $item['delete']; $errors[] = $item['delete'];
} elseif ($item['delete']['found'] && $item['delete']['ok']) {
$n++;
} }
} }
if (!empty($errors)) { if (!empty($errors) || isset($response['errors']) && $response['errors']) {
throw new Exception(__METHOD__ . ' failed deleting records.', $errors); throw new Exception(__METHOD__ . ' failed deleting records.', $errors);
} }
......
...@@ -4,7 +4,9 @@ Yii Framework 2 elasticsearch extension Change Log ...@@ -4,7 +4,9 @@ Yii Framework 2 elasticsearch extension Change Log
2.0.0-rc under development 2.0.0-rc under development
-------------------------- --------------------------
- no changes in this release. - Chg: asArray in ActiveQuery is now equal to using the normal Query. This means, that the output structure has changed and `with` is supported anymore. (cebe)
- Chg: Deletion of a record is now also considered successfull if the record did not exist. (cebe)
- Chg: Requirement changes: Yii now requires elasticsearch version 1.0 or higher (cebe)
2.0.0-beta April 13, 2014 2.0.0-beta April 13, 2014
......
...@@ -315,7 +315,7 @@ class Command extends Component ...@@ -315,7 +315,7 @@ class Command extends Component
{ {
$body = $mapping !== null ? (is_string($mapping) ? $mapping : Json::encode($mapping)) : null; $body = $mapping !== null ? (is_string($mapping) ? $mapping : Json::encode($mapping)) : null;
return $this->db->put([$index, $type, '_mapping'], $options, $body); return $this->db->put([$index, '_mapping', $type], $options, $body);
} }
/** /**
...@@ -326,7 +326,7 @@ class Command extends Component ...@@ -326,7 +326,7 @@ class Command extends Component
*/ */
public function getMapping($index = '_all', $type = '_all') public function getMapping($index = '_all', $type = '_all')
{ {
return $this->db->get([$index, $type, '_mapping']); return $this->db->get([$index, '_mapping', $type]);
} }
/** /**
...@@ -337,7 +337,7 @@ class Command extends Component ...@@ -337,7 +337,7 @@ class Command extends Component
*/ */
public function deleteMapping($index, $type) public function deleteMapping($index, $type)
{ {
return $this->db->delete([$index, $type]); return $this->db->delete([$index, '_mapping', $type]);
} }
/** /**
...@@ -346,10 +346,11 @@ class Command extends Component ...@@ -346,10 +346,11 @@ class Command extends Component
* @return mixed * @return mixed
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html
*/ */
public function getFieldMapping($index, $type = '_all') // public function getFieldMapping($index, $type = '_all')
{ // {
return $this->db->put([$index, $type, '_mapping']); // // TODO implement
} // return $this->db->put([$index, $type, '_mapping']);
// }
/** /**
* @param $options * @param $options
......
...@@ -109,7 +109,7 @@ class Connection extends Component ...@@ -109,7 +109,7 @@ class Connection extends Component
if (strncmp($host, 'inet[/', 6) == 0) { if (strncmp($host, 'inet[/', 6) == 0) {
$host = substr($host, 6, -1); $host = substr($host, 6, -1);
} }
$response = $this->httpRequest('GET', 'http://' . $host . '/_cluster/nodes'); $response = $this->httpRequest('GET', 'http://' . $host . '/_nodes');
$this->nodes = $response['nodes']; $this->nodes = $response['nodes'];
if (empty($this->nodes)) { if (empty($this->nodes)) {
throw new Exception('cluster autodetection did not find any active node.'); throw new Exception('cluster autodetection did not find any active node.');
......
...@@ -58,13 +58,52 @@ class Query extends Component implements QueryInterface ...@@ -58,13 +58,52 @@ class Query extends Component implements QueryInterface
/** /**
* @var array the fields being retrieved from the documents. For example, `['id', 'name']`. * @var array the fields being retrieved from the documents. For example, `['id', 'name']`.
* If not set, it means retrieving all fields. An empty array will result in no fields being * If not set, this option will not be applied to the query and no fields will be returned.
* retrieved. This means that only the primaryKey of a record will be available in the result. * In this case the `_source` field will be returned by default which can be configured using [[source]].
* Setting this to an empty array will result in no fields being retrieved, which means that only the primaryKey
* of a record will be available in the result.
*
* For each field you may also add an array representing a [script field]. Example:
*
* ```php
* $query->fields = [
* 'id',
* 'name',
* 'value_times_two' => [
* 'script' => "doc['my_field_name'].value * 2",
* ],
* 'value_times_factor' => [
* 'script' => "doc['my_field_name'].value * factor",
* 'params' => [
* 'factor' => 2.0
* ],
* ],
* ]
* ```
*
* > Note: Field values are [always returned as arrays] even if they only have one value.
*
* [always returned as arrays]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.x/_return_values.html#_return_values
* [script field]: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-script-fields.html
*
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-fields.html#search-request-fields * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-fields.html#search-request-fields
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-script-fields.html
* @see fields() * @see fields()
* @see source
*/ */
public $fields; public $fields;
/** /**
* @var array this option controls how the `_source` field is returned from the documents. For example, `['id', 'name']`
* means that only the `id` and `name` field should be returned from `_source`.
* If not set, it means retrieving the full `_source` field unless [[fields]] are specified.
* Setting this option to `false` will disable return of the `_source` field, this means that only the primaryKey
* of a record will be available in the result.
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-source-filtering.html
* @see source()
* @see fields
*/
public $source;
/**
* @var string|array The index to retrieve data from. This can be a string representing a single index * @var string|array The index to retrieve data from. This can be a string representing a single index
* or a an array of multiple indexes. If this is not set, indexes are being queried. * or a an array of multiple indexes. If this is not set, indexes are being queried.
* @see from() * @see from()
...@@ -137,25 +176,20 @@ class Query extends Component implements QueryInterface ...@@ -137,25 +176,20 @@ class Query extends Component implements QueryInterface
return []; return [];
} }
$rows = $result['hits']['hits']; $rows = $result['hits']['hits'];
if ($this->indexBy === null && $this->fields === null) { if ($this->indexBy === null) {
return $rows; return $rows;
} }
$models = []; $models = [];
foreach ($rows as $key => $row) { foreach ($rows as $key => $row) {
if ($this->fields !== null) {
$row['_source'] = isset($row['fields']) ? $row['fields'] : [];
unset($row['fields']);
}
if ($this->indexBy !== null) { if ($this->indexBy !== null) {
if (is_string($this->indexBy)) { if (is_string($this->indexBy)) {
$key = $row['_source'][$this->indexBy]; $key = isset($row['fields'][$this->indexBy]) ? reset($row['fields'][$this->indexBy]) : $row['_source'][$this->indexBy];
} else { } else {
$key = call_user_func($this->indexBy, $row); $key = call_user_func($this->indexBy, $row);
} }
} }
$models[$key] = $row; $models[$key] = $row;
} }
return $models; return $models;
} }
...@@ -173,10 +207,6 @@ class Query extends Component implements QueryInterface ...@@ -173,10 +207,6 @@ class Query extends Component implements QueryInterface
return false; return false;
} }
$record = reset($result['hits']['hits']); $record = reset($result['hits']['hits']);
if ($this->fields !== null) {
$record['_source'] = isset($record['fields']) ? $record['fields'] : [];
unset($record['fields']);
}
return $record; return $record;
} }
...@@ -195,25 +225,18 @@ class Query extends Component implements QueryInterface ...@@ -195,25 +225,18 @@ class Query extends Component implements QueryInterface
public function search($db = null, $options = []) public function search($db = null, $options = [])
{ {
$result = $this->createCommand($db)->search($options); $result = $this->createCommand($db)->search($options);
if (!empty($result['hits']['hits']) && ($this->indexBy === null || $this->fields === null)) { if (!empty($result['hits']['hits']) && $this->indexBy !== null) {
$rows = []; $rows = [];
foreach ($result['hits']['hits'] as $key => $row) { foreach ($result['hits']['hits'] as $key => $row) {
if ($this->fields !== null) { if (is_string($this->indexBy)) {
$row['_source'] = isset($row['fields']) ? $row['fields'] : []; $key = isset($row['fields'][$this->indexBy]) ? $row['fields'][$this->indexBy] : $row['_source'][$this->indexBy];
unset($row['fields']); } else {
} $key = call_user_func($this->indexBy, $row);
if ($this->indexBy !== null) {
if (is_string($this->indexBy)) {
$key = $row['_source'][$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
} }
$rows[$key] = $row; $rows[$key] = $row;
} }
$result['hits']['hits'] = $rows; $result['hits']['hits'] = $rows;
} }
return $result; return $result;
} }
...@@ -247,12 +270,17 @@ class Query extends Component implements QueryInterface ...@@ -247,12 +270,17 @@ class Query extends Component implements QueryInterface
*/ */
public function scalar($field, $db = null) public function scalar($field, $db = null)
{ {
$record = self::one($db); // TODO limit fields to the one required $record = self::one($db);
if ($record !== false && isset($record['_source'][$field])) { if ($record !== false) {
return $record['_source'][$field]; if ($field === '_id') {
} else { return $record['_id'];
return null; } elseif (isset($record['_source'][$field])) {
return $record['_source'][$field];
} elseif (isset($record['fields'][$field])) {
return count($record['fields'][$field]) == 1 ? reset($record['fields'][$field]) : $record['fields'][$field];
}
} }
return null;
} }
/** /**
...@@ -265,14 +293,14 @@ class Query extends Component implements QueryInterface ...@@ -265,14 +293,14 @@ class Query extends Component implements QueryInterface
public function column($field, $db = null) public function column($field, $db = null)
{ {
$command = $this->createCommand($db); $command = $this->createCommand($db);
$command->queryParts['fields'] = [$field]; $command->queryParts['_source'] = [$field];
$result = $command->search(); $result = $command->search();
if (empty($result['hits']['hits'])) { if (empty($result['hits']['hits'])) {
return []; return [];
} }
$column = []; $column = [];
foreach ($result['hits']['hits'] as $row) { foreach ($result['hits']['hits'] as $row) {
$column[] = isset($row['fields'][$field]) ? $row['fields'][$field] : null; $column[] = isset($row['_source'][$field]) ? $row['_source'][$field] : null;
} }
return $column; return $column;
} }
...@@ -498,6 +526,22 @@ class Query extends Component implements QueryInterface ...@@ -498,6 +526,22 @@ class Query extends Component implements QueryInterface
} }
/** /**
* Sets the source filtering, specifying how the `_source` field of the document should be returned.
* @param array $source the source patterns to be selected.
* @return static the query object itself
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-source-filtering.html
*/
public function source($source)
{
if (is_array($source) || $source === null) {
$this->source = $source;
} else {
$this->source = func_get_args();
}
return $this;
}
/**
* Sets the search timeout. * Sets the search timeout.
* @param integer $timeout A search timeout, bounding the search request to be executed within the specified time value * @param integer $timeout A search timeout, bounding the search request to be executed within the specified time value
* and bail with the hits accumulated up to that point when expired. Defaults to no timeout. * and bail with the hits accumulated up to that point when expired. Defaults to no timeout.
......
...@@ -45,8 +45,27 @@ class QueryBuilder extends \yii\base\Object ...@@ -45,8 +45,27 @@ class QueryBuilder extends \yii\base\Object
{ {
$parts = []; $parts = [];
if ($query->fields !== null) { if ($query->fields === []) {
$parts['fields'] = (array) $query->fields; $parts['fields'] = [];
} elseif ($query->fields !== null) {
$fields = [];
$scriptFields = [];
foreach($query->fields as $key => $field) {
if (is_int($key)) {
$fields[] = $field;
} else {
$scriptFields[$key] = $field;
}
}
if (!empty($fields)) {
$parts['fields'] = $fields;
}
if (!empty($scriptFields)) {
$parts['script_fields'] = $scriptFields;
}
}
if ($query->source !== null) {
$parts['_source'] = $query->source;
} }
if ($query->limit !== null && $query->limit >= 0) { if ($query->limit !== null && $query->limit >= 0) {
$parts['size'] = $query->limit; $parts['size'] = $query->limit;
......
...@@ -22,6 +22,10 @@ return [ ...@@ -22,6 +22,10 @@ return [
]; ];
``` ```
Requirements
------------
elasticsearch version 1.0 or higher is required.
Installation Installation
------------ ------------
......
...@@ -11,7 +11,7 @@ $this->title = 'Welcome to Gii'; ...@@ -11,7 +11,7 @@ $this->title = 'Welcome to Gii';
?> ?>
<div class="default-index"> <div class="default-index">
<div class="page-header"> <div class="page-header">
<h1>Welcome to Gii <small>a magic tool that can write code for you</small></h1> <h1>Welcome to Gii <small>a magical tool that can write code for you</small></h1>
</div> </div>
<p class="lead">Start the fun with the following code generators:</p> <p class="lead">Start the fun with the following code generators:</p>
......
...@@ -4,7 +4,7 @@ Yii Framework 2 mongodb extension Change Log ...@@ -4,7 +4,7 @@ Yii Framework 2 mongodb extension Change Log
2.0.0-rc under development 2.0.0-rc under development
-------------------------- --------------------------
- no changes in this release. - Bug #3385: Fixed "The 'connected' property is deprecated" (samdark)
2.0.0-beta April 13, 2014 2.0.0-beta April 13, 2014
......
...@@ -219,7 +219,7 @@ class Connection extends Component ...@@ -219,7 +219,7 @@ class Connection extends Component
*/ */
public function getIsActive() public function getIsActive()
{ {
return is_object($this->mongoClient) && $this->mongoClient->connected; return is_object($this->mongoClient) && $this->mongoClient->getConnections() != [];
} }
/** /**
......
...@@ -25,6 +25,7 @@ Yii Framework 2 Change Log ...@@ -25,6 +25,7 @@ Yii Framework 2 Change Log
- Bug #3311: Fixed the bug that `yii\di\Container::has()` did not return correct value (mgrechanik, qiangxue) - Bug #3311: Fixed the bug that `yii\di\Container::has()` did not return correct value (mgrechanik, qiangxue)
- Bug #3327: Fixed "Unable to find debug data" when logging objects with circular references (jarekkozak, samdark) - Bug #3327: Fixed "Unable to find debug data" when logging objects with circular references (jarekkozak, samdark)
- Bug #3368: Fix for comparing numeric attributes in JavaScript (technixp) - Bug #3368: Fix for comparing numeric attributes in JavaScript (technixp)
- Bug: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark)
- Enh #2264: `CookieCollection::has()` will return false for expired or removed cookies (qiangxue) - Enh #2264: `CookieCollection::has()` will return false for expired or removed cookies (qiangxue)
- Enh #2435: `yii\db\IntegrityException` is now thrown on database integrity errors instead of general `yii\db\Exception` (samdark) - Enh #2435: `yii\db\IntegrityException` is now thrown on database integrity errors instead of general `yii\db\Exception` (samdark)
- Enh #2837: Error page now shows arguments in stack trace method calls (samdark) - Enh #2837: Error page now shows arguments in stack trace method calls (samdark)
...@@ -45,9 +46,12 @@ Yii Framework 2 Change Log ...@@ -45,9 +46,12 @@ Yii Framework 2 Change Log
- Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue) - Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)
- Enh: Added `yii\web\UrlManager::addRules()` to simplify adding new URL rules (qiangxue) - Enh: Added `yii\web\UrlManager::addRules()` to simplify adding new URL rules (qiangxue)
- Enh: Added support to insert an event handler at the beginning of class-level event handler queue (qiangxue) - Enh: Added support to insert an event handler at the beginning of class-level event handler queue (qiangxue)
- Enh: Added `yii\console\Controller::EXIT_CODE_NORMAL` and `yii\console\Controller::EXIT_CODE_ERROR` constants (samdark)
- Enh: `yii\console\MigrateController` now returns `yii\console\Controller::EXIT_CODE_ERROR` in case of failed migration (samdark)
- Chg #2913: RBAC `DbManager` is now initialized via migration (samdark) - Chg #2913: RBAC `DbManager` is now initialized via migration (samdark)
- Chg #3036: Upgraded Twitter Bootstrap to 3.1.x (qiangxue) - Chg #3036: Upgraded Twitter Bootstrap to 3.1.x (qiangxue)
- Chg #3175: InvalidCallException, InvalidParamException, UnknownMethodException are now extended from SPL BadMethodCallException (samdark) - Chg #3175: InvalidCallException, InvalidParamException, UnknownMethodException are now extended from SPL BadMethodCallException (samdark)
- Chg #3383: Added `$type` parameter to `IdentityInterface::findIdentityByAccessToken()` (qiangxue)
- Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\rbac\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue) - Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\rbac\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue)
- Chg: Added `$user` as the first parameter of `yii\rbac\Rule::execute()` (qiangxue) - Chg: Added `$user` as the first parameter of `yii\rbac\Rule::execute()` (qiangxue)
- Chg: `yii\grid\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe) - Chg: `yii\grid\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe)
......
...@@ -21,3 +21,11 @@ Upgrade from Yii 2.0 Beta ...@@ -21,3 +21,11 @@ Upgrade from Yii 2.0 Beta
* If you override `yii\grid\DataColumn::getDataCellValue()` with visibility `protected` you have * If you override `yii\grid\DataColumn::getDataCellValue()` with visibility `protected` you have
to change visibility to `public` as visibility of the base method has changed. to change visibility to `public` as visibility of the base method has changed.
* If you have classes implementing `yii\web\IdentityInterface` (very common), you should modify
the signature of `findIdentityByAccessToken()` as
`public static function findIdentityByAccessToken($token, $type = null)`. The new `$type` parameter
will contain the type information about the access token. For example, if you use
`yii\filters\auth\HttpBearerAuth` authentication method, the value of this parameter will be
`yii\filters\auth\HttpBearerAuth`. This allows you to differentiate access tokens taken by
different authentication methods.
...@@ -77,6 +77,7 @@ use Yii; ...@@ -77,6 +77,7 @@ use Yii;
class Object class Object
{ {
/** /**
* Returns the fully qualified name of this class.
* @return string the fully qualified name of this class. * @return string the fully qualified name of this class.
*/ */
public static function className() public static function className()
......
...@@ -54,7 +54,7 @@ class Widget extends Component implements ViewContextInterface ...@@ -54,7 +54,7 @@ class Widget extends Component implements ViewContextInterface
$config['class'] = get_called_class(); $config['class'] = get_called_class();
/** @var Widget $widget */ /** @var Widget $widget */
$widget = Yii::createObject($config); $widget = Yii::createObject($config);
self::$stack[] = $widget; static::$stack[] = $widget;
return $widget; return $widget;
} }
...@@ -67,8 +67,8 @@ class Widget extends Component implements ViewContextInterface ...@@ -67,8 +67,8 @@ class Widget extends Component implements ViewContextInterface
*/ */
public static function end() public static function end()
{ {
if (!empty(self::$stack)) { if (!empty(static::$stack)) {
$widget = array_pop(self::$stack); $widget = array_pop(static::$stack);
if (get_class($widget) === get_called_class()) { if (get_class($widget) === get_called_class()) {
echo $widget->run(); echo $widget->run();
return $widget; return $widget;
...@@ -108,7 +108,7 @@ class Widget extends Component implements ViewContextInterface ...@@ -108,7 +108,7 @@ class Widget extends Component implements ViewContextInterface
public function getId($autoGenerate = true) public function getId($autoGenerate = true)
{ {
if ($autoGenerate && $this->_id === null) { if ($autoGenerate && $this->_id === null) {
$this->_id = self::$autoIdPrefix . self::$counter++; $this->_id = static::$autoIdPrefix . static::$counter++;
} }
return $this->_id; return $this->_id;
......
...@@ -138,7 +138,7 @@ class Application extends \yii\base\Application ...@@ -138,7 +138,7 @@ class Application extends \yii\base\Application
return $result; return $result;
} else { } else {
$response = $this->getResponse(); $response = $this->getResponse();
$response->exitStatus = (int) $result; $response->exitStatus = $result;
return $response; return $response;
} }
...@@ -157,7 +157,7 @@ class Application extends \yii\base\Application ...@@ -157,7 +157,7 @@ class Application extends \yii\base\Application
public function runAction($route, $params = []) public function runAction($route, $params = [])
{ {
try { try {
return parent::runAction($route, $params); return (int)parent::runAction($route, $params);
} catch (InvalidRouteException $e) { } catch (InvalidRouteException $e) {
throw new Exception(Yii::t('yii', 'Unknown command "{command}".', ['command' => $route]), 0, $e); throw new Exception(Yii::t('yii', 'Unknown command "{command}".', ['command' => $route]), 0, $e);
} }
......
...@@ -29,6 +29,9 @@ use yii\helpers\Console; ...@@ -29,6 +29,9 @@ use yii\helpers\Console;
*/ */
class Controller extends \yii\base\Controller class Controller extends \yii\base\Controller
{ {
const EXIT_CODE_NORMAL = 0;
const EXIT_CODE_ERROR = 1;
/** /**
* @var boolean whether to run the command interactively. * @var boolean whether to run the command interactively.
*/ */
......
...@@ -611,7 +611,7 @@ return [ ...@@ -611,7 +611,7 @@ return [
EOD; EOD;
if (file_exists($configFile)) { if (file_exists($configFile)) {
if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) { if (!$this->confirm("File '{$configFile}' already exists. Do you wish to overwrite it?")) {
return; return self::EXIT_CODE_NORMAL;
} }
} }
if (!file_put_contents($configFile, $template)) { if (!file_put_contents($configFile, $template)) {
......
...@@ -96,7 +96,7 @@ class FixtureController extends Controller ...@@ -96,7 +96,7 @@ class FixtureController extends Controller
} }
if (!$this->confirmLoad($foundFixtures, $except)) { if (!$this->confirmLoad($foundFixtures, $except)) {
return; return self::EXIT_CODE_NORMAL;
} }
$filtered = array_diff($foundFixtures, $except); $filtered = array_diff($foundFixtures, $except);
...@@ -140,7 +140,7 @@ class FixtureController extends Controller ...@@ -140,7 +140,7 @@ class FixtureController extends Controller
} }
if (!$this->confirmUnload($foundFixtures, $except)) { if (!$this->confirmUnload($foundFixtures, $except)) {
return; return self::EXIT_CODE_NORMAL;
} }
$filtered = array_diff($foundFixtures, $except); $filtered = array_diff($foundFixtures, $except);
......
...@@ -54,7 +54,7 @@ class MessageController extends Controller ...@@ -54,7 +54,7 @@ class MessageController extends Controller
$filePath = Yii::getAlias($filePath); $filePath = Yii::getAlias($filePath);
if (file_exists($filePath)) { if (file_exists($filePath)) {
if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) { if (!$this->confirm("File '{$filePath}' already exists. Do you wish to overwrite it?")) {
return; return self::EXIT_CODE_NORMAL;
} }
} }
copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath); copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath);
...@@ -298,7 +298,7 @@ class MessageController extends Controller ...@@ -298,7 +298,7 @@ class MessageController extends Controller
if (array_keys($translated) == $messages) { if (array_keys($translated) == $messages) {
echo "nothing new...skipped.\n"; echo "nothing new...skipped.\n";
return; return self::EXIT_CODE_NORMAL;
} }
$merged = []; $merged = [];
$untranslated = []; $untranslated = [];
......
...@@ -81,10 +81,6 @@ class MigrateController extends Controller ...@@ -81,10 +81,6 @@ class MigrateController extends Controller
*/ */
public $templateFile = '@yii/views/migration.php'; public $templateFile = '@yii/views/migration.php';
/** /**
* @var boolean whether to execute the migration in an interactive mode.
*/
public $interactive = true;
/**
* @var Connection|string the DB connection object or the application * @var Connection|string the DB connection object or the application
* component ID of the DB connection. * component ID of the DB connection.
*/ */
...@@ -148,6 +144,8 @@ class MigrateController extends Controller ...@@ -148,6 +144,8 @@ class MigrateController extends Controller
* *
* @param integer $limit the number of new migrations to be applied. If 0, it means * @param integer $limit the number of new migrations to be applied. If 0, it means
* applying all available new migrations. * applying all available new migrations.
*
* @return integer the status of the action execution. 0 means normal, other values mean abnormal.
*/ */
public function actionUp($limit = 0) public function actionUp($limit = 0)
{ {
...@@ -155,7 +153,7 @@ class MigrateController extends Controller ...@@ -155,7 +153,7 @@ class MigrateController extends Controller
if (empty($migrations)) { if (empty($migrations)) {
echo "No new migration found. Your system is up-to-date.\n"; echo "No new migration found. Your system is up-to-date.\n";
return; return self::EXIT_CODE_NORMAL;
} }
$total = count($migrations); $total = count($migrations);
...@@ -181,7 +179,7 @@ class MigrateController extends Controller ...@@ -181,7 +179,7 @@ class MigrateController extends Controller
if (!$this->migrateUp($migration)) { if (!$this->migrateUp($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return self::EXIT_CODE_ERROR;
} }
} }
echo "\nMigrated up successfully.\n"; echo "\nMigrated up successfully.\n";
...@@ -200,6 +198,8 @@ class MigrateController extends Controller ...@@ -200,6 +198,8 @@ class MigrateController extends Controller
* @param integer $limit the number of migrations to be reverted. Defaults to 1, * @param integer $limit the number of migrations to be reverted. Defaults to 1,
* meaning the last applied migration will be reverted. * meaning the last applied migration will be reverted.
* @throws Exception if the number of the steps specified is less than 1. * @throws Exception if the number of the steps specified is less than 1.
*
* @return integer the status of the action execution. 0 means normal, other values mean abnormal.
*/ */
public function actionDown($limit = 1) public function actionDown($limit = 1)
{ {
...@@ -212,7 +212,7 @@ class MigrateController extends Controller ...@@ -212,7 +212,7 @@ class MigrateController extends Controller
if (empty($migrations)) { if (empty($migrations)) {
echo "No migration has been done before.\n"; echo "No migration has been done before.\n";
return; return self::EXIT_CODE_NORMAL;
} }
$migrations = array_keys($migrations); $migrations = array_keys($migrations);
...@@ -228,7 +228,7 @@ class MigrateController extends Controller ...@@ -228,7 +228,7 @@ class MigrateController extends Controller
if (!$this->migrateDown($migration)) { if (!$this->migrateDown($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return self::EXIT_CODE_ERROR;
} }
} }
echo "\nMigrated down successfully.\n"; echo "\nMigrated down successfully.\n";
...@@ -249,6 +249,8 @@ class MigrateController extends Controller ...@@ -249,6 +249,8 @@ class MigrateController extends Controller
* @param integer $limit the number of migrations to be redone. Defaults to 1, * @param integer $limit the number of migrations to be redone. Defaults to 1,
* meaning the last applied migration will be redone. * meaning the last applied migration will be redone.
* @throws Exception if the number of the steps specified is less than 1. * @throws Exception if the number of the steps specified is less than 1.
*
* @return integer the status of the action execution. 0 means normal, other values mean abnormal.
*/ */
public function actionRedo($limit = 1) public function actionRedo($limit = 1)
{ {
...@@ -261,7 +263,7 @@ class MigrateController extends Controller ...@@ -261,7 +263,7 @@ class MigrateController extends Controller
if (empty($migrations)) { if (empty($migrations)) {
echo "No migration has been done before.\n"; echo "No migration has been done before.\n";
return; return self::EXIT_CODE_NORMAL;
} }
$migrations = array_keys($migrations); $migrations = array_keys($migrations);
...@@ -277,14 +279,14 @@ class MigrateController extends Controller ...@@ -277,14 +279,14 @@ class MigrateController extends Controller
if (!$this->migrateDown($migration)) { if (!$this->migrateDown($migration)) {
echo "\nMigration failed. The rest of the migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations are canceled.\n";
return; return self::EXIT_CODE_ERROR;
} }
} }
foreach (array_reverse($migrations) as $migration) { foreach (array_reverse($migrations) as $migration) {
if (!$this->migrateUp($migration)) { if (!$this->migrateUp($migration)) {
echo "\nMigration failed. The rest of the migrations migrations are canceled.\n"; echo "\nMigration failed. The rest of the migrations migrations are canceled.\n";
return; return self::EXIT_CODE_ERROR;
} }
} }
echo "\nMigration redone successfully.\n"; echo "\nMigration redone successfully.\n";
...@@ -365,7 +367,7 @@ class MigrateController extends Controller ...@@ -365,7 +367,7 @@ class MigrateController extends Controller
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n"; echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
} }
return; return self::EXIT_CODE_NORMAL;
} }
} }
...@@ -387,7 +389,7 @@ class MigrateController extends Controller ...@@ -387,7 +389,7 @@ class MigrateController extends Controller
} }
} }
return; return self::EXIT_CODE_NORMAL;
} }
} }
...@@ -602,7 +604,7 @@ class MigrateController extends Controller ...@@ -602,7 +604,7 @@ class MigrateController extends Controller
if (strpos($migration, $version . '_') === 0) { if (strpos($migration, $version . '_') === 0) {
$this->actionUp($i + 1); $this->actionUp($i + 1);
return; return self::EXIT_CODE_NORMAL;
} }
} }
...@@ -616,7 +618,7 @@ class MigrateController extends Controller ...@@ -616,7 +618,7 @@ class MigrateController extends Controller
$this->actionDown($i); $this->actionDown($i);
} }
return; return self::EXIT_CODE_NORMAL;
} }
} }
......
...@@ -77,7 +77,7 @@ class HttpBasicAuth extends AuthMethod ...@@ -77,7 +77,7 @@ class HttpBasicAuth extends AuthMethod
return $identity; return $identity;
} }
} elseif ($username !== null) { } elseif ($username !== null) {
$identity = $user->loginByAccessToken($username); $identity = $user->loginByAccessToken($username, get_class($this));
if ($identity === null) { if ($identity === null) {
$this->handleFailure($response); $this->handleFailure($response);
} }
......
...@@ -43,7 +43,7 @@ class HttpBearerAuth extends AuthMethod ...@@ -43,7 +43,7 @@ class HttpBearerAuth extends AuthMethod
{ {
$authHeader = $request->getHeaders()->get('Authorization'); $authHeader = $request->getHeaders()->get('Authorization');
if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) { if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) {
$identity = $user->loginByAccessToken($matches[1]); $identity = $user->loginByAccessToken($matches[1], get_class($this));
if ($identity === null) { if ($identity === null) {
$this->handleFailure($response); $this->handleFailure($response);
} }
......
...@@ -30,7 +30,7 @@ class QueryParamAuth extends AuthMethod ...@@ -30,7 +30,7 @@ class QueryParamAuth extends AuthMethod
{ {
$accessToken = $request->get($this->tokenParam); $accessToken = $request->get($this->tokenParam);
if (is_string($accessToken)) { if (is_string($accessToken)) {
$identity = $user->loginByAccessToken($accessToken); $identity = $user->loginByAccessToken($accessToken, get_class($this));
if ($identity !== null) { if ($identity !== null) {
return $identity; return $identity;
} }
......
...@@ -98,9 +98,9 @@ class GettextMessageSource extends MessageSource ...@@ -98,9 +98,9 @@ class GettextMessageSource extends MessageSource
{ {
$messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog; $messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog;
if ($this->useMoFile) { if ($this->useMoFile) {
$messageFile .= static::MO_FILE_EXT; $messageFile .= self::MO_FILE_EXT;
} else { } else {
$messageFile .= static::PO_FILE_EXT; $messageFile .= self::PO_FILE_EXT;
} }
return $messageFile; return $messageFile;
......
...@@ -21,7 +21,7 @@ use yii\di\Instance; ...@@ -21,7 +21,7 @@ use yii\di\Instance;
* The database connection is specified by [[db]]. The database schema could be initialized by applying migration: * The database connection is specified by [[db]]. The database schema could be initialized by applying migration:
* *
* ``` * ```
* yii migrate --migrationPath=/vendor/yiisoft/yii2/rbac/migrations/ * yii migrate --migrationPath=@yii/rbac/migrations/
* ``` * ```
* *
* You may change the names of the three tables used to store the authorization data by setting [[itemTable]], * You may change the names of the three tables used to store the authorization data by setting [[itemTable]],
......
<?php <?php
use yii\base\InvalidConfigException;
use yii\db\Schema; use yii\db\Schema;
use yii\rbac\DbManager;
class m140506_102106_rbac_init extends \yii\db\Migration class m140506_102106_rbac_init extends \yii\db\Migration
{ {
/**
* @throws yii\base\InvalidConfigException
* @return DbManager
*/
protected function getAuthManager()
{
$authManager = Yii::$app->getAuthManager();
if (!$authManager instanceof DbManager) {
throw new InvalidConfigException('You should configure "authManager" component to use database before executing this migration.');
}
return $authManager;
}
public function up() public function up()
{ {
$authManager = $this->getAuthManager();
$tableOptions = null; $tableOptions = null;
if ($this->db->driverName === 'mysql') { if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB'; $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
} }
$this->createTable('{{%auth_rule}}', [ $this->createTable($authManager->ruleTable, [
'name' => Schema::TYPE_STRING . '(64) NOT NULL', 'name' => Schema::TYPE_STRING . '(64) NOT NULL',
'data' => Schema::TYPE_TEXT, 'data' => Schema::TYPE_TEXT,
'created_at' => Schema::TYPE_INTEGER, 'created_at' => Schema::TYPE_INTEGER,
'updated_at' => Schema::TYPE_INTEGER, 'updated_at' => Schema::TYPE_INTEGER,
'PRIMARY KEY (name)',
], $tableOptions); ], $tableOptions);
$this->addPrimaryKey('pk-auth_rule', '{{%auth_rule}}', 'name');
$this->createTable('{{%auth_item}}', [ $this->createTable($authManager->itemTable, [
'name' => Schema::TYPE_STRING . '(64) NOT NULL', 'name' => Schema::TYPE_STRING . '(64) NOT NULL',
'type' => Schema::TYPE_INTEGER . ' NOT NULL', 'type' => Schema::TYPE_INTEGER . ' NOT NULL',
'description' => Schema::TYPE_TEXT, 'description' => Schema::TYPE_TEXT,
...@@ -27,33 +44,35 @@ class m140506_102106_rbac_init extends \yii\db\Migration ...@@ -27,33 +44,35 @@ class m140506_102106_rbac_init extends \yii\db\Migration
'data' => Schema::TYPE_TEXT, 'data' => Schema::TYPE_TEXT,
'created_at' => Schema::TYPE_INTEGER, 'created_at' => Schema::TYPE_INTEGER,
'updated_at' => Schema::TYPE_INTEGER, 'updated_at' => Schema::TYPE_INTEGER,
'PRIMARY KEY (name)',
'FOREIGN KEY (rule_name) REFERENCES ' . $authManager->ruleTable . ' (name) ON DELETE SET NULL ON UPDATE CASCADE',
], $tableOptions); ], $tableOptions);
$this->addPrimaryKey('pk-auth_item', '{{%auth_item}}', 'name'); $this->createIndex('idx-auth_item-type', $authManager->itemTable, 'type');
$this->addForeignKey('fk-auth_item-rule_name', '{{%auth_item}}', 'rule_name', '{{%auth_rule}}', 'name', 'SET NULL', 'CASCADE');
$this->createIndex('idx-auth_item-type', '{{%auth_item}}', 'type');
$this->createTable('{{%auth_item_child}}', [ $this->createTable($authManager->itemChildTable, [
'parent' => Schema::TYPE_STRING . '(64) NOT NULL', 'parent' => Schema::TYPE_STRING . '(64) NOT NULL',
'child' => Schema::TYPE_STRING . '(64) NOT NULL', 'child' => Schema::TYPE_STRING . '(64) NOT NULL',
'PRIMARY KEY (parent, child)',
'FOREIGN KEY (parent) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE',
'FOREIGN KEY (child) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE',
], $tableOptions); ], $tableOptions);
$this->addPrimaryKey('pk-auth_item_child', '{{%auth_item_child}}', ['parent', 'child']);
$this->addForeignKey('fk-auth_item_child-parent', '{{%auth_item_child}}', 'parent', '{{%auth_item}}', 'name', 'CASCADE', 'CASCADE');
$this->addForeignKey('fk-auth_item_child-child', '{{%auth_item_child}}', 'child', '{{%auth_item}}', 'name', 'CASCADE', 'CASCADE');
$this->createTable('{{%auth_assignment}}', [ $this->createTable($authManager->assignmentTable, [
'item_name' => Schema::TYPE_STRING . '(64) NOT NULL', 'item_name' => Schema::TYPE_STRING . '(64) NOT NULL',
'user_id' => Schema::TYPE_STRING . '(64) NOT NULL', 'user_id' => Schema::TYPE_STRING . '(64) NOT NULL',
'created_at' => Schema::TYPE_INTEGER, 'created_at' => Schema::TYPE_INTEGER,
'PRIMARY KEY (item_name, user_id)',
'FOREIGN KEY (item_name) REFERENCES ' . $authManager->itemTable . ' (name) ON DELETE CASCADE ON UPDATE CASCADE',
], $tableOptions); ], $tableOptions);
$this->addPrimaryKey('pk-auth_assignment', '{{%auth_assignment}}', ['item_name', 'user_id']);
$this->addForeignKey('fk-auth_assignment-item_name', '{{%auth_assignment}}', 'item_name', '{{%auth_item}}', 'name', 'CASCADE', 'CASCADE');
} }
public function down() public function down()
{ {
$this->dropTable('{{%auth_assignment}}'); $authManager = $this->getAuthManager();
$this->dropTable('{{%auth_item_child}}');
$this->dropTable('{{%auth_item}}'); $this->dropTable($authManager->assignmentTable);
$this->dropTable('{{%auth_rule}}'); $this->dropTable($authManager->itemChildTable);
$this->dropTable($authManager->itemTable);
$this->dropTable($authManager->ruleTable);
} }
} }
...@@ -21,7 +21,7 @@ namespace yii\web; ...@@ -21,7 +21,7 @@ namespace yii\web;
* return static::findOne($id); * return static::findOne($id);
* } * }
* *
* public static function findIdentityByAccessToken($token) * public static function findIdentityByAccessToken($token, $type = null)
* { * {
* return static::findOne(['access_token' => $token]); * return static::findOne(['access_token' => $token]);
* } * }
...@@ -59,11 +59,13 @@ interface IdentityInterface ...@@ -59,11 +59,13 @@ interface IdentityInterface
/** /**
* Finds an identity by the given secrete token. * Finds an identity by the given secrete token.
* @param string $token the secrete token * @param string $token the secrete token
* @param mixed $type the type of the token. The value of this parameter depends on the implementation.
* For example, [[\yii\filters\auth\HttpBearerAuth]] will set this parameter to be `yii\filters\auth\HttpBearerAuth`.
* @return IdentityInterface the identity object that matches the given token. * @return IdentityInterface the identity object that matches the given token.
* Null should be returned if such an identity cannot be found * Null should be returned if such an identity cannot be found
* or the identity is not in an active state (disabled, deleted, etc.) * or the identity is not in an active state (disabled, deleted, etc.)
*/ */
public static function findIdentityByAccessToken($token); public static function findIdentityByAccessToken($token, $type = null);
/** /**
* Returns an ID that can uniquely identify a user identity. * Returns an ID that can uniquely identify a user identity.
* @return string|integer an ID that uniquely identifies a user identity. * @return string|integer an ID that uniquely identifies a user identity.
......
...@@ -216,14 +216,16 @@ class User extends Component ...@@ -216,14 +216,16 @@ class User extends Component
* Note that unlike [[login()]], this method will NOT start a session to remember the user authentication status. * Note that unlike [[login()]], this method will NOT start a session to remember the user authentication status.
* Also if the access token is invalid, the user will remain as a guest. * Also if the access token is invalid, the user will remain as a guest.
* @param string $token the access token * @param string $token the access token
* @param mixed $type the type of the token. The value of this parameter depends on the implementation.
* For example, [[\yii\filters\auth\HttpBearerAuth]] will set this parameter to be `yii\filters\auth\HttpBearerAuth`.
* @return IdentityInterface the identity associated with the given access token. Null is returned if * @return IdentityInterface the identity associated with the given access token. Null is returned if
* the access token is invalid. * the access token is invalid.
*/ */
public function loginByAccessToken($token) public function loginByAccessToken($token, $type = null)
{ {
/** @var IdentityInterface $class */ /** @var IdentityInterface $class */
$class = $this->identityClass; $class = $this->identityClass;
$identity = $class::findIdentityByAccessToken($token); $identity = $class::findIdentityByAccessToken($token, $type);
$this->setIdentity($identity); $this->setIdentity($identity);
return $identity; return $identity;
......
...@@ -27,7 +27,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase ...@@ -27,7 +27,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
* @param mixed $default default value to use when param is not set. * @param mixed $default default value to use when param is not set.
* @return mixed the value of the configuration param * @return mixed the value of the configuration param
*/ */
public function getParam($name, $default = null) public static function getParam($name, $default = null)
{ {
if (static::$params === null) { if (static::$params === null) {
static::$params = require(__DIR__ . '/data/config.php'); static::$params = require(__DIR__ . '/data/config.php');
......
...@@ -14,6 +14,8 @@ use yii\elasticsearch\Command; ...@@ -14,6 +14,8 @@ use yii\elasticsearch\Command;
*/ */
class OrderItem extends ActiveRecord class OrderItem extends ActiveRecord
{ {
public $total;
public function attributes() public function attributes()
{ {
return ['order_id', 'item_id', 'quantity', 'subtotal']; return ['order_id', 'item_id', 'quantity', 'subtotal'];
......
...@@ -151,8 +151,8 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -151,8 +151,8 @@ class ActiveRecordTest extends ElasticSearchTestCase
'name' => 'user2', 'name' => 'user2',
'address' => 'address2', 'address' => 'address2',
'status' => 1, 'status' => 1,
'_score' => 1.0 // '_score' => 1.0
], $customer); ], $customer['_source']);
} }
public function testSearch() public function testSearch()
...@@ -174,21 +174,21 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -174,21 +174,21 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertEquals(3, $result['total']); $this->assertEquals(3, $result['total']);
$customers = $result['hits']; $customers = $result['hits'];
$this->assertEquals(3, count($customers)); $this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]); $this->assertArrayHasKey('id', $customers[0]['_source']);
$this->assertArrayHasKey('name', $customers[0]); $this->assertArrayHasKey('name', $customers[0]['_source']);
$this->assertArrayHasKey('email', $customers[0]); $this->assertArrayHasKey('email', $customers[0]['_source']);
$this->assertArrayHasKey('address', $customers[0]); $this->assertArrayHasKey('address', $customers[0]['_source']);
$this->assertArrayHasKey('status', $customers[0]); $this->assertArrayHasKey('status', $customers[0]['_source']);
$this->assertArrayHasKey('id', $customers[1]); $this->assertArrayHasKey('id', $customers[1]['_source']);
$this->assertArrayHasKey('name', $customers[1]); $this->assertArrayHasKey('name', $customers[1]['_source']);
$this->assertArrayHasKey('email', $customers[1]); $this->assertArrayHasKey('email', $customers[1]['_source']);
$this->assertArrayHasKey('address', $customers[1]); $this->assertArrayHasKey('address', $customers[1]['_source']);
$this->assertArrayHasKey('status', $customers[1]); $this->assertArrayHasKey('status', $customers[1]['_source']);
$this->assertArrayHasKey('id', $customers[2]); $this->assertArrayHasKey('id', $customers[2]['_source']);
$this->assertArrayHasKey('name', $customers[2]); $this->assertArrayHasKey('name', $customers[2]['_source']);
$this->assertArrayHasKey('email', $customers[2]); $this->assertArrayHasKey('email', $customers[2]['_source']);
$this->assertArrayHasKey('address', $customers[2]); $this->assertArrayHasKey('address', $customers[2]['_source']);
$this->assertArrayHasKey('status', $customers[2]); $this->assertArrayHasKey('status', $customers[2]['_source']);
// TODO test asArray() + fields() + indexBy() // TODO test asArray() + fields() + indexBy()
...@@ -387,35 +387,67 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -387,35 +387,67 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this->assertEquals(2, count($customers)); $this->assertEquals(2, count($customers));
} }
public function testScriptFields()
{
$orderItems = OrderItem::find()->fields(['quantity', 'subtotal', 'total' => ['script' => "doc['quantity'].value * doc['subtotal'].value"]])->all();
foreach($orderItems as $item) {
$this->assertEquals($item->subtotal * $item->quantity, $item->total);
}
}
public function testFindAsArrayFields() public function testFindAsArrayFields()
{ {
/** @var TestCase|ActiveRecordTestTrait $this */ /** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray // indexBy + asArray
$customers = Customer::find()->asArray()->fields(['id', 'name'])->all(); $customers = Customer::find()->asArray()->fields(['id', 'name'])->all();
$this->assertEquals(3, count($customers)); $this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]); $this->assertArrayHasKey('id', $customers[0]['fields']);
$this->assertArrayHasKey('name', $customers[0]); $this->assertArrayHasKey('name', $customers[0]['fields']);
$this->assertArrayNotHasKey('email', $customers[0]); $this->assertArrayNotHasKey('email', $customers[0]['fields']);
$this->assertArrayNotHasKey('address', $customers[0]); $this->assertArrayNotHasKey('address', $customers[0]['fields']);
$this->assertArrayNotHasKey('status', $customers[0]); $this->assertArrayNotHasKey('status', $customers[0]['fields']);
$this->assertArrayHasKey('id', $customers[1]); $this->assertArrayHasKey('id', $customers[1]['fields']);
$this->assertArrayHasKey('name', $customers[1]); $this->assertArrayHasKey('name', $customers[1]['fields']);
$this->assertArrayNotHasKey('email', $customers[1]); $this->assertArrayNotHasKey('email', $customers[1]['fields']);
$this->assertArrayNotHasKey('address', $customers[1]); $this->assertArrayNotHasKey('address', $customers[1]['fields']);
$this->assertArrayNotHasKey('status', $customers[1]); $this->assertArrayNotHasKey('status', $customers[1]['fields']);
$this->assertArrayHasKey('id', $customers[2]); $this->assertArrayHasKey('id', $customers[2]['fields']);
$this->assertArrayHasKey('name', $customers[2]); $this->assertArrayHasKey('name', $customers[2]['fields']);
$this->assertArrayNotHasKey('email', $customers[2]); $this->assertArrayNotHasKey('email', $customers[2]['fields']);
$this->assertArrayNotHasKey('address', $customers[2]); $this->assertArrayNotHasKey('address', $customers[2]['fields']);
$this->assertArrayNotHasKey('status', $customers[2]); $this->assertArrayNotHasKey('status', $customers[2]['fields']);
} }
public function testFindIndexByFields() public function testFindAsArraySourceFilter()
{
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = Customer::find()->asArray()->source(['id', 'name'])->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]['_source']);
$this->assertArrayHasKey('name', $customers[0]['_source']);
$this->assertArrayNotHasKey('email', $customers[0]['_source']);
$this->assertArrayNotHasKey('address', $customers[0]['_source']);
$this->assertArrayNotHasKey('status', $customers[0]['_source']);
$this->assertArrayHasKey('id', $customers[1]['_source']);
$this->assertArrayHasKey('name', $customers[1]['_source']);
$this->assertArrayNotHasKey('email', $customers[1]['_source']);
$this->assertArrayNotHasKey('address', $customers[1]['_source']);
$this->assertArrayNotHasKey('status', $customers[1]['_source']);
$this->assertArrayHasKey('id', $customers[2]['_source']);
$this->assertArrayHasKey('name', $customers[2]['_source']);
$this->assertArrayNotHasKey('email', $customers[2]['_source']);
$this->assertArrayNotHasKey('address', $customers[2]['_source']);
$this->assertArrayNotHasKey('status', $customers[2]['_source']);
}
public function testFindIndexBySource()
{ {
$customerClass = $this->getCustomerClass(); $customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */ /** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray // indexBy + asArray
$customers = Customer::find()->indexBy('name')->fields('id', 'name')->all(); $customers = Customer::find()->indexBy('name')->source('id', 'name')->all();
$this->assertEquals(3, count($customers)); $this->assertEquals(3, count($customers));
$this->assertTrue($customers['user1'] instanceof $customerClass); $this->assertTrue($customers['user1'] instanceof $customerClass);
$this->assertTrue($customers['user2'] instanceof $customerClass); $this->assertTrue($customers['user2'] instanceof $customerClass);
...@@ -467,42 +499,89 @@ class ActiveRecordTest extends ElasticSearchTestCase ...@@ -467,42 +499,89 @@ class ActiveRecordTest extends ElasticSearchTestCase
// indexBy + asArray // indexBy + asArray
$customers = Customer::find()->indexBy('name')->asArray()->fields('id', 'name')->all(); $customers = Customer::find()->indexBy('name')->asArray()->fields('id', 'name')->all();
$this->assertEquals(3, count($customers)); $this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['user1']); $this->assertArrayHasKey('id', $customers['user1']['fields']);
$this->assertArrayHasKey('name', $customers['user1']); $this->assertArrayHasKey('name', $customers['user1']['fields']);
$this->assertArrayNotHasKey('email', $customers['user1']); $this->assertArrayNotHasKey('email', $customers['user1']['fields']);
$this->assertArrayNotHasKey('address', $customers['user1']); $this->assertArrayNotHasKey('address', $customers['user1']['fields']);
$this->assertArrayNotHasKey('status', $customers['user1']); $this->assertArrayNotHasKey('status', $customers['user1']['fields']);
$this->assertArrayHasKey('id', $customers['user2']); $this->assertArrayHasKey('id', $customers['user2']['fields']);
$this->assertArrayHasKey('name', $customers['user2']); $this->assertArrayHasKey('name', $customers['user2']['fields']);
$this->assertArrayNotHasKey('email', $customers['user2']); $this->assertArrayNotHasKey('email', $customers['user2']['fields']);
$this->assertArrayNotHasKey('address', $customers['user2']); $this->assertArrayNotHasKey('address', $customers['user2']['fields']);
$this->assertArrayNotHasKey('status', $customers['user2']); $this->assertArrayNotHasKey('status', $customers['user2']['fields']);
$this->assertArrayHasKey('id', $customers['user3']); $this->assertArrayHasKey('id', $customers['user3']['fields']);
$this->assertArrayHasKey('name', $customers['user3']); $this->assertArrayHasKey('name', $customers['user3']['fields']);
$this->assertArrayNotHasKey('email', $customers['user3']); $this->assertArrayNotHasKey('email', $customers['user3']['fields']);
$this->assertArrayNotHasKey('address', $customers['user3']); $this->assertArrayNotHasKey('address', $customers['user3']['fields']);
$this->assertArrayNotHasKey('status', $customers['user3']); $this->assertArrayNotHasKey('status', $customers['user3']['fields']);
// indexBy callable + asArray // indexBy callable + asArray
$customers = Customer::find()->indexBy(function ($customer) { $customers = Customer::find()->indexBy(function ($customer) {
return $customer['id'] . '-' . $customer['name']; return reset($customer['fields']['id']) . '-' . reset($customer['fields']['name']);
})->asArray()->fields('id', 'name')->all(); })->asArray()->fields('id', 'name')->all();
$this->assertEquals(3, count($customers)); $this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['1-user1']); $this->assertArrayHasKey('id', $customers['1-user1']['fields']);
$this->assertArrayHasKey('name', $customers['1-user1']); $this->assertArrayHasKey('name', $customers['1-user1']['fields']);
$this->assertArrayNotHasKey('email', $customers['1-user1']); $this->assertArrayNotHasKey('email', $customers['1-user1']['fields']);
$this->assertArrayNotHasKey('address', $customers['1-user1']); $this->assertArrayNotHasKey('address', $customers['1-user1']['fields']);
$this->assertArrayNotHasKey('status', $customers['1-user1']); $this->assertArrayNotHasKey('status', $customers['1-user1']['fields']);
$this->assertArrayHasKey('id', $customers['2-user2']); $this->assertArrayHasKey('id', $customers['2-user2']['fields']);
$this->assertArrayHasKey('name', $customers['2-user2']); $this->assertArrayHasKey('name', $customers['2-user2']['fields']);
$this->assertArrayNotHasKey('email', $customers['2-user2']); $this->assertArrayNotHasKey('email', $customers['2-user2']['fields']);
$this->assertArrayNotHasKey('address', $customers['2-user2']); $this->assertArrayNotHasKey('address', $customers['2-user2']['fields']);
$this->assertArrayNotHasKey('status', $customers['2-user2']); $this->assertArrayNotHasKey('status', $customers['2-user2']['fields']);
$this->assertArrayHasKey('id', $customers['3-user3']); $this->assertArrayHasKey('id', $customers['3-user3']['fields']);
$this->assertArrayHasKey('name', $customers['3-user3']); $this->assertArrayHasKey('name', $customers['3-user3']['fields']);
$this->assertArrayNotHasKey('email', $customers['3-user3']); $this->assertArrayNotHasKey('email', $customers['3-user3']['fields']);
$this->assertArrayNotHasKey('address', $customers['3-user3']); $this->assertArrayNotHasKey('address', $customers['3-user3']['fields']);
$this->assertArrayNotHasKey('status', $customers['3-user3']); $this->assertArrayNotHasKey('status', $customers['3-user3']['fields']);
}
public function testFindIndexByAsArray()
{
/** @var \yii\db\ActiveRecordInterface $customerClass */
$customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = $customerClass::find()->asArray()->indexBy('name')->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['user1']['_source']);
$this->assertArrayHasKey('name', $customers['user1']['_source']);
$this->assertArrayHasKey('email', $customers['user1']['_source']);
$this->assertArrayHasKey('address', $customers['user1']['_source']);
$this->assertArrayHasKey('status', $customers['user1']['_source']);
$this->assertArrayHasKey('id', $customers['user2']['_source']);
$this->assertArrayHasKey('name', $customers['user2']['_source']);
$this->assertArrayHasKey('email', $customers['user2']['_source']);
$this->assertArrayHasKey('address', $customers['user2']['_source']);
$this->assertArrayHasKey('status', $customers['user2']['_source']);
$this->assertArrayHasKey('id', $customers['user3']['_source']);
$this->assertArrayHasKey('name', $customers['user3']['_source']);
$this->assertArrayHasKey('email', $customers['user3']['_source']);
$this->assertArrayHasKey('address', $customers['user3']['_source']);
$this->assertArrayHasKey('status', $customers['user3']['_source']);
// indexBy callable + asArray
$customers = $customerClass::find()->indexBy(function ($customer) {
return $customer['_source']['id'] . '-' . $customer['_source']['name'];
})->asArray()->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['1-user1']['_source']);
$this->assertArrayHasKey('name', $customers['1-user1']['_source']);
$this->assertArrayHasKey('email', $customers['1-user1']['_source']);
$this->assertArrayHasKey('address', $customers['1-user1']['_source']);
$this->assertArrayHasKey('status', $customers['1-user1']['_source']);
$this->assertArrayHasKey('id', $customers['2-user2']['_source']);
$this->assertArrayHasKey('name', $customers['2-user2']['_source']);
$this->assertArrayHasKey('email', $customers['2-user2']['_source']);
$this->assertArrayHasKey('address', $customers['2-user2']['_source']);
$this->assertArrayHasKey('status', $customers['2-user2']['_source']);
$this->assertArrayHasKey('id', $customers['3-user3']['_source']);
$this->assertArrayHasKey('name', $customers['3-user3']['_source']);
$this->assertArrayHasKey('email', $customers['3-user3']['_source']);
$this->assertArrayHasKey('address', $customers['3-user3']['_source']);
$this->assertArrayHasKey('status', $customers['3-user3']['_source']);
} }
public function testAfterFindGet() public function testAfterFindGet()
......
...@@ -20,7 +20,7 @@ class ElasticSearchConnectionTest extends ElasticSearchTestCase ...@@ -20,7 +20,7 @@ class ElasticSearchConnectionTest extends ElasticSearchTestCase
$connection->open(); $connection->open();
$this->assertNotNull($connection->activeNode); $this->assertNotNull($connection->activeNode);
$this->assertArrayHasKey('name', reset($connection->nodes)); $this->assertArrayHasKey('name', reset($connection->nodes));
$this->assertArrayHasKey('hostname', reset($connection->nodes)); // $this->assertArrayHasKey('hostname', reset($connection->nodes));
$this->assertArrayHasKey('version', reset($connection->nodes)); $this->assertArrayHasKey('version', reset($connection->nodes));
$this->assertArrayHasKey('http_address', reset($connection->nodes)); $this->assertArrayHasKey('http_address', reset($connection->nodes));
} }
......
...@@ -17,7 +17,7 @@ class ElasticSearchTestCase extends TestCase ...@@ -17,7 +17,7 @@ class ElasticSearchTestCase extends TestCase
{ {
$this->mockApplication(); $this->mockApplication();
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : null; $params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : null;
if ($params === null || !isset($params['dsn'])) { if ($params === null || !isset($params['dsn'])) {
$this->markTestSkipped('No elasticsearch server connection configured.'); $this->markTestSkipped('No elasticsearch server connection configured.');
...@@ -40,7 +40,7 @@ class ElasticSearchTestCase extends TestCase ...@@ -40,7 +40,7 @@ class ElasticSearchTestCase extends TestCase
*/ */
public function getConnection($reset = true) public function getConnection($reset = true)
{ {
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : []; $params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : [];
$db = new Connection(); $db = new Connection();
if ($reset) { if ($reset) {
......
...@@ -49,7 +49,7 @@ class QueryBuilderTest extends ElasticSearchTestCase ...@@ -49,7 +49,7 @@ class QueryBuilderTest extends ElasticSearchTestCase
public function testYiiCanBeFoundByQuery() public function testYiiCanBeFoundByQuery()
{ {
$this->prepareDbData(); $this->prepareDbData();
$queryParts = ['field' => ['title' => 'yii']]; $queryParts = ['term' => ['title' => 'yii']];
$query = new Query(); $query = new Query();
$query->from('yiitest', 'article'); $query->from('yiitest', 'article');
$query->query = $queryParts; $query->query = $queryParts;
......
...@@ -40,16 +40,16 @@ class QueryTest extends ElasticSearchTestCase ...@@ -40,16 +40,16 @@ class QueryTest extends ElasticSearchTestCase
$this->assertEquals(['name', 'status'], $query->fields); $this->assertEquals(['name', 'status'], $query->fields);
$result = $query->one($this->getConnection()); $result = $query->one($this->getConnection());
$this->assertEquals(2, count($result['_source'])); $this->assertEquals(2, count($result['fields']));
$this->assertArrayHasKey('status', $result['_source']); $this->assertArrayHasKey('status', $result['fields']);
$this->assertArrayHasKey('name', $result['_source']); $this->assertArrayHasKey('name', $result['fields']);
$this->assertArrayHasKey('_id', $result); $this->assertArrayHasKey('_id', $result);
$query->fields([]); $query->fields([]);
$this->assertEquals([], $query->fields); $this->assertEquals([], $query->fields);
$result = $query->one($this->getConnection()); $result = $query->one($this->getConnection());
$this->assertEquals([], $result['_source']); $this->assertArrayNotHasKey('fields', $result);
$this->assertArrayHasKey('_id', $result); $this->assertArrayHasKey('_id', $result);
$query->fields(null); $query->fields(null);
......
...@@ -34,7 +34,7 @@ class MongoDbTestCase extends TestCase ...@@ -34,7 +34,7 @@ class MongoDbTestCase extends TestCase
if (!extension_loaded('mongo')) { if (!extension_loaded('mongo')) {
$this->markTestSkipped('mongo extension required.'); $this->markTestSkipped('mongo extension required.');
} }
$config = $this->getParam('mongodb'); $config = self::getParam('mongodb');
if (!empty($config)) { if (!empty($config)) {
$this->mongoDbConfig = $config; $this->mongoDbConfig = $config;
} }
......
...@@ -22,7 +22,7 @@ class RedisCacheTest extends CacheTestCase ...@@ -22,7 +22,7 @@ class RedisCacheTest extends CacheTestCase
*/ */
protected function getCacheInstance() protected function getCacheInstance()
{ {
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : null; $params = isset($databases['redis']) ? $databases['redis'] : null;
if ($params === null) { if ($params === null) {
$this->markTestSkipped('No redis server connection configured.'); $this->markTestSkipped('No redis server connection configured.');
......
...@@ -15,7 +15,7 @@ abstract class RedisTestCase extends TestCase ...@@ -15,7 +15,7 @@ abstract class RedisTestCase extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : null; $params = isset($databases['redis']) ? $databases['redis'] : null;
if ($params === null) { if ($params === null) {
$this->markTestSkipped('No redis server connection configured.'); $this->markTestSkipped('No redis server connection configured.');
...@@ -36,7 +36,7 @@ abstract class RedisTestCase extends TestCase ...@@ -36,7 +36,7 @@ abstract class RedisTestCase extends TestCase
*/ */
public function getConnection($reset = true) public function getConnection($reset = true)
{ {
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : []; $params = isset($databases['redis']) ? $databases['redis'] : [];
$db = new Connection($params); $db = new Connection($params);
if ($reset) { if ($reset) {
......
...@@ -48,7 +48,7 @@ class SphinxTestCase extends TestCase ...@@ -48,7 +48,7 @@ class SphinxTestCase extends TestCase
if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) { if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) {
$this->markTestSkipped('pdo and pdo_mysql extension are required.'); $this->markTestSkipped('pdo and pdo_mysql extension are required.');
} }
$config = $this->getParam('sphinx'); $config = self::getParam('sphinx');
if (!empty($config)) { if (!empty($config)) {
$this->sphinxConfig = $config['sphinx']; $this->sphinxConfig = $config['sphinx'];
$this->dbConfig = $config['db']; $this->dbConfig = $config['db'];
......
...@@ -70,25 +70,6 @@ trait ActiveRecordTestTrait ...@@ -70,25 +70,6 @@ trait ActiveRecordTestTrait
$this->assertTrue($customers[1] instanceof $customerClass); $this->assertTrue($customers[1] instanceof $customerClass);
$this->assertTrue($customers[2] instanceof $customerClass); $this->assertTrue($customers[2] instanceof $customerClass);
// find all asArray
$customers = $customerClass::find()->asArray()->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]);
$this->assertArrayHasKey('name', $customers[0]);
$this->assertArrayHasKey('email', $customers[0]);
$this->assertArrayHasKey('address', $customers[0]);
$this->assertArrayHasKey('status', $customers[0]);
$this->assertArrayHasKey('id', $customers[1]);
$this->assertArrayHasKey('name', $customers[1]);
$this->assertArrayHasKey('email', $customers[1]);
$this->assertArrayHasKey('address', $customers[1]);
$this->assertArrayHasKey('status', $customers[1]);
$this->assertArrayHasKey('id', $customers[2]);
$this->assertArrayHasKey('name', $customers[2]);
$this->assertArrayHasKey('email', $customers[2]);
$this->assertArrayHasKey('address', $customers[2]);
$this->assertArrayHasKey('status', $customers[2]);
// find by a single primary key // find by a single primary key
$customer = $customerClass::findOne(2); $customer = $customerClass::findOne(2);
$this->assertTrue($customer instanceof $customerClass); $this->assertTrue($customer instanceof $customerClass);
...@@ -136,6 +117,25 @@ trait ActiveRecordTestTrait ...@@ -136,6 +117,25 @@ trait ActiveRecordTestTrait
'status' => 1, 'status' => 1,
'profile_id' => null, 'profile_id' => null,
], $customer); ], $customer);
// find all asArray
$customers = $customerClass::find()->asArray()->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers[0]);
$this->assertArrayHasKey('name', $customers[0]);
$this->assertArrayHasKey('email', $customers[0]);
$this->assertArrayHasKey('address', $customers[0]);
$this->assertArrayHasKey('status', $customers[0]);
$this->assertArrayHasKey('id', $customers[1]);
$this->assertArrayHasKey('name', $customers[1]);
$this->assertArrayHasKey('email', $customers[1]);
$this->assertArrayHasKey('address', $customers[1]);
$this->assertArrayHasKey('status', $customers[1]);
$this->assertArrayHasKey('id', $customers[2]);
$this->assertArrayHasKey('name', $customers[2]);
$this->assertArrayHasKey('email', $customers[2]);
$this->assertArrayHasKey('address', $customers[2]);
$this->assertArrayHasKey('status', $customers[2]);
} }
public function testFindScalar() public function testFindScalar()
......
...@@ -40,7 +40,7 @@ class DbCacheTest extends CacheTestCase ...@@ -40,7 +40,7 @@ class DbCacheTest extends CacheTestCase
public function getConnection($reset = true) public function getConnection($reset = true)
{ {
if ($this->_connection === null) { if ($this->_connection === null) {
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$params = $databases['mysql']; $params = $databases['mysql'];
$db = new \yii\db\Connection; $db = new \yii\db\Connection;
$db->dsn = $params['dsn']; $db->dsn = $params['dsn'];
......
...@@ -16,7 +16,7 @@ abstract class DatabaseTestCase extends TestCase ...@@ -16,7 +16,7 @@ abstract class DatabaseTestCase extends TestCase
protected function setUp() protected function setUp()
{ {
parent::setUp(); parent::setUp();
$databases = $this->getParam('databases'); $databases = self::getParam('databases');
$this->database = $databases[$this->driverName]; $this->database = $databases[$this->driverName];
$pdo_database = 'pdo_'.$this->driverName; $pdo_database = 'pdo_'.$this->driverName;
......
<?php <?php
namespace yiiunit\framework\rbac; namespace yiiunit\framework\rbac;
use Yii;
use yii\console\Application;
use yii\console\Controller;
use yii\console\controllers\MigrateController;
use yii\db\Connection; use yii\db\Connection;
use yii\rbac\DbManager; use yii\rbac\DbManager;
...@@ -9,70 +13,97 @@ use yii\rbac\DbManager; ...@@ -9,70 +13,97 @@ use yii\rbac\DbManager;
*/ */
abstract class DbManagerTestCase extends ManagerTestCase abstract class DbManagerTestCase extends ManagerTestCase
{ {
protected $database; protected static $database;
protected $driverName = 'mysql'; protected static $driverName = 'mysql';
/** /**
* @var Connection * @var Connection
*/ */
protected $db; protected static $db;
protected function setUp() protected static function runConsoleAction($route, $params = [])
{ {
parent::setUp(); if (Yii::$app === null) {
$databases = $this->getParam('databases'); new Application([
$this->database = $databases[$this->driverName]; 'id' => 'Migrator',
$pdo_database = 'pdo_'.$this->driverName; 'basePath' => '@yiiunit',
'components' => [
'db' => static::getConnection(),
'authManager' => '\yii\rbac\DbManager',
],
]);
}
ob_start();
$result = Yii::$app->runAction('migrate/up', ['migrationPath' => '@yii/rbac/migrations/', 'interactive' => false]);
echo "Result is ".$result;
if ($result !== Controller::EXIT_CODE_NORMAL) {
ob_end_flush();
} else {
ob_end_clean();
}
}
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
$databases = static::getParam('databases');
static::$database = $databases[static::$driverName];
$pdo_database = 'pdo_' . static::$driverName;
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) { if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) {
$this->markTestSkipped('pdo and '.$pdo_database.' extension are required.'); static::markTestSkipped('pdo and ' . $pdo_database . ' extension are required.');
} }
static::runConsoleAction('migrate/up', ['migrationPath' => '@yii/rbac/migrations/', 'interactive' => false]);
}
public static function tearDownAfterClass()
{
static::runConsoleAction('migrate/down', ['migrationPath' => '@yii/rbac/migrations/', 'interactive' => false]);
if (static::$db) {
static::$db->close();
}
Yii::$app = null;
parent::tearDownAfterClass();
}
protected function setUp()
{
parent::setUp();
$this->auth = new DbManager(['db' => $this->getConnection()]); $this->auth = new DbManager(['db' => $this->getConnection()]);
} }
protected function tearDown() protected function tearDown()
{ {
parent::tearDown(); parent::tearDown();
if ($this->db) { $this->auth->removeAll();
$this->db->close();
}
$this->destroyApplication();
} }
/** /**
* @param boolean $reset whether to clean up the test database
* @param boolean $open whether to open and populate test database
* @throws \yii\base\InvalidParamException * @throws \yii\base\InvalidParamException
* @throws \yii\db\Exception * @throws \yii\db\Exception
* @throws \yii\base\InvalidConfigException * @throws \yii\base\InvalidConfigException
* @return \yii\db\Connection * @return \yii\db\Connection
*/ */
public function getConnection($reset = true, $open = true) public static function getConnection()
{ {
if (!$reset && $this->db) { if (static::$db == null) {
return $this->db; $db = new Connection;
} $db->dsn = static::$database['dsn'];
$db = new Connection; if (isset(static::$database['username'])) {
$db->dsn = $this->database['dsn']; $db->username = static::$database['username'];
if (isset($this->database['username'])) { $db->password = static::$database['password'];
$db->username = $this->database['username'];
$db->password = $this->database['password'];
}
if (isset($this->database['attributes'])) {
$db->attributes = $this->database['attributes'];
}
if ($open) {
$db->open();
$lines = explode(';', file_get_contents(\Yii::getAlias('@yii/rbac/schema-'.$this->driverName.'.sql')));
foreach ($lines as $line) {
if (trim($line) !== '') {
$db->pdo->exec($line);
}
} }
if (isset(static::$database['attributes'])) {
$db->attributes = static::$database['attributes'];
}
if (!$db->isActive) {
$db->open();
}
static::$db = $db;
} }
$this->db = $db; return static::$db;
return $db;
} }
} }
...@@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac; ...@@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac;
*/ */
class PgSQLManagerTest extends DbManagerTestCase class PgSQLManagerTest extends DbManagerTestCase
{ {
protected $driverName = 'pgsql'; protected static $driverName = 'pgsql';
} }
...@@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac; ...@@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac;
*/ */
class SqliteManagerTest extends DbManagerTestCase class SqliteManagerTest extends DbManagerTestCase
{ {
protected $driverName = 'sqlite'; protected static $driverName = 'sqlite';
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment