Browse Source

finalized Query interface + general cleanup

tags/2.0.0-beta
Carsten Brandt 11 years ago
parent
commit
5164a1671c
  1. 72
      extensions/elasticsearch/ActiveQuery.php
  2. 3
      extensions/elasticsearch/ActiveRecord.php
  3. 16
      extensions/elasticsearch/Cluster.php
  4. 13
      extensions/elasticsearch/Command.php
  5. 2
      extensions/elasticsearch/Connection.php
  6. 13
      extensions/elasticsearch/GuzzleConnection.php
  7. 23
      extensions/elasticsearch/Node.php
  8. 117
      extensions/elasticsearch/Query.php
  9. 29
      extensions/elasticsearch/QueryBuilder.php
  10. 1
      framework/yii/db/ActiveQuery.php
  11. 178
      tests/unit/extensions/elasticsearch/ActiveRecordTest.php
  12. 45
      tests/unit/framework/ar/ActiveRecordTestTrait.php

72
extensions/elasticsearch/ActiveQuery.php

@ -12,24 +12,21 @@ use yii\db\ActiveQueryTrait;
use yii\helpers\Json;
/**
* ActiveQuery represents a query associated with an Active Record class.
* ActiveQuery represents a [[Query]] associated with an [[ActiveRecord]] class.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]]
* and [[ActiveRecord::count()]].
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
* - [[one()]]: returns a single record populated with the first row of data.
* - [[all()]]: returns all records based on the query results.
* - [[count()]]: returns the number of records.
* - [[sum()]]: returns the sum over the specified column.
* - [[average()]]: returns the average over the specified column.
* - [[min()]]: returns the min over the specified column.
* - [[max()]]: returns the max over the specified column.
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
* - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* You can use query methods, such as [[where()]], [[limit()]] and [[orderBy()]] to customize the query options.
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
* [[orderBy()]] to customize the query options.
*
* ActiveQuery also provides the following additional query options:
*
@ -83,16 +80,28 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function all($db = null)
{
$command = $this->createCommand($db);
$result = $command->search();
if (empty($result['hits'])) {
$result = $this->createCommand($db)->search();
if (empty($result['hits']['hits'])) {
return [];
}
$models = $this->createModels($result['hits']);
if ($this->asArray) {
if ($this->fields !== null) {
foreach ($result['hits']['hits'] as &$row) {
$row['_source'] = isset($row['fields']) ? $row['fields'] : [];
unset($row['fields']);
}
unset($row);
}
if ($this->asArray && $this->indexBy) {
foreach ($result['hits']['hits'] as &$row) {
$row['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $row['_id'];
$row = $row['_source'];
}
}
$models = $this->createModels($result['hits']['hits']);
if ($this->asArray && !$this->indexBy) {
foreach($models as $key => $model) {
$model['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $model['_id'];
$models[$key] = $model['_source'];
$models[$key][ActiveRecord::PRIMARY_KEY_NAME] = $model['_id'];
}
}
if (!empty($this->with)) {
@ -133,6 +142,28 @@ class ActiveQuery extends Query implements ActiveQueryInterface
/**
* @inheritDocs
*/
public function search($db = null, $options = [])
{
$result = $this->createCommand($db)->search($options);
if (!empty($result['hits']['hits'])) {
$models = $this->createModels($result['hits']['hits']);
if ($this->asArray) {
foreach($models as $key => $model) {
$model['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $model['_id'];
$models[$key] = $model['_source'];
}
}
if (!empty($this->with)) {
$this->findWith($this->with, $models);
}
$result['hits']['hits'] = $models;
}
return $result;
}
/**
* @inheritDocs
*/
public function scalar($field, $db = null)
{
$record = parent::one($db);
@ -154,12 +185,15 @@ class ActiveQuery extends Query implements ActiveQueryInterface
if ($field == ActiveRecord::PRIMARY_KEY_NAME) {
$command = $this->createCommand($db);
$command->queryParts['fields'] = [];
$rows = $command->search()['hits'];
$result = [];
foreach ($rows as $row) {
$result[] = $row['_id'];
$result = $command->search();
if (empty($result['hits']['hits'])) {
return [];
}
$column = [];
foreach ($result['hits']['hits'] as $row) {
$column[] = $row['_id'];
}
return $result;
return $column;
}
return parent::column($field, $db);
}

3
extensions/elasticsearch/ActiveRecord.php

@ -1,7 +1,7 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
@ -377,7 +377,6 @@ class ActiveRecord extends \yii\db\ActiveRecord
if ($item['update']['ok']) {
$n++;
}
// TODO might want to update the _version in update()
}
return $n;
}

16
extensions/elasticsearch/Cluster.php

@ -1,16 +0,0 @@
<?php
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace yii\elasticsearch;
use yii\base\Object;
class Cluster extends Object
{
// TODO implement http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster.html
}

13
extensions/elasticsearch/Command.php

@ -1,6 +1,8 @@
<?php
/**
* @author Carsten Brandt <mail@cebe.cc>
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
@ -16,10 +18,13 @@ use yii\helpers\Json;
/**
* Class Command
* The Command class implements the API for accessing the elasticsearch REST API.
*
* http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html
* Check the [elasticsearch guide](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/index.html)
* for details on these commands.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Command extends Component
{
@ -61,7 +66,7 @@ class Command extends Component
$this->type !== null ? $this->type : '_all',
'_search'
];
return $this->db->get($url, array_merge($this->options, $options), $query)['hits'];
return $this->db->get($url, array_merge($this->options, $options), $query);
}
/**

2
extensions/elasticsearch/Connection.php

@ -1,7 +1,7 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

13
extensions/elasticsearch/GuzzleConnection.php

@ -1,17 +1,22 @@
<?php
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use Guzzle\Http\Exception\ClientErrorResponseException;
use yii\base\Exception;
use yii\helpers\Json;
/**
* Class GuzzleConnection
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class GuzzleConnection extends Connection
{
/**

23
extensions/elasticsearch/Node.php

@ -1,23 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use yii\base\Object;
/**
* Represents an elastic search cluster node.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Node extends Object
{
public $host;
public $port;
}

117
extensions/elasticsearch/Query.php

@ -9,6 +9,7 @@ namespace yii\elasticsearch;
use Yii;
use yii\base\Component;
use yii\base\NotSupportedException;
use yii\db\QueryInterface;
use yii\db\QueryTrait;
@ -49,16 +50,28 @@ class Query extends Component implements QueryInterface
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-body.html#_parameters_3
*/
public $timeout;
/**
* @var array|string The query part of this search query. This is an array or json string that follows the format of
* the elasticsearch [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html).
*/
public $query;
/**
* @var array|string The filter part of this search query. This is an array or json string that follows the format of
* the elasticsearch [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html).
*/
public $filter;
public $facets = [];
public $facetResults = [];
public $totalCount;
public function init()
{
parent::init();
// setting the default limit according to elasticsearch defaults
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-body.html#_parameters_3
if ($this->limit === null) {
$this->limit = 10;
}
}
/**
* Creates a DB command that can be used to execute this query.
@ -85,8 +98,10 @@ class Query extends Component implements QueryInterface
public function all($db = null)
{
$result = $this->createCommand($db)->search();
// TODO publish facet results
$rows = $result['hits'];
if (empty($result['hits']['hits'])) {
return [];
}
$rows = $result['hits']['hits'];
if ($this->indexBy === null && $this->fields === null) {
return $rows;
}
@ -119,11 +134,10 @@ class Query extends Component implements QueryInterface
{
$options['size'] = 1;
$result = $this->createCommand($db)->search($options);
// TODO publish facet results
if (empty($result['hits'])) {
if (empty($result['hits']['hits'])) {
return false;
}
$record = reset($result['hits']);
$record = reset($result['hits']['hits']);
if ($this->fields !== null) {
$record['_source'] = isset($record['fields']) ? $record['fields'] : [];
unset($record['fields']);
@ -132,6 +146,43 @@ class Query extends Component implements QueryInterface
}
/**
* Executes the query and returns the complete search result including e.g. hits, facets, totalCount.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `elasticsearch` application component will be used.
* @param array $options The options given with this query. Possible options are:
* - [routing](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search.html#search-routing)
* - [search_type](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-search-type.html)
* @return array the query results.
*/
public function search($db = null, $options = [])
{
$result = $this->createCommand($db)->search($options);
if (!empty($result['hits']['hits']) && ($this->indexBy === null || $this->fields === 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);
}
}
$rows[$key] = $row;
}
$result['hits']['hits'] = $rows;
}
return $result;
}
// TODO add query stats http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search.html#stats-groups
// TODO add scroll/scan http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-search-type.html#scan
/**
* Executes the query and deletes all matching documents.
*
* This will not run facet queries.
@ -142,7 +193,8 @@ class Query extends Component implements QueryInterface
*/
public function delete($db = null)
{
// TODO http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
// TODO implement http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
throw new NotSupportedException('Delete by query is not implemented yet.');
}
/**
@ -156,7 +208,7 @@ class Query extends Component implements QueryInterface
*/
public function scalar($field, $db = null)
{
$record = self::one($db);
$record = self::one($db); // TODO limit fields to the one required
if ($record !== false && isset($record['_source'][$field])) {
return $record['_source'][$field];
} else {
@ -175,12 +227,15 @@ class Query extends Component implements QueryInterface
{
$command = $this->createCommand($db);
$command->queryParts['fields'] = [$field];
$rows = $command->search()['hits'];
$result = [];
foreach ($rows as $row) {
$result[] = isset($row['fields'][$field]) ? $row['fields'][$field] : null;
$result = $command->search();
if (empty($result['hits']['hits'])) {
return [];
}
return $result;
$column = [];
foreach ($result['hits']['hits'] as $row) {
$column[] = isset($row['fields'][$field]) ? $row['fields'][$field] : null;
}
return $column;
}
/**
@ -198,7 +253,7 @@ class Query extends Component implements QueryInterface
$options = [];
$options['search_type'] = 'count';
$count = $this->createCommand($db)->search($options)['total'];
$count = $this->createCommand($db)->search($options)['hits']['total'];
if ($this->limit === null && $this->offset === null) {
return $count;
} elseif ($this->offset !== null) {
@ -354,9 +409,26 @@ class Query extends Component implements QueryInterface
// TODO support multi query via static method http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-multi-search.html
public function query()
/**
* Sets the querypart of this search query.
* @param string $query
* @return static
*/
public function query($query)
{
$this->query = $query;
return $this;
}
/**
* Sets the filter part of this search query.
* @param string $filter
* @return static
*/
public function filter($filter)
{
$this->filter = $filter;
return $this;
}
/**
@ -381,7 +453,11 @@ class Query extends Component implements QueryInterface
*/
public function fields($fields)
{
$this->fields = $fields;
if (is_array($fields)) {
$this->fields = $fields;
} else {
$this->fields = func_get_args();
}
return $this;
}
@ -397,5 +473,4 @@ class Query extends Component implements QueryInterface
$this->timeout = $timeout;
return $this;
}
}

29
extensions/elasticsearch/QueryBuilder.php

@ -9,6 +9,7 @@ namespace yii\elasticsearch;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\helpers\Json;
/**
* QueryBuilder builds an elasticsearch query based on the specification given as a [[Query]] object.
@ -55,13 +56,25 @@ class QueryBuilder extends \yii\base\Object
$parts['from'] = (int) $query->offset;
}
$filters = empty($query->filter) ? [] : [$query->filter];
$whereFilter = $this->buildCondition($query->where);
if (!empty($whereFilter)) {
$filters[] = $whereFilter;
if (empty($parts['query'])) {
$parts['query'] = ["match_all" => (object)[]];
}
if (!empty($filters)) {
$parts['filter'] = count($filters) > 1 ? ['and' => $filters] : $filters[0];
$whereFilter = $this->buildCondition($query->where);
if (is_string($query->filter)) {
if (empty($whereFilter)) {
$parts['filter'] = $query->filter;
} else {
$parts['filter'] = '{"and": [' . $query->filter . ', ' . Json::encode($whereFilter) . ']}';
}
} elseif ($query->filter !== null) {
if (empty($whereFilter)) {
$parts['filter'] = $query->filter;
} else {
$parts['filter'] = ['and' => [$query->filter, $whereFilter]];
}
} elseif (!empty($whereFilter)) {
$parts['filter'] = $whereFilter;
}
$sort = $this->buildOrderBy($query->orderBy);
@ -69,8 +82,8 @@ class QueryBuilder extends \yii\base\Object
$parts['sort'] = $sort;
}
if (empty($parts['query'])) {
$parts['query'] = ["match_all" => (object)[]];
if (!empty($query->facets)) {
$parts['facets'] = $query->facets;
}
$options = [];

1
framework/yii/db/ActiveQuery.php

@ -23,6 +23,7 @@ namespace yii\db;
* - [[min()]]: returns the min over the specified column.
* - [[max()]]: returns the max over the specified column.
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
* - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],

178
tests/unit/extensions/elasticsearch/ActiveRecordTest.php

@ -132,6 +132,62 @@ class ActiveRecordTest extends ElasticSearchTestCase
$db->createCommand()->flushIndex('yiitest');
}
public function testSearch()
{
$customers = $this->callCustomerFind()->search()['hits'];
$this->assertEquals(3, $customers['total']);
$this->assertEquals(3, count($customers['hits']));
$this->assertTrue($customers['hits'][0] instanceof Customer);
$this->assertTrue($customers['hits'][1] instanceof Customer);
$this->assertTrue($customers['hits'][2] instanceof Customer);
// limit vs. totalcount
$customers = $this->callCustomerFind()->limit(2)->search()['hits'];
$this->assertEquals(3, $customers['total']);
$this->assertEquals(2, count($customers['hits']));
// asArray
$result = $this->callCustomerFind()->asArray()->search()['hits'];
$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]);
// TODO test asArray() + fields() + indexBy()
// find by attributes
$result = $this->callCustomerFind()->where(['name' => 'user2'])->search()['hits'];
$customer = reset($result['hits']);
$this->assertTrue($customer instanceof Customer);
$this->assertEquals(2, $customer->id);
// TODO test query() and filter()
}
public function testSearchFacets()
{
$result = $this->callCustomerFind()->addStatisticalFacet('status_stats', ['field' => 'status'])->search();
$this->assertArrayHasKey('facets', $result);
$this->assertEquals(3, $result['facets']['status_stats']['count']);
$this->assertEquals(4, $result['facets']['status_stats']['total']); // sum of values
$this->assertEquals(1, $result['facets']['status_stats']['min']);
$this->assertEquals(2, $result['facets']['status_stats']['max']);
}
public function testGetDb()
{
$this->mockApplication(['components' => ['elasticsearch' => Connection::className()]]);
@ -314,4 +370,126 @@ class ActiveRecordTest extends ElasticSearchTestCase
$customers = $this->callCustomerFind()->where(['status' => false])->all();
$this->assertEquals(2, count($customers));
}
public function testfindAsArrayFields()
{
$customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = $this->callCustomerFind()->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]);
}
public function testfindIndexByFields()
{
$customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = $this->callCustomerFind()->indexBy('name')->fields('id', 'name')->all();
$this->assertEquals(3, count($customers));
$this->assertTrue($customers['user1'] instanceof $customerClass);
$this->assertTrue($customers['user2'] instanceof $customerClass);
$this->assertTrue($customers['user3'] instanceof $customerClass);
$this->assertNotNull($customers['user1']->id);
$this->assertNotNull($customers['user1']->name);
$this->assertNull($customers['user1']->email);
$this->assertNull($customers['user1']->address);
$this->assertNull($customers['user1']->status);
$this->assertNotNull($customers['user2']->id);
$this->assertNotNull($customers['user2']->name);
$this->assertNull($customers['user2']->email);
$this->assertNull($customers['user2']->address);
$this->assertNull($customers['user2']->status);
$this->assertNotNull($customers['user3']->id);
$this->assertNotNull($customers['user3']->name);
$this->assertNull($customers['user3']->email);
$this->assertNull($customers['user3']->address);
$this->assertNull($customers['user3']->status);
// indexBy callable + asArray
$customers = $this->callCustomerFind()->indexBy(function ($customer) {
return $customer->id . '-' . $customer->name;
})->fields('id', 'name')->all();
$this->assertEquals(3, count($customers));
$this->assertTrue($customers['1-user1'] instanceof $customerClass);
$this->assertTrue($customers['2-user2'] instanceof $customerClass);
$this->assertTrue($customers['3-user3'] instanceof $customerClass);
$this->assertNotNull($customers['1-user1']->id);
$this->assertNotNull($customers['1-user1']->name);
$this->assertNull($customers['1-user1']->email);
$this->assertNull($customers['1-user1']->address);
$this->assertNull($customers['1-user1']->status);
$this->assertNotNull($customers['2-user2']->id);
$this->assertNotNull($customers['2-user2']->name);
$this->assertNull($customers['2-user2']->email);
$this->assertNull($customers['2-user2']->address);
$this->assertNull($customers['2-user2']->status);
$this->assertNotNull($customers['3-user3']->id);
$this->assertNotNull($customers['3-user3']->name);
$this->assertNull($customers['3-user3']->email);
$this->assertNull($customers['3-user3']->address);
$this->assertNull($customers['3-user3']->status);
}
public function testfindIndexByAsArrayFields()
{
$customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = $this->callCustomerFind()->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']);
// indexBy callable + asArray
$customers = $this->callCustomerFind()->indexBy(function ($customer) {
return $customer['id'] . '-' . $customer['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']);
}
}

45
tests/unit/framework/ar/ActiveRecordTestTrait.php

@ -199,6 +199,51 @@ trait ActiveRecordTestTrait
$this->assertTrue($customers['3-user3'] instanceof $customerClass);
}
public function testfindIndexByAsArray()
{
$customerClass = $this->getCustomerClass();
/** @var TestCase|ActiveRecordTestTrait $this */
// indexBy + asArray
$customers = $this->callCustomerFind()->asArray()->indexBy('name')->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['user1']);
$this->assertArrayHasKey('name', $customers['user1']);
$this->assertArrayHasKey('email', $customers['user1']);
$this->assertArrayHasKey('address', $customers['user1']);
$this->assertArrayHasKey('status', $customers['user1']);
$this->assertArrayHasKey('id', $customers['user2']);
$this->assertArrayHasKey('name', $customers['user2']);
$this->assertArrayHasKey('email', $customers['user2']);
$this->assertArrayHasKey('address', $customers['user2']);
$this->assertArrayHasKey('status', $customers['user2']);
$this->assertArrayHasKey('id', $customers['user3']);
$this->assertArrayHasKey('name', $customers['user3']);
$this->assertArrayHasKey('email', $customers['user3']);
$this->assertArrayHasKey('address', $customers['user3']);
$this->assertArrayHasKey('status', $customers['user3']);
// indexBy callable + asArray
$customers = $this->callCustomerFind()->indexBy(function ($customer) {
return $customer['id'] . '-' . $customer['name'];
})->asArray()->all();
$this->assertEquals(3, count($customers));
$this->assertArrayHasKey('id', $customers['1-user1']);
$this->assertArrayHasKey('name', $customers['1-user1']);
$this->assertArrayHasKey('email', $customers['1-user1']);
$this->assertArrayHasKey('address', $customers['1-user1']);
$this->assertArrayHasKey('status', $customers['1-user1']);
$this->assertArrayHasKey('id', $customers['2-user2']);
$this->assertArrayHasKey('name', $customers['2-user2']);
$this->assertArrayHasKey('email', $customers['2-user2']);
$this->assertArrayHasKey('address', $customers['2-user2']);
$this->assertArrayHasKey('status', $customers['2-user2']);
$this->assertArrayHasKey('id', $customers['3-user3']);
$this->assertArrayHasKey('name', $customers['3-user3']);
$this->assertArrayHasKey('email', $customers['3-user3']);
$this->assertArrayHasKey('address', $customers['3-user3']);
$this->assertArrayHasKey('status', $customers['3-user3']);
}
public function testRefresh()
{
$customerClass = $this->getCustomerClass();

Loading…
Cancel
Save