Revision #43 has been created by rackycz on Oct 5, 2020, 7:45:00 PM with the memo:
Child aggregation in Parent GridView
« previous (#42) next (#44) »
Changes
Title
unchanged
Yii v2 snippet guide II
Category
unchanged
Tutorials
Yii version
unchanged
2.0
Tags
unchanged
tutorial,beginner,yii2,snippets
Content
changed
[...]
],
],
```
There is only one formatter in the whole of Yii and you can extend it = you can add more methods and the rest of the formatter will remain so you can use all other methods as mentioned in documentation.
**Displaying SUM of child models in a GridView with parent models**
---
... can be easily done by adding a MySQL VIEW into your DB, creating a model for it and using it in the "ParentSearch" model as the base class.
Let's show it on list of invoices and their items. Invoices are in table "invoice" (model Invoice) and their items in "invoice_item" (model InvoiceItem). Now we need to join them and sort and filter them by SUM of prices (amounts). To avoid calculations in PHP, DB can do it for us if we prepare a MySQL VIEW:
```sql
CREATE VIEW v_invoice AS
SELECT invoice.*,
SUM(invoice_item.units * invoice_item.price_per_unit) as amount,
COUNT(invoice_item.id) as items
FROM invoice
LEFT JOIN invoice_item
ON (invoice.id = invoice_item.id_invoice)
GROUP BY invoice.id
```
**Note:** Here you can read why it is better not to use COUNT(\*) in LEFT JOIN:
- [https://learnsql.com/blog/introduction-using-aggregate-functions-joins/](https://learnsql.com/blog/introduction-using-aggregate-functions-joins/)
- Chapter: Dealing with NULLs
This will technically clone the original table "invoice" into "v_invoice" and will append 2 calculated columns: "amount" + "items". Now you can easily use this VIEW as a TABLE (for reading only) and display it in a GridView. If you already have a GridView for table "invoice" the change is just tiny. Create this model:
```php
<?php
namespace app\models;
class v_Invoice extends Invoice
{
public static function primaryKey()
{
// here is specified which column(s) create the fictive primary key in the mysql-view
return ['id'];
}
public static function tableName()
{
return 'v_invoice';
}
}
```
.. and in model InvoiceSearch replace word Invoice with v_Invoice (on 2 places I guess) plus add rules for those new columns. Example:
```php
public function rules()
{
return [
// ...
[['amount'], 'number'], // decimal
[['items'], 'integer'],
];
}
```
Into method search() add HAVING condition if you want to filter by amount or items:
```php
if (trim($this->amount)!=='') {
$query->andHaving([
'amount' => $this->amount
]);
}
```
In the GridView you can now use the columns "amount" and "items" as native columns. Filtering and sorting will work.
**Danger:** Read below how to search and sort by related columns. This might stop working if you want to join your MySQL with another table.
> I believe this approach is the simplest to reach the goal. The advantage is that the MySQL VIEW is only used when search() method is called - it means in the list of invoices. Other parts of the web are not influenced because they use the original Invoice model. But if you need some special method from the Invoice model, you have it also in v_Invoice. If data is saved or changed, you must always modify the original table "invoice".