From 3a9b6da55b1431d45a540357f0f668b1eb6bc060 Mon Sep 17 00:00:00 2001 From: kk_loke86 Date: Sun, 24 Nov 2013 09:13:50 +0800 Subject: [PATCH 01/11] Update apps-advanced.md Should run migration to import yii tbl_user if not the sign up/login may report error. --- docs/guide/apps-advanced.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/apps-advanced.md b/docs/guide/apps-advanced.md index 890b1e9..5090b00 100644 --- a/docs/guide/apps-advanced.md +++ b/docs/guide/apps-advanced.md @@ -35,8 +35,8 @@ php /path/to/yii-application/init --- 2. Create a new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`. 3. In `common/config/params.php` set your database details in `components.db` values. - -4. Set document roots of your Web server: +4. Apply migrations with console command 'yii migrate'. +5. Set document roots of your Web server: - for frontend `/path/to/yii-application/frontend/web/` and using the URL `http://frontend/` - for backend `/path/to/yii-application/backend/web/` and using the URL `http://backend/` From 69fa7bab6b567400179950079017ea82dbd683fd Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 23 Nov 2013 20:44:36 -0500 Subject: [PATCH 02/11] Fixes #1307: move batchInsert() to base class. --- framework/yii/db/QueryBuilder.php | 25 +++++++++++++++-- framework/yii/db/cubrid/QueryBuilder.php | 47 -------------------------------- framework/yii/db/mysql/QueryBuilder.php | 47 -------------------------------- 3 files changed, 23 insertions(+), 96 deletions(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 0a547ae..d628bc0 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -140,12 +140,33 @@ class QueryBuilder extends \yii\base\Object * @param array $columns the column names * @param array $rows the rows to be batch inserted into the table * @return string the batch INSERT SQL statement - * @throws NotSupportedException if this is not supported by the underlying DBMS */ public function batchInsert($table, $columns, $rows) { - throw new NotSupportedException($this->db->getDriverName() . ' does not support batch insert.'); + if (($tableSchema = $this->db->getTableSchema($table)) !== null) { + $columnSchemas = $tableSchema->columns; + } else { + $columnSchemas = []; + } + + foreach ($columns as $i => $name) { + $columns[$i] = $this->db->quoteColumnName($name); + } + $values = []; + foreach ($rows as $row) { + $vs = []; + foreach ($row as $i => $value) { + if (!is_array($value) && isset($columnSchemas[$columns[$i]])) { + $value = $columnSchemas[$columns[$i]]->typecast($value); + } + $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value; + } + $values[] = '(' . implode(', ', $vs) . ')'; + } + + return 'INSERT INTO ' . $this->db->quoteTableName($table) + . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values); } /** diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index e80e1d6..727cb2d 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -67,51 +67,4 @@ class QueryBuilder extends \yii\db\QueryBuilder throw new InvalidParamException("There is not sequence associated with table '$tableName'."); } } - - /** - * Generates a batch INSERT SQL statement. - * For example, - * - * ~~~ - * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [ - * ['Tom', 30], - * ['Jane', 20], - * ['Linda', 25], - * ])->execute(); - * ~~~ - * - * Note that the values in each row must match the corresponding column names. - * - * @param string $table the table that new rows will be inserted into. - * @param array $columns the column names - * @param array $rows the rows to be batch inserted into the table - * @return string the batch INSERT SQL statement - */ - public function batchInsert($table, $columns, $rows) - { - if (($tableSchema = $this->db->getTableSchema($table)) !== null) { - $columnSchemas = $tableSchema->columns; - } else { - $columnSchemas = []; - } - - foreach ($columns as $i => $name) { - $columns[$i] = $this->db->quoteColumnName($name); - } - - $values = []; - foreach ($rows as $row) { - $vs = []; - foreach ($row as $i => $value) { - if (!is_array($value) && isset($columnSchemas[$columns[$i]])) { - $value = $columnSchemas[$columns[$i]]->typecast($value); - } - $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value; - } - $values[] = '(' . implode(', ', $vs) . ')'; - } - - return 'INSERT INTO ' . $this->db->quoteTableName($table) - . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values); - } } diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 50e717c..27df567 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -140,51 +140,4 @@ class QueryBuilder extends \yii\db\QueryBuilder { return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0); } - - /** - * Generates a batch INSERT SQL statement. - * For example, - * - * ~~~ - * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [ - * ['Tom', 30], - * ['Jane', 20], - * ['Linda', 25], - * ])->execute(); - * ~~~ - * - * Note that the values in each row must match the corresponding column names. - * - * @param string $table the table that new rows will be inserted into. - * @param array $columns the column names - * @param array $rows the rows to be batch inserted into the table - * @return string the batch INSERT SQL statement - */ - public function batchInsert($table, $columns, $rows) - { - if (($tableSchema = $this->db->getTableSchema($table)) !== null) { - $columnSchemas = $tableSchema->columns; - } else { - $columnSchemas = []; - } - - foreach ($columns as $i => $name) { - $columns[$i] = $this->db->quoteColumnName($name); - } - - $values = []; - foreach ($rows as $row) { - $vs = []; - foreach ($row as $i => $value) { - if (!is_array($value) && isset($columnSchemas[$columns[$i]])) { - $value = $columnSchemas[$columns[$i]]->typecast($value); - } - $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value; - } - $values[] = '(' . implode(', ', $vs) . ')'; - } - - return 'INSERT INTO ' . $this->db->quoteTableName($table) - . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values); - } } From 210cbda732ea3e7597fdec07e64cba81b62ccc49 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 23 Nov 2013 23:05:06 -0500 Subject: [PATCH 03/11] Fixes #1296: stricter check of dashes in route. --- framework/yii/base/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php index 1e5302c..3ca3aa2 100644 --- a/framework/yii/base/Module.php +++ b/framework/yii/base/Module.php @@ -623,7 +623,7 @@ abstract class Module extends Component if (isset($this->controllerMap[$id])) { $controller = Yii::createObject($this->controllerMap[$id], $id, $this); - } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { + } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) { $className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller'; $classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php'; if (!is_file($classFile)) { From 337b1b08b5949812aa9f1e89b8556190b099fc45 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 23 Nov 2013 23:22:58 -0500 Subject: [PATCH 04/11] Fixes #1298: supporting route with trailing slash. --- framework/yii/base/Module.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php index 3ca3aa2..1dd243e 100644 --- a/framework/yii/base/Module.php +++ b/framework/yii/base/Module.php @@ -586,7 +586,8 @@ abstract class Module extends Component Yii::$app->controller = $oldController; return $result; } else { - throw new InvalidRouteException('Unable to resolve the request "' . trim($this->getUniqueId() . '/' . $route, '/') . '".'); + $id = $this->getUniqueId(); + throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".'); } } @@ -608,9 +609,8 @@ abstract class Module extends Component if ($route === '') { $route = $this->defaultRoute; } - if (($pos = strpos($route, '/')) !== false) { - $id = substr($route, 0, $pos); - $route = substr($route, $pos + 1); + if (strpos($route, '/') !== false) { + list ($id, $route) = explode('/', $route, 2); } else { $id = $route; $route = ''; From e9fcde32640b27cc18d9cd75ec9d1aa967c0fb50 Mon Sep 17 00:00:00 2001 From: John Was Date: Sun, 24 Nov 2013 11:53:27 +0100 Subject: [PATCH 05/11] support for batch insert in sqlite older than 3.7.11 --- framework/yii/db/sqlite/QueryBuilder.php | 47 ++++++++++++++++++++++ .../framework/db/sqlite/SqliteQueryBuilderTest.php | 6 +++ 2 files changed, 53 insertions(+) diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index 4a5407f..1e1bf61 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -42,6 +42,53 @@ class QueryBuilder extends \yii\db\QueryBuilder ]; /** + * Generates a batch INSERT SQL statement. + * For example, + * + * ~~~ + * $connection->createCommand()->batchInsert('tbl_user', ['name', 'age'], [ + * ['Tom', 30], + * ['Jane', 20], + * ['Linda', 25], + * ])->execute(); + * ~~~ + * + * Note that the values in each row must match the corresponding column names. + * + * @param string $table the table that new rows will be inserted into. + * @param array $columns the column names + * @param array $rows the rows to be batch inserted into the table + * @return string the batch INSERT SQL statement + */ + public function batchInsert($table, $columns, $rows) + { + if (($tableSchema = $this->db->getTableSchema($table)) !== null) { + $columnSchemas = $tableSchema->columns; + } else { + $columnSchemas = []; + } + + foreach ($columns as $i => $name) { + $columns[$i] = $this->db->quoteColumnName($name); + } + + $values = []; + foreach ($rows as $row) { + $vs = []; + foreach ($row as $i => $value) { + if (!is_array($value) && isset($columnSchemas[$columns[$i]])) { + $value = $columnSchemas[$columns[$i]]->typecast($value); + } + $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value; + } + $values[] = implode(', ', $vs); + } + + return 'INSERT INTO ' . $this->db->quoteTableName($table) + . ' (' . implode(', ', $columns) . ') SELECT ' . implode(' UNION ALL ', $values); + } + + /** * Creates a SQL statement for resetting the sequence value of a table's primary key. * The sequence will be reset such that the primary key of the next new row inserted * will have the specified value or 1. diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php index d7904d0..67cae28 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -81,4 +81,10 @@ class SqliteQueryBuilderTest extends QueryBuilderTest $this->setExpectedException('yii\base\NotSupportedException'); parent::testAddDropPrimaryKey(); } + + public function testBatchInsert() + { + $sql = $this->getQueryBuilder()->batchInsert('{{tbl_customer}} t', ['t.id','t.name'], array(array(1,'a'), array(2,'b'))); + $this->assertEquals("INSERT INTO {{tbl_customer}} t ('t'.\"id\", 't'.\"name\") SELECT 1, 'a' UNION ALL 2, 'b'", $sql); + } } From 039ed688cf611dfba85cc0291a82ea1b73c848ae Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 24 Nov 2013 10:23:22 -0500 Subject: [PATCH 06/11] Fixes #1301: fixed scenario generation when there are "except" scenarios. --- framework/yii/base/Model.php | 45 +++++++++++++++++++++++---------- tests/unit/framework/base/ModelTest.php | 38 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index caa6b61..50032ee 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -165,7 +165,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * ] * ~~~ * - * By default, an active attribute that is considered safe and can be massively assigned. + * By default, an active attribute is considered safe and can be massively assigned. * If an attribute should NOT be massively assigned (thus considered unsafe), * please prefix the attribute with an exclamation character (e.g. '!rank'). * @@ -178,29 +178,48 @@ class Model extends Component implements IteratorAggregate, ArrayAccess */ public function scenarios() { - $scenarios = []; - $defaults = []; - /** @var $validator Validator */ + $scenarios = [self::DEFAULT_SCENARIO => []]; foreach ($this->getValidators() as $validator) { - if (empty($validator->on)) { - foreach ($validator->attributes as $attribute) { - $defaults[$attribute] = true; + foreach ($validator->on as $scenario) { + $scenarios[$scenario] = []; + } + foreach ($validator->except as $scenario) { + $scenarios[$scenario] = []; + } + } + $names = array_keys($scenarios); + + foreach ($this->getValidators() as $validator) { + if (empty($validator->on) && empty($validator->except)) { + foreach ($names as $name) { + foreach ($validator->attributes as $attribute) { + $scenarios[$name][$attribute] = true; + } + } + } elseif (empty($validator->on)) { + foreach ($names as $name) { + if (!in_array($name, $validator->except, true)) { + foreach ($validator->attributes as $attribute) { + $scenarios[$name][$attribute] = true; + } + } } } else { - foreach ($validator->on as $scenario) { + foreach ($validator->on as $name) { foreach ($validator->attributes as $attribute) { - $scenarios[$scenario][$attribute] = true; + $scenarios[$name][$attribute] = true; } } } } + foreach ($scenarios as $scenario => $attributes) { - foreach (array_keys($defaults) as $attribute) { - $attributes[$attribute] = true; + if (empty($attributes)) { + unset($scenarios[$scenario]); + } else { + $scenarios[$scenario] = array_keys($attributes); } - $scenarios[$scenario] = array_keys($attributes); } - $scenarios[self::DEFAULT_SCENARIO] = array_keys($defaults); return $scenarios; } diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php index da8495b..d304741 100644 --- a/tests/unit/framework/base/ModelTest.php +++ b/tests/unit/framework/base/ModelTest.php @@ -217,6 +217,20 @@ class ModelTest extends TestCase { $singer = new Singer(); $this->assertEquals(['default' => ['lastName', 'underscore_style']], $singer->scenarios()); + + $scenarios = [ + 'default' => ['id', 'name', 'description'], + 'administration' => ['name', 'description', 'is_disabled'], + ]; + $model = new ComplexModel1(); + $this->assertEquals($scenarios, $model->scenarios()); + $scenarios = [ + 'default' => ['id', 'name', 'description'], + 'suddenlyUnexpectedScenario' => ['name', 'description'], + 'administration' => ['id', 'name', 'description', 'is_disabled'], + ]; + $model = new ComplexModel2(); + $this->assertEquals($scenarios, $model->scenarios()); } public function testIsAttributeRequired() @@ -234,3 +248,27 @@ class ModelTest extends TestCase $invalid->createValidators(); } } + +class ComplexModel1 extends Model +{ + public function rules() + { + return [ + [['id'], 'required', 'except' => 'administration'], + [['name', 'description'], 'filter', 'filter' => 'trim'], + [['is_disabled'], 'boolean', 'on' => 'administration'], + ]; + } +} + +class ComplexModel2 extends Model +{ + public function rules() + { + return [ + [['id'], 'required', 'except' => 'suddenlyUnexpectedScenario'], + [['name', 'description'], 'filter', 'filter' => 'trim'], + [['is_disabled'], 'boolean', 'on' => 'administration'], + ]; + } +} From 858326c0749583b18cc957d108ddc11d1751995b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 24 Nov 2013 11:51:55 -0500 Subject: [PATCH 07/11] Fixed test break. --- framework/yii/base/Model.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 50032ee..502f696 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -220,6 +220,11 @@ class Model extends Component implements IteratorAggregate, ArrayAccess $scenarios[$scenario] = array_keys($attributes); } } + + if (empty($scenarios[self::DEFAULT_SCENARIO])) { + // keep the default scenario so that models without any rules defined can work properly + $scenarios[self::DEFAULT_SCENARIO] = []; + } return $scenarios; } From e4e376ba28c142ed33d1763e78c8b6666f403b51 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 24 Nov 2013 11:52:50 -0500 Subject: [PATCH 08/11] fixed test break. --- framework/yii/base/Model.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 502f696..06f0d1f 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -214,17 +214,13 @@ class Model extends Component implements IteratorAggregate, ArrayAccess } foreach ($scenarios as $scenario => $attributes) { - if (empty($attributes)) { + if (empty($attributes) && $scenario !== self::DEFAULT_SCENARIO) { unset($scenarios[$scenario]); } else { $scenarios[$scenario] = array_keys($attributes); } } - if (empty($scenarios[self::DEFAULT_SCENARIO])) { - // keep the default scenario so that models without any rules defined can work properly - $scenarios[self::DEFAULT_SCENARIO] = []; - } return $scenarios; } From 6920da4a80876618dfb32de4eb5bf89c38ed7275 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 24 Nov 2013 19:22:51 +0100 Subject: [PATCH 09/11] fixed limit/offset for sqlite,mysql and cubrid tests for this are on elasticsearch branch (due to huge refactoring there) and will come later by merge --- framework/yii/db/cubrid/QueryBuilder.php | 20 ++++++++++++++++++++ framework/yii/db/mysql/QueryBuilder.php | 20 ++++++++++++++++++++ framework/yii/db/sqlite/QueryBuilder.php | 19 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 727cb2d..7682516 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -67,4 +67,24 @@ class QueryBuilder extends \yii\db\QueryBuilder throw new InvalidParamException("There is not sequence associated with table '$tableName'."); } } + + /** + * @inheritDocs + */ + public function buildLimit($limit, $offset) + { + $sql = ''; + // limit is not optional in CUBRID + // http://www.cubrid.org/manual/90/en/LIMIT%20Clause + // "You can specify a very big integer for row_count to display to the last row, starting from a specific row." + if ($limit !== null && $limit >= 0) { + $sql = 'LIMIT ' . (int)$limit; + if ($offset > 0) { + $sql .= ' OFFSET ' . (int)$offset; + } + } elseif ($offset > 0) { + $sql = 'LIMIT ' . (int)$offset . ', 18446744073709551615'; // 2^64-1 + } + return $sql; + } } diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 27df567..93a06e3 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -140,4 +140,24 @@ class QueryBuilder extends \yii\db\QueryBuilder { return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0); } + + /** + * @inheritDocs + */ + public function buildLimit($limit, $offset) + { + $sql = ''; + // limit is not optional in MySQL + // http://stackoverflow.com/a/271650/1106908 + // http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240 + if ($limit !== null && $limit >= 0) { + $sql = 'LIMIT ' . (int)$limit; + if ($offset > 0) { + $sql .= ' OFFSET ' . (int)$offset; + } + } elseif ($offset > 0) { + $sql = 'LIMIT ' . (int)$offset . ', 18446744073709551615'; // 2^64-1 + } + return $sql; + } } diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index 4a5407f..d138176 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -206,4 +206,23 @@ class QueryBuilder extends \yii\db\QueryBuilder { throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.'); } + + /** + * @inheritDocs + */ + public function buildLimit($limit, $offset) + { + $sql = ''; + // limit is not optional in SQLite + // http://www.sqlite.org/syntaxdiagrams.html#select-stmt + if ($limit !== null && $limit >= 0) { + $sql = 'LIMIT ' . (int)$limit; + if ($offset > 0) { + $sql .= ' OFFSET ' . (int)$offset; + } + } elseif ($offset > 0) { + $sql = 'LIMIT 9223372036854775807 OFFSET ' . (int)$offset; // 2^63-1 + } + return $sql; + } } From 0b43aa4f8158b47e3fea14d6f44a10b8510f8bf3 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 24 Nov 2013 13:47:40 -0500 Subject: [PATCH 10/11] Fixes #1310: ActiveRelation does not preserve order of items on find via() and viaTable() --- framework/yii/db/ActiveRelationTrait.php | 41 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/framework/yii/db/ActiveRelationTrait.php b/framework/yii/db/ActiveRelationTrait.php index be42eb6..2960196 100644 --- a/framework/yii/db/ActiveRelationTrait.php +++ b/framework/yii/db/ActiveRelationTrait.php @@ -142,35 +142,42 @@ trait ActiveRelationTrait */ private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) { - $buckets = []; - $linkKeys = array_keys($link); - foreach ($models as $i => $model) { - $key = $this->getModelKey($model, $linkKeys); - if ($this->indexBy !== null) { - $buckets[$key][$i] = $model; - } else { - $buckets[$key][] = $model; - } - } - if ($viaModels !== null) { - $viaBuckets = []; + $map = []; $viaLinkKeys = array_keys($viaLink); $linkValues = array_values($link); foreach ($viaModels as $viaModel) { $key1 = $this->getModelKey($viaModel, $viaLinkKeys); $key2 = $this->getModelKey($viaModel, $linkValues); - if (isset($buckets[$key2])) { - foreach ($buckets[$key2] as $i => $bucket) { + $map[$key2][$key1] = true; + } + } + + $buckets = []; + $linkKeys = array_keys($link); + + if (isset($map)) { + foreach ($models as $i => $model) { + $key = $this->getModelKey($model, $linkKeys); + if (isset($map[$key])) { + foreach (array_keys($map[$key]) as $key2) { if ($this->indexBy !== null) { - $viaBuckets[$key1][$i] = $bucket; + $buckets[$key2][$i] = $model; } else { - $viaBuckets[$key1][] = $bucket; + $buckets[$key2][] = $model; } } } } - $buckets = $viaBuckets; + } else { + foreach ($models as $i => $model) { + $key = $this->getModelKey($model, $linkKeys); + if ($this->indexBy !== null) { + $buckets[$key][$i] = $model; + } else { + $buckets[$key][] = $model; + } + } } if (!$this->multiple) { From 1de06faa3bc1a99405e471bbacdaa737ef769476 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 24 Nov 2013 14:18:33 -0500 Subject: [PATCH 11/11] Fixes #1253 --- tests/unit/framework/i18n/FormatterTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index ed5ab33..3d384fc 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -77,8 +77,10 @@ class FormatterTest extends TestCase $this->assertSame('$123.00', $this->formatter->asCurrency($value)); $value = '123.456'; $this->assertSame("$123.46", $this->formatter->asCurrency($value)); - $value = '-123456.123'; - $this->assertSame("($123,456.12)", $this->formatter->asCurrency($value)); + // Starting from ICU 52.1, negative currency value will be formatted as -$123,456.12 + // see: http://source.icu-project.org/repos/icu/icu/tags/release-52-1/source/data/locales/en.txt +// $value = '-123456.123'; +// $this->assertSame("($123,456.12)", $this->formatter->asCurrency($value)); $this->assertSame($this->formatter->nullDisplay, $this->formatter->asCurrency(null)); }