How I made friends memcache and Propel in Symfony

    This article is written in the continuation of the post “ ORM is evil or How I tried to cache Propel in Symfony ” by user remal .

    As I wrote in a previous article, in order to earn a cache, you must override 4 pool methods that are in Peer objects:

    public static function addInstanceToPool($obj, $key = null);
    public static function removeInstanceFromPool($value);
    public static function getInstanceFromPool($key);
    public static function clearInstancePool();

    * This source code was highlighted with Source Code Highlighter.


    In order not to duplicate the code for each Peer object (as suggested by the remal user), you can use the Propel code generator. When creating a project, you should create a config / propel.ini file, which indicates the classes responsible for generating:

    propel.builder.peer.class = plugins.sfPropelPlugin.lib.builder.SfPeerBuilder
    propel.builder.object.class = plugins.sfPropelPlugin.lib.builder.SfObjectBuilder

    * This source code was highlighted with Source Code Highlighter.


    In our case, these are the SfPeerBuilder and SfObjectBuilder classes, which are located in the sfPropelPlugin plugin.

    When studying these classes, it turned out that they have a set of methods that are called in a specific order. A link to the $ script variable is passed to each of them. These methods are named by the mask add <method_name_in_ generated_object>. For example, addAddInstanceToPool (& $ script).

    So, we create the SfPeerBuilderMemcache class, where we redefine the SfPeerBuilder logic

    class SfPeerBuilderMemcache extends SfPeerBuilder
    {
      protected function addGetPropelCacheStorage(&$script){}
      protected function addRemoveInstanceFromPool(&$script){}
      protected function addClearInstancePool(&$script){}
      protected function addGetInstanceFromPool(&$script){}
    }

    * This source code was highlighted with Source Code Highlighter.


    But since we don’t need to cache the entire model, we will make it possible to enable and disable the cache in the scheme (schema.yml). Example:

    user:
      _attributes:  { phpName: User, cache: on }

    * This source code was highlighted with Source Code Highlighter.


    And add to the SfPeerBuilderMemcache class a method that will check whether the cache for a particular model is turned on or not.

    protected function isPropelCacheEnabled()
    {
      return (boolean)$this->getTable()->getAttribute("cache");
    }

    * This source code was highlighted with Source Code Highlighter.


    I leave the further implementation of this class to those who need it ... well, or look at the source code at the end of the article :)

    But that's not all !!! As I wrote in a previous article, when joins are not used, when receiving a bound object, Propel instead of retriveByPk () makes a request to the database, which is not permissible in our case. To solve this situation, it is necessary to redefine the SfObjectBuilder class, namely the addFKAccessor method, which, if the associated model is cached, will generate the logic we need:

    class SfObjectBuilderMemcache extends SfObjectBuilder
    {
     protected function addFKAccessor(&$script, ForeignKey $fk)
     {
      if(!$this->getForeignTable($fk)->getAttribute("cache"))
      {
       return parent::addFKAccessor($script, $fk);
      }
      
      //что-то делаем
     }
    }

    * This source code was highlighted with Source Code Highlighter.


    Now we just have to change the parameters in the config / propel.ini file to something like this:

    propel.builder.peer.class = plugins.sfPropelMemcachePlugin.lib.builder.SfPeerBuilderMemcache
    propel.builder.object.class = plugins.sfPropelMemcachePlugin.lib.builder.SfObjectBuilderMemcache

    * This source code was highlighted with Source Code Highlighter.


    All! It remains only to rebuild the model.

    Who needs to delve into the source code in more detail .

    What happened in my case?
    I turned on the cache for the user model. Now, if I call the getUser () method on the Photo object, it will call the UserPeer :: retrieveByPK () method. Which will take an object from the cache or from the database, storing it in the cache. By analogy with Photo, all objects associated with User will arrive.

    UPD: Continued
    habrahabr.ru/blogs/symfony/76162

    Also popular now: