Administrateur PHPfrance |
3131 Messages
14 janv. 2009, 12:28
Déjà tu peux intercaler très proprement une classe entre "BasePeer" et "MonModelePeer", en utilisant l'option "baseClass" dans ton schema.yml (au même endroit que "phpName") :
Quand Propel génèrera le modèle, on aura alors :
-
MonModelePeer extends
BaseMonModelePeer extends
MaBasePeer extends
BasePeer
-
MonModele extends
BaseMonModele extends
BaseObject (il est également possible d'utiliser l'option "baseObject" pour intercaler une classe entre "BaseMonModele" et "BaseObject")
Bref, ça c'est déjà pour te permettre d'étendre BasePeer dans ton modèle sans avoir à faire du copier-coller dans chaque classe de ton modèle

Tu auras par exemple une classe "CachedSelectPeer" que tu appliques comme "basePeer" des classes du modèle que tu veux mettre en cache.
Ensuite, concernant le cache en lui-même, on peut faire quelque chose d'assez générique :
class CachedSelectPeer extends BasePeer
{
/**
* Mise en cache des résultats de requête
*
*/
protected static $cache = null;
/**
* Effectue la requête, mise en cache si possible
*
* @param Criteria $criteria A Criteria.
* @param Connection $con A connection to use.
* @return ResultSet The resultset.
* @throws PropelException
* @see createSelectSql()
*/
public static function doSelect(Criteria $criteria, $con = null)
{
// Chercher dans le cache
if (!($result = self::cachedResult($criteria, $con))) {
// Pas trouvé : on exécute la requête et on met en cache
$result = parent::doSelect($criteria, $con);
self::cacheResult($criteria, $result, $con);
}
return $result;
}
/**
* Retourne le résultat mis en cache de la requête
*
* @param Criteria $criteria
* @param Connection $con
* @return ResultSet
*/
protected static function cachedResult(Criteria $criteria, $con = null)
{
if (!self::$cache) {
self::initCache();
}
// à implémenter
}
/**
* Met en cache un résultat
*
* @param Criteria $criteria
* @param ResultSet $result
* @param Connection $con
* @param int $timeout Expiration du cache (secondes)
*/
protected static function cacheResult(Criteria $criteria, ResultSet $result, $con = null, $timeout = 3600)
{
if (!self::$cache) {
self::initCache();
}
// à implémenter
}
/**
* Initialise le cache
*
*/
protected static function initCache()
{
// à implémenter
}
}
Les éléments "à implémenter" dépendent du cache que tu veux mettre en place :
- un cache basé sur un vulgaire tableau limité à l'exécution en cours (avec self::$cache qui sera un array)
- un cache "persistant" basé sur l'API de sfCache (sfFileCache et serialize peuvent fonctionner ensemble, ça aura l'avantage d'être persistant mais il faut vraiment benchmarker parce que ce n'est pas si sûr qu'on y gagne en perfs), avec la possibilité d'utiliser des plugins comme sfMemCached qui déchire pas mal
Si on part sur un simple tableau, il faudra penser que Criteria est un objet, qu'on ne peut pas se baser sur son "toString" pour avoir une unicité, et donc qu'on ne peut pas avoir un joli tableau associatif bien performant : il faudra à chaque fois le parcourir
initCache :
self::$cache = array();
cachedResult :
foreach (self::$cache as $cache) {
if ($criteria->equals($cache['criteria'])) {
return $cache['result'];
}
}
return false;
cacheResult :
self::$cache[] = array(
'criteria' => $criteria,
'result' => $result,
);
Voilà un exemple d'implémentation de ce que tu veux faire, mais je pense que c'est inutilisable en l'état pour cause de performances qui seront probablement plus dégradées que sans le cache
Note 1 : à ce stade on met en cache les "ResultSet", il peut effectivement être plus intéressant pour toi de mettre en cache les objets Propel déjà "hydratés". Dans ce cas comme cette étape se fait dans "BaseMonModele" tu seras effectivement obligé d'ajouter manuellement une méthode dans "MonModele".
Note 2 : un select de 40'000 éléments, ce n'est pas tant que ce soit lourd à charger côté serveur, mais c'est surtout inutilisable côté client. Du coup, le cache est-il vraiment la solution ?