Symfony, Propel und SQL Aggregate Functions

Da ich jetzt fast zwei Stunden gebraucht habe um einen GROUP BY und COUNT query in Symfony hinzukriegen, hier mal die Erklärung dazu.

Bei Google hab ich auch nichts sonderlich hilfreiches gefunden, bis auf einen Beitrag:
hier … und da die letzte Antwort.

Will man also einen Query wie folgt mit Propel bauen:


SELECT referer.ID, referer.IP, referer.CREATED_AT, count(*) AS cnt

FROM `referer`

WHERE referer.CREATED_AT >'2009-07-22 00:00:00'

GROUP BY referer.URL

ORDER BY referer.CREATED_AT ASC LIMIT 25

macht man so:

$c = new Criteria();
$c->addAsColumn('cnt', "count(*)");
RefererPeer::addSelectColumns($c);
$c->add(RefererPeer::CREATED_AT,date('Y-m-d').' 00:00:00',Criteria::GREATER_THAN);
$c->addAscendingOrderByColumn(RefererPeer::CREATED_AT);
$c->addGroupByColumn(RefererPeer::URL);
$c->setLimit(sfConfig::get('max_refererdashboard',25));
$called = RefererPeer::doSelect($c);

Die Objekte im Ergebnis Array enthalten nun aber nicht die neue Column ‘cnt‘.
Die muss man dem Model noch beibringen.

Um das Model Objekt zu erstellen benutzt Symfony eine hydrate() Funktion aus der Base Klasse.
Die muss im Model überschrieben werden und darin die neue Column cnt in das Ergebnis Objekt aufgenommen werden.

ungefähr so:

class Referer extends BaseReferer
{
public function hydrate($row, $startcol = 0, $rehydrate = false)
{
$r = parent::hydrate($row, $startcol, $rehydrate);
$this->cnt = $row[7];
return $r;
}
}

Dann gehts!

One Reply to “Symfony, Propel und SQL Aggregate Functions”

  1. Zufall! Ich hatte exakt das gleiche Problem und habe auch mit dem Gedanken gespielt, dazu was zu schreiben und mein Leid zu klagen. In dem Falle ging es um eine simple Aggregatfunktion (LEFT JOIN, dann count(Pk) über eine Rating-(Ziel-)Tabelle), dann nach Pk der Quelltabelle sortiert.

    Also vereinfacht.

    SELECT a.*, count(b.id) c rating FROM a
    LEFT JOIN b ON b.a_id = a.id
    GROUP BY a.id
    ORDER BY c
    

    Natürlich fliegt einem populateObjects() aufgrund der veränderten Anzahl Attribute um die Ohren.

    Die Lösung in dem Fall war einfach: count(b.id) aus dem SELECT Statement herausnehmen (also addAsColumn() entfernen) und in ->addDescendingOrder(‘count(‘.bPeer::ID.’)’) hineinfummeln.

    Wahrscheinlich ist es nur ein unglaublicher Zufall, dass das überhaupt funktioniert :D

    Komplexe Custom Queries sind einfach nicht gut Freund mit Propel, was sich aber in der kürzlich angekündigten 1.4 bzw. PHP5.3-kompatiblen 2.0 Branch grundlegend ändern soll. Man wird sehen, was da kommt.

Comments are closed.