diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php index 6f43b28..b1cf75f 100644 --- a/apps/advanced/common/models/User.php +++ b/apps/advanced/common/models/User.php @@ -69,7 +69,7 @@ class User extends ActiveRecord implements IdentityInterface /** * @inheritdoc */ - public static function findIdentityByAccessToken($token) + public static function findIdentityByAccessToken($token, $type = null) { throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); } diff --git a/apps/basic/models/User.php b/apps/basic/models/User.php index 5de7e20..cbfb9fe 100644 --- a/apps/basic/models/User.php +++ b/apps/basic/models/User.php @@ -38,7 +38,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface /** * @inheritdoc */ - public static function findIdentityByAccessToken($token) + public static function findIdentityByAccessToken($token, $type = null) { foreach (self::$users as $user) { if ($user['accessToken'] === $token) { diff --git a/docs/guide/rest-quick-start.md b/docs/guide/rest-quick-start.md index ee44edd..2da13e9 100644 --- a/docs/guide/rest-quick-start.md +++ b/docs/guide/rest-quick-start.md @@ -679,7 +679,7 @@ use yii\web\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]); } diff --git a/docs/guide/security-authentication.md b/docs/guide/security-authentication.md index 7f24c9b..fb9cd0f 100644 --- a/docs/guide/security-authentication.md +++ b/docs/guide/security-authentication.md @@ -32,7 +32,7 @@ class User extends ActiveRecord implements IdentityInterface * @param string $token the token to be looked for * @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]); } diff --git a/docs/guide/security-authorization.md b/docs/guide/security-authorization.md index d9bc358..9a75411 100644 --- a/docs/guide/security-authorization.md +++ b/docs/guide/security-authorization.md @@ -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" 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 return [ diff --git a/docs/guide/start-basic.md b/docs/guide/start-installation.md similarity index 54% rename from docs/guide/start-basic.md rename to docs/guide/start-installation.md index e02ad70..6d52276 100644 --- a/docs/guide/start-basic.md +++ b/docs/guide/start-installation.md @@ -1,5 +1,85 @@ -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. diff --git a/extensions/apidoc/models/BaseDoc.php b/extensions/apidoc/models/BaseDoc.php index 7e1ed94..c20d9c9 100644 --- a/extensions/apidoc/models/BaseDoc.php +++ b/extensions/apidoc/models/BaseDoc.php @@ -42,6 +42,26 @@ class BaseDoc extends Object */ 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 Context $context @@ -70,7 +90,7 @@ class BaseDoc extends Object '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(); diff --git a/extensions/apidoc/models/Context.php b/extensions/apidoc/models/Context.php index 351f96e..ea584a4 100644 --- a/extensions/apidoc/models/Context.php +++ b/extensions/apidoc/models/Context.php @@ -164,11 +164,33 @@ class Context extends Component { // TODO also for properties? foreach ($class->methods as $m) { - $inheritedMethod = $this->inheritMethodRecursive($m, $class); - foreach (['shortDescription', 'description', 'params', 'return', 'returnType', 'returnTypes', 'exceptions'] as $property) { - if (empty($m->$property)) { - $m->$property = $inheritedMethod->$property; + if ($m->hasTag('inheritdoc')) { + $inheritedMethod = $this->inheritMethodRecursive($m, $class); + foreach (['shortDescription', 'description', 'return', 'returnType', 'returnTypes', 'exceptions'] as $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 */ private function inheritMethodRecursive($method, $class) { - if (!isset($this->classes[$class->parentClass])) { - return $method; - } - $parent = $this->classes[$class->parentClass]; - foreach ($method->tags as $tag) { - if (strtolower($tag->getName()) == 'inheritdoc') { - if (isset($parent->methods[$method->name])) { - $method = $parent->methods[$method->name]; + $inheritanceCandidates = array_merge( + $this->getParents($class), + $this->getInterfaces($class) + ); + + $methods = []; + foreach($inheritanceCandidates as $candidate) { + 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; } /** diff --git a/extensions/elasticsearch/ActiveQuery.php b/extensions/elasticsearch/ActiveQuery.php index 4fb02ff..499eccc 100644 --- a/extensions/elasticsearch/ActiveQuery.php +++ b/extensions/elasticsearch/ActiveQuery.php @@ -7,6 +7,7 @@ namespace yii\elasticsearch; +use yii\base\NotSupportedException; use yii\db\ActiveQueryInterface; use yii\db\ActiveQueryTrait; use yii\db\ActiveRelationTrait; @@ -143,47 +144,21 @@ class ActiveQuery extends Query implements ActiveQueryInterface */ public function all($db = null) { + if ($this->asArray) { + // TODO implement with + return parent::all($db); + } + $result = $this->createCommand($db)->search(); if (empty($result['hits']['hits'])) { 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']); - 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)) { $this->findWith($this->with, $models); } - if (!$this->asArray) { - foreach ($models as $model) { - $model->afterFind(); - } + foreach ($models as $model) { + $model->afterFind(); } return $models; @@ -203,30 +178,34 @@ class ActiveQuery extends Query implements ActiveQueryInterface return null; } if ($this->asArray) { - /** @var ActiveRecord $modelClass */ - $modelClass = $this->modelClass; - $model = $result['_source']; - $pk = $modelClass::primaryKey()[0]; - if ($pk === '_id') { - $model['_id'] = $result['_id']; - } - $model['_score'] = $result['_score']; + // TODO implement with +// /** @var ActiveRecord $modelClass */ +// $modelClass = $this->modelClass; +// $model = $result['_source']; +// $pk = $modelClass::primaryKey()[0]; +// if ($pk === '_id') { +// $model['_id'] = $result['_id']; +// } +// $model['_score'] = $result['_score']; +// if (!empty($this->with)) { +// $models = [$model]; +// $this->findWith($this->with, $models); +// $model = $models[0]; +// } + return $result; } else { /** @var ActiveRecord $class */ $class = $this->modelClass; $model = $class::instantiate($result); $class::populateRecord($model, $result); - } - if (!empty($this->with)) { - $models = [$model]; - $this->findWith($this->with, $models); - $model = $models[0]; - } - if (!$this->asArray) { + if (!empty($this->with)) { + $models = [$model]; + $this->findWith($this->with, $models); + $model = $models[0]; + } $model->afterFind(); + return $model; } - - return $model; } /** @@ -235,27 +214,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface public function search($db = null, $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']); - 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)) { $this->findWith($this->with, $models); } - if (!$this->asArray) { - foreach ($models as $model) { - $model->afterFind(); - } + foreach ($models as $model) { + $model->afterFind(); } $result['hits']['hits'] = $models; } @@ -266,28 +232,12 @@ class ActiveQuery extends Query implements ActiveQueryInterface /** * @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) { if ($field == '_id') { $command = $this->createCommand($db); $command->queryParts['fields'] = []; + $command->queryParts['_source'] = false; $result = $command->search(); if (empty($result['hits']['hits'])) { return []; diff --git a/extensions/elasticsearch/ActiveRecord.php b/extensions/elasticsearch/ActiveRecord.php index b5c1454..6ff1095 100644 --- a/extensions/elasticsearch/ActiveRecord.php +++ b/extensions/elasticsearch/ActiveRecord.php @@ -114,7 +114,7 @@ class ActiveRecord extends BaseActiveRecord } $command = static::getDb()->createCommand(); $result = $command->get(static::index(), static::type(), $primaryKey, $options); - if ($result['exists']) { + if ($result['found']) { $model = static::instantiate($result); static::populateRecord($model, $result); $model->afterFind(); @@ -150,7 +150,7 @@ class ActiveRecord extends BaseActiveRecord $result = $command->mget(static::index(), static::type(), $primaryKeys, $options); $models = []; foreach ($result['docs'] as $doc) { - if ($doc['exists']) { + if ($doc['found']) { $model = static::instantiate($doc); static::populateRecord($model, $doc); $model->afterFind(); @@ -282,8 +282,23 @@ class ActiveRecord extends BaseActiveRecord */ public static function populateRecord($record, $row) { - parent::populateRecord($record, $row['_source']); - $pk = static::primaryKey()[0]; + $attributes = []; + 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') { $record->_id = $row['_id']; } @@ -379,9 +394,9 @@ class ActiveRecord extends BaseActiveRecord $options ); - if (!isset($response['ok'])) { - return false; - } +// if (!isset($response['ok'])) { +// return false; +// } $pk = static::primaryKey()[0]; $this->$pk = $response['_id']; if ($pk != '_id') { @@ -444,13 +459,13 @@ class ActiveRecord extends BaseActiveRecord $n = 0; $errors = []; foreach ($response['items'] as $item) { - if (isset($item['update']['error'])) { - $errors[] = $item['update']; - } elseif ($item['update']['ok']) { + if (isset($item['update']['status']) && $item['update']['status'] == 200) { $n++; + } else { + $errors[] = $item['update']; } } - if (!empty($errors)) { + if (!empty($errors) || isset($response['errors']) && $response['errors']) { throw new Exception(__METHOD__ . ' failed updating records.', $errors); } @@ -508,13 +523,13 @@ class ActiveRecord extends BaseActiveRecord $n = 0; $errors = []; foreach ($response['items'] as $item) { - if (isset($item['update']['error'])) { - $errors[] = $item['update']; - } elseif ($item['update']['ok']) { + if (isset($item['update']['status']) && $item['update']['status'] == 200) { $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); } @@ -563,13 +578,15 @@ class ActiveRecord extends BaseActiveRecord $n = 0; $errors = []; 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']; - } 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); } diff --git a/extensions/elasticsearch/CHANGELOG.md b/extensions/elasticsearch/CHANGELOG.md index 6b69719..3710c97 100644 --- a/extensions/elasticsearch/CHANGELOG.md +++ b/extensions/elasticsearch/CHANGELOG.md @@ -4,7 +4,9 @@ Yii Framework 2 elasticsearch extension Change Log 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 diff --git a/extensions/elasticsearch/Command.php b/extensions/elasticsearch/Command.php index 560992b..f1313ad 100644 --- a/extensions/elasticsearch/Command.php +++ b/extensions/elasticsearch/Command.php @@ -315,7 +315,7 @@ class Command extends Component { $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 */ 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 */ 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 * @return mixed * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html */ - public function getFieldMapping($index, $type = '_all') - { - return $this->db->put([$index, $type, '_mapping']); - } +// public function getFieldMapping($index, $type = '_all') +// { +// // TODO implement +// return $this->db->put([$index, $type, '_mapping']); +// } /** * @param $options diff --git a/extensions/elasticsearch/Connection.php b/extensions/elasticsearch/Connection.php index 5d0737b..8a8124d 100644 --- a/extensions/elasticsearch/Connection.php +++ b/extensions/elasticsearch/Connection.php @@ -109,7 +109,7 @@ class Connection extends Component if (strncmp($host, 'inet[/', 6) == 0) { $host = substr($host, 6, -1); } - $response = $this->httpRequest('GET', 'http://' . $host . '/_cluster/nodes'); + $response = $this->httpRequest('GET', 'http://' . $host . '/_nodes'); $this->nodes = $response['nodes']; if (empty($this->nodes)) { throw new Exception('cluster autodetection did not find any active node.'); diff --git a/extensions/elasticsearch/Query.php b/extensions/elasticsearch/Query.php index dc9996b..535e1c8 100644 --- a/extensions/elasticsearch/Query.php +++ b/extensions/elasticsearch/Query.php @@ -58,13 +58,52 @@ class Query extends Component implements QueryInterface /** * @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 - * retrieved. This means that only the primaryKey of a record will be available in the result. + * If not set, this option will not be applied to the query and no fields will be returned. + * 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-script-fields.html * @see fields() + * @see source */ 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 * or a an array of multiple indexes. If this is not set, indexes are being queried. * @see from() @@ -137,25 +176,20 @@ class Query extends Component implements QueryInterface return []; } $rows = $result['hits']['hits']; - if ($this->indexBy === null && $this->fields === null) { + if ($this->indexBy === null) { return $rows; } $models = []; foreach ($rows as $key => $row) { - if ($this->fields !== null) { - $row['_source'] = isset($row['fields']) ? $row['fields'] : []; - unset($row['fields']); - } if ($this->indexBy !== null) { 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 { $key = call_user_func($this->indexBy, $row); } } $models[$key] = $row; } - return $models; } @@ -173,10 +207,6 @@ class Query extends Component implements QueryInterface return false; } $record = reset($result['hits']['hits']); - if ($this->fields !== null) { - $record['_source'] = isset($record['fields']) ? $record['fields'] : []; - unset($record['fields']); - } return $record; } @@ -195,25 +225,18 @@ class Query extends Component implements QueryInterface public function search($db = null, $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 = []; foreach ($result['hits']['hits'] as $key => $row) { - if ($this->fields !== null) { - $row['_source'] = isset($row['fields']) ? $row['fields'] : []; - unset($row['fields']); - } - if ($this->indexBy !== null) { - if (is_string($this->indexBy)) { - $key = $row['_source'][$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); - } + if (is_string($this->indexBy)) { + $key = isset($row['fields'][$this->indexBy]) ? $row['fields'][$this->indexBy] : $row['_source'][$this->indexBy]; + } else { + $key = call_user_func($this->indexBy, $row); } $rows[$key] = $row; } $result['hits']['hits'] = $rows; } - return $result; } @@ -247,12 +270,17 @@ class Query extends Component implements QueryInterface */ public function scalar($field, $db = null) { - $record = self::one($db); // TODO limit fields to the one required - if ($record !== false && isset($record['_source'][$field])) { - return $record['_source'][$field]; - } else { - return null; + $record = self::one($db); + if ($record !== false) { + if ($field === '_id') { + return $record['_id']; + } 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 public function column($field, $db = null) { $command = $this->createCommand($db); - $command->queryParts['fields'] = [$field]; + $command->queryParts['_source'] = [$field]; $result = $command->search(); if (empty($result['hits']['hits'])) { return []; } $column = []; 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; } @@ -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. * @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. diff --git a/extensions/elasticsearch/QueryBuilder.php b/extensions/elasticsearch/QueryBuilder.php index 7d0e770..951e6f6 100644 --- a/extensions/elasticsearch/QueryBuilder.php +++ b/extensions/elasticsearch/QueryBuilder.php @@ -45,8 +45,27 @@ class QueryBuilder extends \yii\base\Object { $parts = []; - if ($query->fields !== null) { - $parts['fields'] = (array) $query->fields; + if ($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) { $parts['size'] = $query->limit; diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md index e952f0a..0875e8e 100644 --- a/extensions/elasticsearch/README.md +++ b/extensions/elasticsearch/README.md @@ -22,6 +22,10 @@ return [ ]; ``` +Requirements +------------ + +elasticsearch version 1.0 or higher is required. Installation ------------ diff --git a/extensions/gii/views/default/index.php b/extensions/gii/views/default/index.php index 0ba6529..05ce312 100644 --- a/extensions/gii/views/default/index.php +++ b/extensions/gii/views/default/index.php @@ -11,7 +11,7 @@ $this->title = 'Welcome to Gii'; ?> <div class="default-index"> <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> <p class="lead">Start the fun with the following code generators:</p> diff --git a/extensions/mongodb/CHANGELOG.md b/extensions/mongodb/CHANGELOG.md index a3292d7..0d1b697 100644 --- a/extensions/mongodb/CHANGELOG.md +++ b/extensions/mongodb/CHANGELOG.md @@ -4,7 +4,7 @@ Yii Framework 2 mongodb extension Change Log 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 diff --git a/extensions/mongodb/Connection.php b/extensions/mongodb/Connection.php index fab3e88..50ff815 100644 --- a/extensions/mongodb/Connection.php +++ b/extensions/mongodb/Connection.php @@ -219,7 +219,7 @@ class Connection extends Component */ public function getIsActive() { - return is_object($this->mongoClient) && $this->mongoClient->connected; + return is_object($this->mongoClient) && $this->mongoClient->getConnections() != []; } /** diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ba03abc..034446a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -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 #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: Fixed inconsistent return of `\yii\console\Application::runAction()` (samdark) - 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 #2837: Error page now shows arguments in stack trace method calls (samdark) @@ -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: 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 `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 #3036: Upgraded Twitter Bootstrap to 3.1.x (qiangxue) - 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: 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) diff --git a/framework/UPGRADE.md b/framework/UPGRADE.md index be0e4a5..ffbade4 100644 --- a/framework/UPGRADE.md +++ b/framework/UPGRADE.md @@ -21,3 +21,11 @@ Upgrade from Yii 2.0 Beta * 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. + +* 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. diff --git a/framework/base/Object.php b/framework/base/Object.php index a5b9d3f..73e4240 100644 --- a/framework/base/Object.php +++ b/framework/base/Object.php @@ -77,6 +77,7 @@ use Yii; class Object { /** + * Returns the fully qualified name of this class. * @return string the fully qualified name of this class. */ public static function className() diff --git a/framework/base/Widget.php b/framework/base/Widget.php index ea7358d..95946ba 100644 --- a/framework/base/Widget.php +++ b/framework/base/Widget.php @@ -54,7 +54,7 @@ class Widget extends Component implements ViewContextInterface $config['class'] = get_called_class(); /** @var Widget $widget */ $widget = Yii::createObject($config); - self::$stack[] = $widget; + static::$stack[] = $widget; return $widget; } @@ -67,8 +67,8 @@ class Widget extends Component implements ViewContextInterface */ public static function end() { - if (!empty(self::$stack)) { - $widget = array_pop(self::$stack); + if (!empty(static::$stack)) { + $widget = array_pop(static::$stack); if (get_class($widget) === get_called_class()) { echo $widget->run(); return $widget; @@ -108,7 +108,7 @@ class Widget extends Component implements ViewContextInterface public function getId($autoGenerate = true) { if ($autoGenerate && $this->_id === null) { - $this->_id = self::$autoIdPrefix . self::$counter++; + $this->_id = static::$autoIdPrefix . static::$counter++; } return $this->_id; diff --git a/framework/console/Application.php b/framework/console/Application.php index b373e7e..913e3af 100644 --- a/framework/console/Application.php +++ b/framework/console/Application.php @@ -138,7 +138,7 @@ class Application extends \yii\base\Application return $result; } else { $response = $this->getResponse(); - $response->exitStatus = (int) $result; + $response->exitStatus = $result; return $response; } @@ -157,7 +157,7 @@ class Application extends \yii\base\Application public function runAction($route, $params = []) { try { - return parent::runAction($route, $params); + return (int)parent::runAction($route, $params); } catch (InvalidRouteException $e) { throw new Exception(Yii::t('yii', 'Unknown command "{command}".', ['command' => $route]), 0, $e); } diff --git a/framework/console/Controller.php b/framework/console/Controller.php index a927692..362e39c 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -29,6 +29,9 @@ use yii\helpers\Console; */ class Controller extends \yii\base\Controller { + const EXIT_CODE_NORMAL = 0; + const EXIT_CODE_ERROR = 1; + /** * @var boolean whether to run the command interactively. */ diff --git a/framework/console/controllers/AssetController.php b/framework/console/controllers/AssetController.php index c23761b..b7e1356 100644 --- a/framework/console/controllers/AssetController.php +++ b/framework/console/controllers/AssetController.php @@ -611,7 +611,7 @@ return [ EOD; if (file_exists($configFile)) { 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)) { diff --git a/framework/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php index d798268..8978298 100644 --- a/framework/console/controllers/FixtureController.php +++ b/framework/console/controllers/FixtureController.php @@ -96,7 +96,7 @@ class FixtureController extends Controller } if (!$this->confirmLoad($foundFixtures, $except)) { - return; + return self::EXIT_CODE_NORMAL; } $filtered = array_diff($foundFixtures, $except); @@ -140,7 +140,7 @@ class FixtureController extends Controller } if (!$this->confirmUnload($foundFixtures, $except)) { - return; + return self::EXIT_CODE_NORMAL; } $filtered = array_diff($foundFixtures, $except); diff --git a/framework/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php index ec32561..e3ba05a 100644 --- a/framework/console/controllers/MessageController.php +++ b/framework/console/controllers/MessageController.php @@ -54,7 +54,7 @@ class MessageController extends Controller $filePath = Yii::getAlias($filePath); if (file_exists($filePath)) { 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); @@ -298,7 +298,7 @@ class MessageController extends Controller if (array_keys($translated) == $messages) { echo "nothing new...skipped.\n"; - return; + return self::EXIT_CODE_NORMAL; } $merged = []; $untranslated = []; diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php index 8ed9994..77fbbc9 100644 --- a/framework/console/controllers/MigrateController.php +++ b/framework/console/controllers/MigrateController.php @@ -81,10 +81,6 @@ class MigrateController extends Controller */ 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 * component ID of the DB connection. */ @@ -148,6 +144,8 @@ class MigrateController extends Controller * * @param integer $limit the number of new migrations to be applied. If 0, it means * 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) { @@ -155,7 +153,7 @@ class MigrateController extends Controller if (empty($migrations)) { echo "No new migration found. Your system is up-to-date.\n"; - return; + return self::EXIT_CODE_NORMAL; } $total = count($migrations); @@ -181,7 +179,7 @@ class MigrateController extends Controller if (!$this->migrateUp($migration)) { echo "\nMigration failed. The rest of the migrations are canceled.\n"; - return; + return self::EXIT_CODE_ERROR; } } echo "\nMigrated up successfully.\n"; @@ -200,6 +198,8 @@ class MigrateController extends Controller * @param integer $limit the number of migrations to be reverted. Defaults to 1, * meaning the last applied migration will be reverted. * @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) { @@ -212,7 +212,7 @@ class MigrateController extends Controller if (empty($migrations)) { echo "No migration has been done before.\n"; - return; + return self::EXIT_CODE_NORMAL; } $migrations = array_keys($migrations); @@ -228,7 +228,7 @@ class MigrateController extends Controller if (!$this->migrateDown($migration)) { echo "\nMigration failed. The rest of the migrations are canceled.\n"; - return; + return self::EXIT_CODE_ERROR; } } echo "\nMigrated down successfully.\n"; @@ -249,6 +249,8 @@ class MigrateController extends Controller * @param integer $limit the number of migrations to be redone. Defaults to 1, * meaning the last applied migration will be redone. * @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) { @@ -261,7 +263,7 @@ class MigrateController extends Controller if (empty($migrations)) { echo "No migration has been done before.\n"; - return; + return self::EXIT_CODE_NORMAL; } $migrations = array_keys($migrations); @@ -277,14 +279,14 @@ class MigrateController extends Controller if (!$this->migrateDown($migration)) { echo "\nMigration failed. The rest of the migrations are canceled.\n"; - return; + return self::EXIT_CODE_ERROR; } } foreach (array_reverse($migrations) as $migration) { if (!$this->migrateUp($migration)) { echo "\nMigration failed. The rest of the migrations migrations are canceled.\n"; - return; + return self::EXIT_CODE_ERROR; } } echo "\nMigration redone successfully.\n"; @@ -365,7 +367,7 @@ class MigrateController extends Controller 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 } } - return; + return self::EXIT_CODE_NORMAL; } } @@ -602,7 +604,7 @@ class MigrateController extends Controller if (strpos($migration, $version . '_') === 0) { $this->actionUp($i + 1); - return; + return self::EXIT_CODE_NORMAL; } } @@ -616,7 +618,7 @@ class MigrateController extends Controller $this->actionDown($i); } - return; + return self::EXIT_CODE_NORMAL; } } diff --git a/framework/filters/auth/HttpBasicAuth.php b/framework/filters/auth/HttpBasicAuth.php index 565112c..594faed 100644 --- a/framework/filters/auth/HttpBasicAuth.php +++ b/framework/filters/auth/HttpBasicAuth.php @@ -77,7 +77,7 @@ class HttpBasicAuth extends AuthMethod return $identity; } } elseif ($username !== null) { - $identity = $user->loginByAccessToken($username); + $identity = $user->loginByAccessToken($username, get_class($this)); if ($identity === null) { $this->handleFailure($response); } diff --git a/framework/filters/auth/HttpBearerAuth.php b/framework/filters/auth/HttpBearerAuth.php index d1d56a2..f21fcb3 100644 --- a/framework/filters/auth/HttpBearerAuth.php +++ b/framework/filters/auth/HttpBearerAuth.php @@ -43,7 +43,7 @@ class HttpBearerAuth extends AuthMethod { $authHeader = $request->getHeaders()->get('Authorization'); 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) { $this->handleFailure($response); } diff --git a/framework/filters/auth/QueryParamAuth.php b/framework/filters/auth/QueryParamAuth.php index 9c618d3..4b143b0 100644 --- a/framework/filters/auth/QueryParamAuth.php +++ b/framework/filters/auth/QueryParamAuth.php @@ -30,7 +30,7 @@ class QueryParamAuth extends AuthMethod { $accessToken = $request->get($this->tokenParam); if (is_string($accessToken)) { - $identity = $user->loginByAccessToken($accessToken); + $identity = $user->loginByAccessToken($accessToken, get_class($this)); if ($identity !== null) { return $identity; } diff --git a/framework/i18n/GettextMessageSource.php b/framework/i18n/GettextMessageSource.php index 306da15..4ddd2ba 100644 --- a/framework/i18n/GettextMessageSource.php +++ b/framework/i18n/GettextMessageSource.php @@ -98,9 +98,9 @@ class GettextMessageSource extends MessageSource { $messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog; if ($this->useMoFile) { - $messageFile .= static::MO_FILE_EXT; + $messageFile .= self::MO_FILE_EXT; } else { - $messageFile .= static::PO_FILE_EXT; + $messageFile .= self::PO_FILE_EXT; } return $messageFile; diff --git a/framework/rbac/DbManager.php b/framework/rbac/DbManager.php index c2d31df..2e34a93 100644 --- a/framework/rbac/DbManager.php +++ b/framework/rbac/DbManager.php @@ -21,7 +21,7 @@ use yii\di\Instance; * 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]], diff --git a/framework/rbac/migrations/m140506_102106_rbac_init.php b/framework/rbac/migrations/m140506_102106_rbac_init.php index a05d94d..28af9ac 100644 --- a/framework/rbac/migrations/m140506_102106_rbac_init.php +++ b/framework/rbac/migrations/m140506_102106_rbac_init.php @@ -1,25 +1,42 @@ <?php +use yii\base\InvalidConfigException; use yii\db\Schema; +use yii\rbac\DbManager; 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() { + $authManager = $this->getAuthManager(); + $tableOptions = null; if ($this->db->driverName === 'mysql') { $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', 'data' => Schema::TYPE_TEXT, 'created_at' => Schema::TYPE_INTEGER, 'updated_at' => Schema::TYPE_INTEGER, + 'PRIMARY KEY (name)', ], $tableOptions); - $this->addPrimaryKey('pk-auth_rule', '{{%auth_rule}}', 'name'); - $this->createTable('{{%auth_item}}', [ + $this->createTable($authManager->itemTable, [ 'name' => Schema::TYPE_STRING . '(64) NOT NULL', 'type' => Schema::TYPE_INTEGER . ' NOT NULL', 'description' => Schema::TYPE_TEXT, @@ -27,33 +44,35 @@ class m140506_102106_rbac_init extends \yii\db\Migration 'data' => Schema::TYPE_TEXT, 'created_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); - $this->addPrimaryKey('pk-auth_item', '{{%auth_item}}', 'name'); - $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->createIndex('idx-auth_item-type', $authManager->itemTable, 'type'); - $this->createTable('{{%auth_item_child}}', [ + $this->createTable($authManager->itemChildTable, [ 'parent' => 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); - $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', 'user_id' => Schema::TYPE_STRING . '(64) NOT NULL', '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); - $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() { - $this->dropTable('{{%auth_assignment}}'); - $this->dropTable('{{%auth_item_child}}'); - $this->dropTable('{{%auth_item}}'); - $this->dropTable('{{%auth_rule}}'); + $authManager = $this->getAuthManager(); + + $this->dropTable($authManager->assignmentTable); + $this->dropTable($authManager->itemChildTable); + $this->dropTable($authManager->itemTable); + $this->dropTable($authManager->ruleTable); } } diff --git a/framework/web/IdentityInterface.php b/framework/web/IdentityInterface.php index 423361e..9ab1125 100644 --- a/framework/web/IdentityInterface.php +++ b/framework/web/IdentityInterface.php @@ -21,7 +21,7 @@ namespace yii\web; * return static::findOne($id); * } * - * public static function findIdentityByAccessToken($token) + * public static function findIdentityByAccessToken($token, $type = null) * { * return static::findOne(['access_token' => $token]); * } @@ -59,11 +59,13 @@ interface IdentityInterface /** * Finds an identity by the given 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. * Null should be returned if such an identity cannot be found * 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. * @return string|integer an ID that uniquely identifies a user identity. diff --git a/framework/web/User.php b/framework/web/User.php index 2e375a1..78d25b3 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -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. * Also if the access token is invalid, the user will remain as a guest. * @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 * the access token is invalid. */ - public function loginByAccessToken($token) + public function loginByAccessToken($token, $type = null) { /** @var IdentityInterface $class */ $class = $this->identityClass; - $identity = $class::findIdentityByAccessToken($token); + $identity = $class::findIdentityByAccessToken($token, $type); $this->setIdentity($identity); return $identity; diff --git a/tests/unit/TestCase.php b/tests/unit/TestCase.php index 37b4313..4d9b7aa 100644 --- a/tests/unit/TestCase.php +++ b/tests/unit/TestCase.php @@ -27,7 +27,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase * @param mixed $default default value to use when param is not set. * @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) { static::$params = require(__DIR__ . '/data/config.php'); diff --git a/tests/unit/data/ar/elasticsearch/OrderItem.php b/tests/unit/data/ar/elasticsearch/OrderItem.php index 1e1b6e6..4541559 100644 --- a/tests/unit/data/ar/elasticsearch/OrderItem.php +++ b/tests/unit/data/ar/elasticsearch/OrderItem.php @@ -14,6 +14,8 @@ use yii\elasticsearch\Command; */ class OrderItem extends ActiveRecord { + public $total; + public function attributes() { return ['order_id', 'item_id', 'quantity', 'subtotal']; diff --git a/tests/unit/extensions/elasticsearch/ActiveRecordTest.php b/tests/unit/extensions/elasticsearch/ActiveRecordTest.php index bb95219..125df42 100644 --- a/tests/unit/extensions/elasticsearch/ActiveRecordTest.php +++ b/tests/unit/extensions/elasticsearch/ActiveRecordTest.php @@ -151,8 +151,8 @@ class ActiveRecordTest extends ElasticSearchTestCase 'name' => 'user2', 'address' => 'address2', 'status' => 1, - '_score' => 1.0 - ], $customer); +// '_score' => 1.0 + ], $customer['_source']); } public function testSearch() @@ -174,21 +174,21 @@ class ActiveRecordTest extends ElasticSearchTestCase $this->assertEquals(3, $result['total']); $customers = $result['hits']; $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]); + $this->assertArrayHasKey('id', $customers[0]['_source']); + $this->assertArrayHasKey('name', $customers[0]['_source']); + $this->assertArrayHasKey('email', $customers[0]['_source']); + $this->assertArrayHasKey('address', $customers[0]['_source']); + $this->assertArrayHasKey('status', $customers[0]['_source']); + $this->assertArrayHasKey('id', $customers[1]['_source']); + $this->assertArrayHasKey('name', $customers[1]['_source']); + $this->assertArrayHasKey('email', $customers[1]['_source']); + $this->assertArrayHasKey('address', $customers[1]['_source']); + $this->assertArrayHasKey('status', $customers[1]['_source']); + $this->assertArrayHasKey('id', $customers[2]['_source']); + $this->assertArrayHasKey('name', $customers[2]['_source']); + $this->assertArrayHasKey('email', $customers[2]['_source']); + $this->assertArrayHasKey('address', $customers[2]['_source']); + $this->assertArrayHasKey('status', $customers[2]['_source']); // TODO test asArray() + fields() + indexBy() @@ -387,35 +387,67 @@ class ActiveRecordTest extends ElasticSearchTestCase $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() { /** @var TestCase|ActiveRecordTestTrait $this */ // indexBy + asArray $customers = Customer::find()->asArray()->fields(['id', 'name'])->all(); $this->assertEquals(3, count($customers)); - $this->assertArrayHasKey('id', $customers[0]); - $this->assertArrayHasKey('name', $customers[0]); - $this->assertArrayNotHasKey('email', $customers[0]); - $this->assertArrayNotHasKey('address', $customers[0]); - $this->assertArrayNotHasKey('status', $customers[0]); - $this->assertArrayHasKey('id', $customers[1]); - $this->assertArrayHasKey('name', $customers[1]); - $this->assertArrayNotHasKey('email', $customers[1]); - $this->assertArrayNotHasKey('address', $customers[1]); - $this->assertArrayNotHasKey('status', $customers[1]); - $this->assertArrayHasKey('id', $customers[2]); - $this->assertArrayHasKey('name', $customers[2]); - $this->assertArrayNotHasKey('email', $customers[2]); - $this->assertArrayNotHasKey('address', $customers[2]); - $this->assertArrayNotHasKey('status', $customers[2]); + $this->assertArrayHasKey('id', $customers[0]['fields']); + $this->assertArrayHasKey('name', $customers[0]['fields']); + $this->assertArrayNotHasKey('email', $customers[0]['fields']); + $this->assertArrayNotHasKey('address', $customers[0]['fields']); + $this->assertArrayNotHasKey('status', $customers[0]['fields']); + $this->assertArrayHasKey('id', $customers[1]['fields']); + $this->assertArrayHasKey('name', $customers[1]['fields']); + $this->assertArrayNotHasKey('email', $customers[1]['fields']); + $this->assertArrayNotHasKey('address', $customers[1]['fields']); + $this->assertArrayNotHasKey('status', $customers[1]['fields']); + $this->assertArrayHasKey('id', $customers[2]['fields']); + $this->assertArrayHasKey('name', $customers[2]['fields']); + $this->assertArrayNotHasKey('email', $customers[2]['fields']); + $this->assertArrayNotHasKey('address', $customers[2]['fields']); + $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(); /** @var TestCase|ActiveRecordTestTrait $this */ // 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->assertTrue($customers['user1'] instanceof $customerClass); $this->assertTrue($customers['user2'] instanceof $customerClass); @@ -467,42 +499,89 @@ class ActiveRecordTest extends ElasticSearchTestCase // indexBy + asArray $customers = Customer::find()->indexBy('name')->asArray()->fields('id', 'name')->all(); $this->assertEquals(3, count($customers)); - $this->assertArrayHasKey('id', $customers['user1']); - $this->assertArrayHasKey('name', $customers['user1']); - $this->assertArrayNotHasKey('email', $customers['user1']); - $this->assertArrayNotHasKey('address', $customers['user1']); - $this->assertArrayNotHasKey('status', $customers['user1']); - $this->assertArrayHasKey('id', $customers['user2']); - $this->assertArrayHasKey('name', $customers['user2']); - $this->assertArrayNotHasKey('email', $customers['user2']); - $this->assertArrayNotHasKey('address', $customers['user2']); - $this->assertArrayNotHasKey('status', $customers['user2']); - $this->assertArrayHasKey('id', $customers['user3']); - $this->assertArrayHasKey('name', $customers['user3']); - $this->assertArrayNotHasKey('email', $customers['user3']); - $this->assertArrayNotHasKey('address', $customers['user3']); - $this->assertArrayNotHasKey('status', $customers['user3']); + $this->assertArrayHasKey('id', $customers['user1']['fields']); + $this->assertArrayHasKey('name', $customers['user1']['fields']); + $this->assertArrayNotHasKey('email', $customers['user1']['fields']); + $this->assertArrayNotHasKey('address', $customers['user1']['fields']); + $this->assertArrayNotHasKey('status', $customers['user1']['fields']); + $this->assertArrayHasKey('id', $customers['user2']['fields']); + $this->assertArrayHasKey('name', $customers['user2']['fields']); + $this->assertArrayNotHasKey('email', $customers['user2']['fields']); + $this->assertArrayNotHasKey('address', $customers['user2']['fields']); + $this->assertArrayNotHasKey('status', $customers['user2']['fields']); + $this->assertArrayHasKey('id', $customers['user3']['fields']); + $this->assertArrayHasKey('name', $customers['user3']['fields']); + $this->assertArrayNotHasKey('email', $customers['user3']['fields']); + $this->assertArrayNotHasKey('address', $customers['user3']['fields']); + $this->assertArrayNotHasKey('status', $customers['user3']['fields']); // indexBy callable + asArray $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(); $this->assertEquals(3, count($customers)); - $this->assertArrayHasKey('id', $customers['1-user1']); - $this->assertArrayHasKey('name', $customers['1-user1']); - $this->assertArrayNotHasKey('email', $customers['1-user1']); - $this->assertArrayNotHasKey('address', $customers['1-user1']); - $this->assertArrayNotHasKey('status', $customers['1-user1']); - $this->assertArrayHasKey('id', $customers['2-user2']); - $this->assertArrayHasKey('name', $customers['2-user2']); - $this->assertArrayNotHasKey('email', $customers['2-user2']); - $this->assertArrayNotHasKey('address', $customers['2-user2']); - $this->assertArrayNotHasKey('status', $customers['2-user2']); - $this->assertArrayHasKey('id', $customers['3-user3']); - $this->assertArrayHasKey('name', $customers['3-user3']); - $this->assertArrayNotHasKey('email', $customers['3-user3']); - $this->assertArrayNotHasKey('address', $customers['3-user3']); - $this->assertArrayNotHasKey('status', $customers['3-user3']); + $this->assertArrayHasKey('id', $customers['1-user1']['fields']); + $this->assertArrayHasKey('name', $customers['1-user1']['fields']); + $this->assertArrayNotHasKey('email', $customers['1-user1']['fields']); + $this->assertArrayNotHasKey('address', $customers['1-user1']['fields']); + $this->assertArrayNotHasKey('status', $customers['1-user1']['fields']); + $this->assertArrayHasKey('id', $customers['2-user2']['fields']); + $this->assertArrayHasKey('name', $customers['2-user2']['fields']); + $this->assertArrayNotHasKey('email', $customers['2-user2']['fields']); + $this->assertArrayNotHasKey('address', $customers['2-user2']['fields']); + $this->assertArrayNotHasKey('status', $customers['2-user2']['fields']); + $this->assertArrayHasKey('id', $customers['3-user3']['fields']); + $this->assertArrayHasKey('name', $customers['3-user3']['fields']); + $this->assertArrayNotHasKey('email', $customers['3-user3']['fields']); + $this->assertArrayNotHasKey('address', $customers['3-user3']['fields']); + $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() diff --git a/tests/unit/extensions/elasticsearch/ElasticSearchConnectionTest.php b/tests/unit/extensions/elasticsearch/ElasticSearchConnectionTest.php index b072585..ee44ac9 100644 --- a/tests/unit/extensions/elasticsearch/ElasticSearchConnectionTest.php +++ b/tests/unit/extensions/elasticsearch/ElasticSearchConnectionTest.php @@ -20,7 +20,7 @@ class ElasticSearchConnectionTest extends ElasticSearchTestCase $connection->open(); $this->assertNotNull($connection->activeNode); $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('http_address', reset($connection->nodes)); } diff --git a/tests/unit/extensions/elasticsearch/ElasticSearchTestCase.php b/tests/unit/extensions/elasticsearch/ElasticSearchTestCase.php index bdad9e6..d1a59b8 100644 --- a/tests/unit/extensions/elasticsearch/ElasticSearchTestCase.php +++ b/tests/unit/extensions/elasticsearch/ElasticSearchTestCase.php @@ -17,7 +17,7 @@ class ElasticSearchTestCase extends TestCase { $this->mockApplication(); - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : null; if ($params === null || !isset($params['dsn'])) { $this->markTestSkipped('No elasticsearch server connection configured.'); @@ -40,7 +40,7 @@ class ElasticSearchTestCase extends TestCase */ public function getConnection($reset = true) { - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = isset($databases['elasticsearch']) ? $databases['elasticsearch'] : []; $db = new Connection(); if ($reset) { diff --git a/tests/unit/extensions/elasticsearch/QueryBuilderTest.php b/tests/unit/extensions/elasticsearch/QueryBuilderTest.php index 8238761..bad2387 100644 --- a/tests/unit/extensions/elasticsearch/QueryBuilderTest.php +++ b/tests/unit/extensions/elasticsearch/QueryBuilderTest.php @@ -49,7 +49,7 @@ class QueryBuilderTest extends ElasticSearchTestCase public function testYiiCanBeFoundByQuery() { $this->prepareDbData(); - $queryParts = ['field' => ['title' => 'yii']]; + $queryParts = ['term' => ['title' => 'yii']]; $query = new Query(); $query->from('yiitest', 'article'); $query->query = $queryParts; diff --git a/tests/unit/extensions/elasticsearch/QueryTest.php b/tests/unit/extensions/elasticsearch/QueryTest.php index d631f15..1e853da 100644 --- a/tests/unit/extensions/elasticsearch/QueryTest.php +++ b/tests/unit/extensions/elasticsearch/QueryTest.php @@ -40,16 +40,16 @@ class QueryTest extends ElasticSearchTestCase $this->assertEquals(['name', 'status'], $query->fields); $result = $query->one($this->getConnection()); - $this->assertEquals(2, count($result['_source'])); - $this->assertArrayHasKey('status', $result['_source']); - $this->assertArrayHasKey('name', $result['_source']); + $this->assertEquals(2, count($result['fields'])); + $this->assertArrayHasKey('status', $result['fields']); + $this->assertArrayHasKey('name', $result['fields']); $this->assertArrayHasKey('_id', $result); $query->fields([]); $this->assertEquals([], $query->fields); $result = $query->one($this->getConnection()); - $this->assertEquals([], $result['_source']); + $this->assertArrayNotHasKey('fields', $result); $this->assertArrayHasKey('_id', $result); $query->fields(null); diff --git a/tests/unit/extensions/mongodb/MongoDbTestCase.php b/tests/unit/extensions/mongodb/MongoDbTestCase.php index 669bf58..00f4033 100644 --- a/tests/unit/extensions/mongodb/MongoDbTestCase.php +++ b/tests/unit/extensions/mongodb/MongoDbTestCase.php @@ -34,7 +34,7 @@ class MongoDbTestCase extends TestCase if (!extension_loaded('mongo')) { $this->markTestSkipped('mongo extension required.'); } - $config = $this->getParam('mongodb'); + $config = self::getParam('mongodb'); if (!empty($config)) { $this->mongoDbConfig = $config; } diff --git a/tests/unit/extensions/redis/RedisCacheTest.php b/tests/unit/extensions/redis/RedisCacheTest.php index 6caab91..76025c7 100644 --- a/tests/unit/extensions/redis/RedisCacheTest.php +++ b/tests/unit/extensions/redis/RedisCacheTest.php @@ -22,7 +22,7 @@ class RedisCacheTest extends CacheTestCase */ protected function getCacheInstance() { - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = isset($databases['redis']) ? $databases['redis'] : null; if ($params === null) { $this->markTestSkipped('No redis server connection configured.'); diff --git a/tests/unit/extensions/redis/RedisTestCase.php b/tests/unit/extensions/redis/RedisTestCase.php index 5a2d30c..bd35117 100644 --- a/tests/unit/extensions/redis/RedisTestCase.php +++ b/tests/unit/extensions/redis/RedisTestCase.php @@ -15,7 +15,7 @@ abstract class RedisTestCase extends TestCase { protected function setUp() { - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = isset($databases['redis']) ? $databases['redis'] : null; if ($params === null) { $this->markTestSkipped('No redis server connection configured.'); @@ -36,7 +36,7 @@ abstract class RedisTestCase extends TestCase */ public function getConnection($reset = true) { - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = isset($databases['redis']) ? $databases['redis'] : []; $db = new Connection($params); if ($reset) { diff --git a/tests/unit/extensions/sphinx/SphinxTestCase.php b/tests/unit/extensions/sphinx/SphinxTestCase.php index 6c1e5e0..83b0984 100644 --- a/tests/unit/extensions/sphinx/SphinxTestCase.php +++ b/tests/unit/extensions/sphinx/SphinxTestCase.php @@ -48,7 +48,7 @@ class SphinxTestCase extends TestCase if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) { $this->markTestSkipped('pdo and pdo_mysql extension are required.'); } - $config = $this->getParam('sphinx'); + $config = self::getParam('sphinx'); if (!empty($config)) { $this->sphinxConfig = $config['sphinx']; $this->dbConfig = $config['db']; diff --git a/tests/unit/framework/ar/ActiveRecordTestTrait.php b/tests/unit/framework/ar/ActiveRecordTestTrait.php index e52bb70..5ae9d90 100644 --- a/tests/unit/framework/ar/ActiveRecordTestTrait.php +++ b/tests/unit/framework/ar/ActiveRecordTestTrait.php @@ -70,25 +70,6 @@ trait ActiveRecordTestTrait $this->assertTrue($customers[1] 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 $customer = $customerClass::findOne(2); $this->assertTrue($customer instanceof $customerClass); @@ -136,6 +117,25 @@ trait ActiveRecordTestTrait 'status' => 1, 'profile_id' => null, ], $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() diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index 81bf139..0036ffe 100644 --- a/tests/unit/framework/caching/DbCacheTest.php +++ b/tests/unit/framework/caching/DbCacheTest.php @@ -40,7 +40,7 @@ class DbCacheTest extends CacheTestCase public function getConnection($reset = true) { if ($this->_connection === null) { - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $params = $databases['mysql']; $db = new \yii\db\Connection; $db->dsn = $params['dsn']; diff --git a/tests/unit/framework/db/DatabaseTestCase.php b/tests/unit/framework/db/DatabaseTestCase.php index 8bb677b..b66601e 100644 --- a/tests/unit/framework/db/DatabaseTestCase.php +++ b/tests/unit/framework/db/DatabaseTestCase.php @@ -16,7 +16,7 @@ abstract class DatabaseTestCase extends TestCase protected function setUp() { parent::setUp(); - $databases = $this->getParam('databases'); + $databases = self::getParam('databases'); $this->database = $databases[$this->driverName]; $pdo_database = 'pdo_'.$this->driverName; diff --git a/tests/unit/framework/rbac/DbManagerTestCase.php b/tests/unit/framework/rbac/DbManagerTestCase.php index 4e5334e..150ed7c 100644 --- a/tests/unit/framework/rbac/DbManagerTestCase.php +++ b/tests/unit/framework/rbac/DbManagerTestCase.php @@ -1,6 +1,10 @@ <?php 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\rbac\DbManager; @@ -9,70 +13,97 @@ use yii\rbac\DbManager; */ abstract class DbManagerTestCase extends ManagerTestCase { - protected $database; - protected $driverName = 'mysql'; + protected static $database; + protected static $driverName = 'mysql'; /** * @var Connection */ - protected $db; + protected static $db; - protected function setUp() + protected static function runConsoleAction($route, $params = []) { - parent::setUp(); - $databases = $this->getParam('databases'); - $this->database = $databases[$this->driverName]; - $pdo_database = 'pdo_'.$this->driverName; + if (Yii::$app === null) { + new Application([ + 'id' => 'Migrator', + '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)) { - $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()]); + } protected function tearDown() { parent::tearDown(); - if ($this->db) { - $this->db->close(); - } - $this->destroyApplication(); + $this->auth->removeAll(); } /** - * @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\db\Exception * @throws \yii\base\InvalidConfigException * @return \yii\db\Connection */ - public function getConnection($reset = true, $open = true) + public static function getConnection() { - if (!$reset && $this->db) { - return $this->db; - } - $db = new Connection; - $db->dsn = $this->database['dsn']; - if (isset($this->database['username'])) { - $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 (static::$db == null) { + $db = new Connection; + $db->dsn = static::$database['dsn']; + if (isset(static::$database['username'])) { + $db->username = static::$database['username']; + $db->password = static::$database['password']; } + if (isset(static::$database['attributes'])) { + $db->attributes = static::$database['attributes']; + } + if (!$db->isActive) { + $db->open(); + } + static::$db = $db; } - $this->db = $db; - - return $db; + return static::$db; } } diff --git a/tests/unit/framework/rbac/PgSQLManagerTest.php b/tests/unit/framework/rbac/PgSQLManagerTest.php index fc072c1..4186fe2 100644 --- a/tests/unit/framework/rbac/PgSQLManagerTest.php +++ b/tests/unit/framework/rbac/PgSQLManagerTest.php @@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac; */ class PgSQLManagerTest extends DbManagerTestCase { - protected $driverName = 'pgsql'; + protected static $driverName = 'pgsql'; } diff --git a/tests/unit/framework/rbac/SqliteManagerTest.php b/tests/unit/framework/rbac/SqliteManagerTest.php index 3bc8529..316c46a 100644 --- a/tests/unit/framework/rbac/SqliteManagerTest.php +++ b/tests/unit/framework/rbac/SqliteManagerTest.php @@ -6,5 +6,5 @@ namespace yiiunit\framework\rbac; */ class SqliteManagerTest extends DbManagerTestCase { - protected $driverName = 'sqlite'; + protected static $driverName = 'sqlite'; }