Aggregate Column Behavior
The aggregate_column
behavior keeps a column updated using an aggregate function executed on a related table.
Basic Usage
In the schema.xml
, use the <behavior>
tag to add the aggregate_column
behavior to a table. You must provide parameters for the aggregate column name
, the foreign table name, and the aggegate expression
. For instance, to add an aggregate column keeping the comment count in a post
table:
<table name="post">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="title" type="VARCHAR" required="true" primaryString="true" />
<behavior name="aggregate_column">
<parameter name="name" value="nb_comments" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="COUNT(id)" />
</behavior>
</table>
<table name="comment">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="post_id" type="INTEGER" />
<foreign-key foreignTable="post" onDelete="cascade">
<reference local="post_id" foreign="id" />
</foreign-key>
</table>
Rebuild your model, and insert the table creation sql again. The model now has an additional nb_comments
column, of type integer
by default. And each time an record from the foreign table is added, modified, or removed, the aggregate column is updated:
<?php
$post = new Post();
$post->setTitle('How Is Life On Earth?');
$post->save();
echo $post->getNbComments(); // 0
$comment1 = new Comment();
$comment1->setPost($post);
$comment1->save();
echo $post->getNbComments(); // 1
$comment2 = new Comment();
$comment2->setPost($post);
$comment2->save();
echo $post->getNbComments(); // 2
$comment2->delete();
echo $post->getNbComments(); // 1
The aggregate column is also kept up to date when related records get modified through a Query object:
<?php
CommentQuery::create()
->filterByPost($post)
->delete():
echo $post->getNbComments(); // 0
Customizing The Aggregate Calculation
Any aggregate function can be used on any of the foreign columns. For instance, you can use the aggregate_column
behavior to keep the latest update date of the related comments, or the total votes on the comments. You can even keep several aggregate columns in a single table:
<table name="post">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="title" type="VARCHAR" required="true" primaryString="true" />
<behavior name="aggregate_column">
<parameter name="name" value="nb_comments" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="COUNT(id)" />
</behavior>
<behavior name="aggregate_column">
<parameter name="name" value="last_comment" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="MAX(created_at)" />
</behavior>
<behavior name="aggregate_column">
<parameter name="name" value="total_votes" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="SUM(vote)" />
</behavior>
</table>
<table name="comment">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="post_id" type="INTEGER" />
<foreign-key foreignTable="post" onDelete="cascade">
<reference local="post_id" foreign="id" />
</foreign-key>
<column name="created_at" type="TIMESTAMP" />
<column name="vote" type="INTEGER" />
</table>
The behavior adds a computeXXX()
method to the Post
class to compute the value of the aggregate function. This method, called each time records are modified in the related comment
table, is the translation of the behavior settings into a SQL query:
<?php
// in om/BasePost.php
public function computeNbComments(PropelPDO $con)
{
$stmt = $con->prepare('SELECT COUNT(id) FROM `comment` WHERE comment.POST_ID = :p1');
$stmt->bindValue(':p1', $this->getId());
$stmt->execute();
return $stmt->fetchColumn();
}
You can override this method in the model class to customize the aggregate column calculation.
Additional Condition
What if you use your own soft deletion and want to calculate only comments which are not marked as deleted? It is possible to add a custom SQL condition:
<table name="post">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="title" type="VARCHAR" required="true" primaryString="true" />
<behavior name="aggregate_column">
<parameter name="name" value="nb_comments" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="COUNT(id)" />
<parameter name="condition" value="is_deleted = false" />
</behavior>
</table>
Which will result in generated SQL query:
<?php
// in om/BasePost.php
public function computeNbComments(PropelPDO $con)
{
$stmt = $con->prepare('SELECT COUNT(id) FROM `comment` WHERE is_deleted = false AND comment.POST_ID = :p1');
$stmt->bindValue(':p1', $this->getId());
$stmt->execute();
return $stmt->fetchColumn();
}
Customizing The Aggregate Column
By default, the behavior adds one columns to the model. If this column is already described in the schema, the behavior detects it and doesn't add it a second time. This can be useful if you need to use a custom type
or phpName
for the aggregate column:
<table name="post">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="title" type="VARCHAR" required="true" primaryString="true" />
<column name="nb_comments" phpName="CommentCount" type="INTEGER" />
<behavior name="aggregate_column">
<parameter name="name" value="nb_comments" />
<parameter name="foreign_table" value="comment" />
<parameter name="expression" value="COUNT(id)" />
</behavior>
</table>
Found a typo ? Something is wrong in this documentation ? Just fork and edit it !