mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 06:15:19 +08:00
abort removing duplicate records when pk is not in result set
in this cases it does not make sense to remove duplicates as the result is not on record level anymore. This could be the case after GROUP BY has been applied. fixes #8772
This commit is contained in:
@ -20,6 +20,7 @@ Yii Framework 2 Change Log
|
||||
- Bug #8606: Fixed `yii\web\Response::xSendFile()` does not reset format (vyants)
|
||||
- Bug #8627: Fixed `yii\db\Migration` produces incorrect results due to table schema caching (klimov-paul)
|
||||
- Bug #8661: Fixed `yii.activeForm.js` scrolling to top (nkovacs)
|
||||
- Bug #8772: ActiveQuery failed removing duplicate records after join when the resultset did not contain the pk values e.g. after grouping (cebe)
|
||||
- Bug: Fixed string comparison in `BaseActiveRecord::unlink()` which may result in wrong comparison result for hash valued primary keys starting with `0e` (cebe)
|
||||
- Bug: Pass correct action name to `yii\console\Controller::options()` when default action was requested (cebe)
|
||||
- Bug: Automatic garbage collection in `yii\caching\FileCache` was not triggered (kidol)
|
||||
|
@ -248,9 +248,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
||||
$pks = $class::primaryKey();
|
||||
|
||||
if (count($pks) > 1) {
|
||||
// composite primary key
|
||||
foreach ($models as $i => $model) {
|
||||
$key = [];
|
||||
foreach ($pks as $pk) {
|
||||
if (!isset($model[$pk])) {
|
||||
// do not continue if the primary key is not part of the result set
|
||||
break 2;
|
||||
}
|
||||
$key[] = $model[$pk];
|
||||
}
|
||||
$key = serialize($key);
|
||||
@ -263,8 +268,13 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
||||
} elseif (empty($pks)) {
|
||||
throw new InvalidConfigException("Primary key of '{$class}' can not be empty.");
|
||||
} else {
|
||||
// single column primary key
|
||||
$pk = reset($pks);
|
||||
foreach ($models as $i => $model) {
|
||||
if (!isset($model[$pk])) {
|
||||
// do not continue if the primary key is not part of the result set
|
||||
break;
|
||||
}
|
||||
$key = $model[$pk];
|
||||
if (isset($hash[$key])) {
|
||||
unset($models[$i]);
|
||||
|
@ -22,6 +22,8 @@ class Customer extends ActiveRecord
|
||||
|
||||
public $status2;
|
||||
|
||||
public $sumTotal;
|
||||
|
||||
public static function tableName()
|
||||
{
|
||||
return 'customer';
|
||||
@ -32,6 +34,11 @@ class Customer extends ActiveRecord
|
||||
return $this->hasOne(Profile::className(), ['id' => 'profile_id']);
|
||||
}
|
||||
|
||||
public function getOrdersPlain()
|
||||
{
|
||||
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
|
||||
}
|
||||
|
||||
public function getOrders()
|
||||
{
|
||||
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('id');
|
||||
|
@ -736,4 +736,88 @@ class ActiveRecordTest extends DatabaseTestCase
|
||||
$this->setExpectedException('yii\db\StaleObjectException');
|
||||
$record->save(false);
|
||||
}
|
||||
|
||||
public function testPopulateWithoutPk()
|
||||
{
|
||||
// tests with single pk asArray
|
||||
$aggregation = Customer::find()
|
||||
->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumtotal]]'])
|
||||
->joinWith('ordersPlain', false)
|
||||
->groupBy('{{customer}}.[[status]]')
|
||||
->orderBy('status')
|
||||
->asArray()
|
||||
->all();
|
||||
|
||||
$expected = [
|
||||
[
|
||||
'status' => 1,
|
||||
'sumtotal' => 183,
|
||||
],
|
||||
[
|
||||
'status' => 2,
|
||||
'sumtotal' => 0,
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $aggregation);
|
||||
|
||||
// tests with single pk with Models
|
||||
$aggregation = Customer::find()
|
||||
->select(['{{customer}}.[[status]]', 'SUM({{order}}.[[total]]) AS [[sumTotal]]'])
|
||||
->joinWith('ordersPlain', false)
|
||||
->groupBy('{{customer}}.[[status]]')
|
||||
->orderBy('status')
|
||||
->all();
|
||||
$this->assertCount(2, $aggregation);
|
||||
$this->assertContainsOnlyInstancesOf(Customer::className(), $aggregation);
|
||||
foreach($aggregation as $item) {
|
||||
if ($item->status == 1) {
|
||||
$this->assertEquals(183, $item->sumTotal);
|
||||
} elseif ($item->status == 2) {
|
||||
$this->assertEquals(0, $item->sumTotal);
|
||||
}
|
||||
}
|
||||
|
||||
// tests with composite pk asArray
|
||||
$aggregation = OrderItem::find()
|
||||
->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
|
||||
->joinWith('order', false)
|
||||
->groupBy('[[order_id]]')
|
||||
->orderBy('[[order_id]]')
|
||||
->asArray()
|
||||
->all();
|
||||
$expected = [
|
||||
[
|
||||
'order_id' => 1,
|
||||
'subtotal' => 70,
|
||||
],
|
||||
[
|
||||
'order_id' => 2,
|
||||
'subtotal' => 33,
|
||||
],
|
||||
[
|
||||
'order_id' => 3,
|
||||
'subtotal' => 40,
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected, $aggregation);
|
||||
|
||||
// tests with composite pk with Models
|
||||
$aggregation = OrderItem::find()
|
||||
->select(['[[order_id]]', 'SUM([[subtotal]]) AS [[subtotal]]'])
|
||||
->joinWith('order', false)
|
||||
->groupBy('[[order_id]]')
|
||||
->orderBy('[[order_id]]')
|
||||
->all();
|
||||
$this->assertCount(3, $aggregation);
|
||||
$this->assertContainsOnlyInstancesOf(OrderItem::className(), $aggregation);
|
||||
foreach($aggregation as $item) {
|
||||
if ($item->order_id == 1) {
|
||||
$this->assertEquals(70, $item->subtotal);
|
||||
} elseif ($item->order_id == 2) {
|
||||
$this->assertEquals(33, $item->subtotal);
|
||||
} elseif ($item->order_id == 3) {
|
||||
$this->assertEquals(40, $item->subtotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user