sfPropelMemcache plugin ======================= The `sfPropelMemcache` is a symfony plugin that provides model cache in Propel 1.3. In Propel 1.4 not tested. This plugin overrides the classes generating objects Propel. Details ------- It overwrite four model methods: public static function addInstanceToPool($obj, $key = null); public static function removeInstanceFromPool($value); public static function getInstanceFromPool($key); public static function clearInstancePool(); Model pool after that use memcache to store objects. * After the execute method save stored object is reload. * After execute method delete object deletes from database * Method retrieveByPK loads an object from the cache (or from database) For example: $oUser->save(); //execute addInstanceToPool($this) $oUser->delete(); //execute removeInstanceFromPool() UserPeer::retrieveByPk(); //execute getInstanceFromPool and addInstanceToPool() UserPeer::doDelete(); //clearInstancePool() or removeInstanceFromPool() Links for Russian-speaking: * http://habrahabr.ru/blogs/symfony/74654/ * http://habrahabr.ru/blogs/symfony/74873/ Installation ------------ * Change Propel generation classes in config/propel.ini propel.builder.peer.class = plugins.sfPropelMemcachePlugin.lib.builder.SfPeerBuilderMemcache propel.builder.object.class = plugins.sfPropelMemcachePlugin.lib.builder.SfObjectBuilderMemcache * Add cache: on to table attributes [yml] user: _attributes: { phpName: User, cache: on } * Rebuild model $ symfony propel:build-model Use keys for cache ------------------ * Add parameter cache_key: "column" to any column ( MUST BE UNIQ ) [yml] user: username: { type: varchar, size: 128, required: true, index: unique, cache_key: "username" } * rebuild model $ symfony propel:build-model Keys may be multiple: cache_key: "username+some_column" Configure example ------------------ [yml] sf_guard_user: _attributes: { phpName: sfGuardUser, cache: on } id: ~ username: { type: varchar, size: 128, required: true, index: unique, cache_key: "username" } algorithm: { type: varchar, size: 128, required: true, default: sha1 } salt: { type: varchar, size: 128, required: true } password: { type: varchar, size: 128, required: true } created_at: ~ is_active: { type: boolean, required: true, default: 1 } is_super_admin: { type: boolean, required: true, default: 0 } avatar_id: { type: INTEGER, size: '11', required: false, foreignTable: photo, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT } _indexes: { sf_guard_user_fk: [avatar_id] } sf_guard_user_profile: _attributes: { phpName: sfGuardUserProfile, cache: on } id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true } user_id: { type: INTEGER, size: '11', required: true, foreignTable: sf_guard_user, foreignReference: id, onDelete: CASCADE, onUpdate: RESTRICT, index: unique, cache_key: "user_id" } first_name: { type: VARCHAR, size: '100', required: false } second_name: { type: VARCHAR, size: '100', required: false } last_name: { type: VARCHAR, size: '100', required: false } email: { type: VARCHAR, size: '200', required: true } country_id: { type: INTEGER, size: '11', required: false, foreignTable: country, foreignReference: id, onDelete: RESTRICT, onUpdate: RESTRICT } _indexes: { sf_guard_user_profile_FI_1: [user_id], country_id: [country_id] } _uniques: { ux_email: [email] } photo: _attributes: { phpName: Photo, cache: on } id: { type: INTEGER, size: '11', required: true, primaryKey: true, autoIncrement: true } user_id: { type: INTEGER, size: '11', required: true, foreignKey: true, foreignTable: sf_guard_user, foreignReference: id, onDelete: CASCADE } photo_album_id: { type: INTEGER, size: '11', required: true, foreignKey: true, foreignTable: photo_album, foreignReference: id, onDelete: CASCADE } name: { type: VARCHAR, size: '100', required: false, defaultValue: '' } _indexes: { user_id: [user_id], photo_album_id: [photo_album_id] } photo_album: _attributes: { phpName: PhotoAlbum, cache: on } id: { type: INTEGER, size: '11', required: true, primaryKey: true, autoIncrement: true } user_id: { type: INTEGER, size: '11', required: true, foreignKey: true, foreignTable: sf_guard_user, foreignReference: id, onDelete: CASCADE } name: { type: VARCHAR, size: '100', required: true } _indexes: { user_id: [user_id] } Now: * in photo object and photo_album object method getSfGuardUser() execute sfGuardUserPeer::retrieveByPk(). Yes from cache :) * in user object method getPhoto() execute PhotoPeer::retrieveByPK(); Yes from cache :) * in profile object method getCountry() execute CountryPeer::retrieveByPK() :))))) * add method to retriveByUserId to sfGuardUserProfilePeer and takes profile object from cache ($oUser->getProfile()) [php] public static function retriveByUserId($id) { $key = 'user_id-'.$id; if (null !== ($obj = self::getInstanceFromPool($key))) { return $obj; } $c = new Criteria(); $c->add(self::USER_ID, $id); return parent::doSelectOne($c); } Cache in object --------------- if cache: on in model you can use new generated methods which provide local object cache: * getFromCache($key, $default = null) * setToCache($key, $data) * removeFromCache($key) * clearCustomPropelCache() Example: * We have Articles and tags. Cache will be cleaned then executed method save()! [php] public function getTags() { $key = 'article-tags'; if(is_null($tags = $this->getFromCache($key))) { $links = $this->getArticle2tags(); $tags = array(); foreach ( $links as $link ) { $tags[] = $link->getTagId(); } $this->setToCache($key, serialize($tags)); } //get string from cache if(is_string($tags)) { $tags = unserialize($tags); } $oTags = array(); foreach($tags as $pk) { // get from cache )) $oTags[] = ArticleTagPeer::retrieveByPK($pk); } return $oTags; } NEW 10.02.2010 ============== * Cache in object