Development

Changeset 8458

You must first sign up to be able to contribute.

Changeset 8458

Show
Ignore:
Timestamp:
04/14/08 23:18:23 (5 months ago)
Author:
naholyr
Message:

[nahoPropelOptimizerPlugin]
- Updated README (ready for publishing)
- Added support for returning NULL instead of NULLs-hydrated object (in case of
LEFT JOIN with non-existing related object)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/nahoPropelOptimizerPlugin/README

    r8156 r8458  
    1 Current bundled optimizations, and the defect they fix : 
    2 - Remove useless includes in Peer and MapBuilder (causing overhead) 
    3 - Change implicit joins to explicit LEFT if key has required:false or INNER  
    4 if key has required:true (default implicit inner for everything causing  
    5 errors in results of doSelectJoinXXX) 
    6 - Remove calls to Propel::import (causing the impossibility of overriding a  
    7 plugin's model's class by simply copying it into project's lib) 
     1= nahoPropelOptimizerPlugin plugin = 
    82 
    9 In :      config/propel.ini 
    10 Replace : addon.propel.builder.Sf* 
    11 With :    plugins.nahoPropelOptimizerPlugin.lib.SfOptimized* 
     3This `nahoPropelOptimizerPlugin` plugin fixes a few defects (not to say "bugs") in Propel model generation, that are not yet handled by Symfony builder wrapping. 
     4 
     5== Instalation == 
     6 
     7  * Install the plugin 
     8     
     9    {{{ 
     10      symfony plugin-install http://plugins.symfony-project.com/nahoPropelOptimizerPlugin 
     11    }}} 
     12     
     13    Like any other plugin, you can either extract one of the attached archives, or use Subversion. 
     14   
     15  * Edit `config/propel.ini` and find the following lines : 
     16     
     17    {{{ 
     18      ; builder settings 
     19      propel.builder.peer.class              = addon.propel.builder.SfPeerBuilder 
     20      propel.builder.object.class            = addon.propel.builder.SfObjectBuilder 
     21       
     22      propel.builder.objectstub.class        = addon.propel.builder.SfExtensionObjectBuilder 
     23      propel.builder.peerstub.class          = addon.propel.builder.SfExtensionPeerBuilder 
     24      propel.builder.objectmultiextend.class = addon.propel.builder.SfMultiExtendObjectBuilder 
     25      propel.builder.mapbuilder.class        = addon.propel.builder.SfMapBuilderBuilder 
     26    }}} 
     27     
     28    And replace `addon.propel.builder.Sf` with `plugins.nahoPropelOptimizerPlugin.lib.SfOptimized` : 
     29     
     30    {{{ 
     31      ; builder settings 
     32      propel.builder.peer.class              = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedPeerBuilder 
     33      propel.builder.object.class            = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedObjectBuilder 
     34       
     35      propel.builder.objectstub.class        = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedExtensionObjectBuilder 
     36      propel.builder.peerstub.class          = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedExtensionPeerBuilder 
     37      propel.builder.objectmultiextend.class = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedMultiExtendObjectBuilder 
     38      propel.builder.mapbuilder.class        = plugins.nahoPropelOptimizerPlugin.lib.SfOptimizedMapBuilderBuilder 
     39    }}} 
     40   
     41  * Rebuild your model : 
     42     
     43    {{{ 
     44      $ symfony propel-build-model 
     45      $ symfony cc 
     46    }}} 
     47 
     48Your model classes have been regenerated with all the supported optimizations. You're ready to go ! 
     49 
     50Default behavior is enabling all supported optimizations, see the "Configuration" section to disable some of them if you want. 
     51 
     52== Optimizations == 
     53 
     54This section lists all the changes made by this custom builder. 
     55 
     56=== Includes overhead (defect) === 
     57 
     58Default behavior does not remove all the useless calls to "include_once" made by Propel (it removes some of them, but not all). They are useless as the autoloader already takes care of this, and they add a little useless overhead. 
     59 
     60`nahoPropelOptimizer` removes all these calls in Peer and MapBuilder classes. 
     61 
     62=== Explicit joins (bug) === 
     63 
     64With the default builder, Peer classes join with an implicit JOIN. PostPeer::doSelectJoinAuthor will execute this query : 
     65 
     66{{{ 
     67  SELECT * FROM post, author WHERE post.author_id = author.id AND ... 
     68}}} 
     69 
     70Which is equivalent to : 
     71 
     72{{{ 
     73  SELECT * FROM post INNER JOIN author ON (post.author_id = author.id) WHERE ... 
     74}}} 
     75 
     76This behavior makes the `doSelectJoinXXX` methods unreliable if you expected to be sure to have the same results with or without the join (if you had a not required foreign key, this implicit inner join will just drop the lines where this key was `NULL`). 
     77 
     78`nahoPropelOptimizer` forces Propel to use real explicit joins, and checks if the foreign key is `required` or not to make a LEFT JOIN or an INNER JOIN. 
     79 
     80Due to a still partial support of those joins in Propel, the related object is always hydrated, but when the foreign key was `NULL` it's hydrated with `NULL` values... But the next optimization is here to fix that ;) 
     81 
     82=== LEFT JOINS and related objects hydrated with NULL values (defect) === 
     83 
     84When you make a LEFT JOIN, Propel always hydrates the related object, even if there is *no* related object ! 
     85 
     86If there is no related object, you will get corrupted results. 
     87 
     88Let's see this schema, where a user can have a group, but it's not required : 
     89 
     90{{{ 
     91  t_group: 
     92    id: ~ 
     93    name: { type: varchar(128), index: unique } 
     94  t_user: 
     95    id: ~ 
     96    login: { type: varchar(128), index: unique } 
     97    group_id: { type: integer, foreignTable: t_group, foreignReference: id, required: false } 
     98}}} 
     99 
     100{{{ 
     101  <?php 
     102     
     103    // Retrieve the user, joined with group, with explicit joins optimization activated 
     104    // We will retrieve the user with ID = $id, and we know this user has no related group 
     105     
     106    $criteria = new Criteria; 
     107    $criteria->add(TUser::ID, $id); 
     108     
     109    $users = TUserPeer::doSelectJoinTGroup($criteria); 
     110    // Executed query : SELECT * FROM t_user JOIN t_group ON (t_user.group_id = t_group.id) WHERE t_user.id = $id 
     111     
     112    $user = $users[0]; 
     113     
     114    $group = $user->getGroup(); 
     115    // Default behavior : $group is an instance of TGroup, and all its fields are NULL 
     116    // Fixed behavior   : $group is NULL 
     117}}}  
     118 
     119With this optimization, you will not retreive corrupted objects, when the object does not exist, you get NULL as you would expect it to be. 
     120 
     121=== Calls to Propel::import (bug) === 
     122 
     123This bug makes the overriding of plugins' model totally impossible, and adds a little overhead just like includes. 
     124 
     125If a plugin has a bundled schema with a package attribute different than "lib.model", because of this bug you will not be able to customize the model without touching the files directly located in the plugin's directory. 
     126 
     127This is caused by useless calls to Propel::import(). This optimization just removes all of them : they are fully useless as the autoloader handles the loading of model classes very better. 
     128 
     129== Configuration == 
     130 
     131All optimizations are activated when you don't specify anything. 
     132 
     133To disable an optimization, just add the corresponding option to your `config/propel.ini` : 
     134 
     135{{{ 
     136  ; Disable optimization "Includes overhead" 
     137  propel.builder.addIncludes = true 
     138   
     139  ; Disable optimization "Explicit joins" 
     140  propel.builder.implicitJoins = true 
     141   
     142  ; Disable optimization "LEFT JOINS and related objects hydrated with NULL values" 
     143  propel.builder.hydrateNULLs = true 
     144   
     145  ; Disable optimization "Calls to Propel::import" 
     146  propel.builder.addPropelImports = true 
     147}}} 
  • plugins/nahoPropelOptimizerPlugin/lib/SfOptimizedObjectBuilder.php

    r8156 r8458  
    2121  } 
    2222   
     23  protected function addFKAccessor(&$script, ForeignKey $fk) 
     24  { 
     25    // Make original modifications 
     26    parent::addFKAccessor($script, $fk); 
     27     
     28    // With the explicit joins support, the related object returned can be hydrated with all NULL values, in this case we could simply return NULL 
     29    if (!DataModelBuilder::getBuildProperty('builderHydrateNULLs')) { 
     30      $varName = $this->getFKVarName($fk); 
     31      $return = 'return $this->' . $varName . ';'; 
     32      $check_null_hydrated_script = ' 
     33      if (!$this->' . $varName . '->isNew() && is_null($this->' . $varName . '->getPrimaryKey())) { 
     34        return NULL; 
     35      } 
     36      ' . $return; 
     37      $script = str_replace($return, $check_null_hydrated_script, $script); 
     38    } 
     39  } 
     40   
    2341} 
  • plugins/nahoPropelOptimizerPlugin/lib/SfOptimizedPeerBuilder.php

    r8156 r8458  
    1919     
    2020    // Change implicit joins (all inner) to explicit INNER or LEFT, depending on the fact the key can be null or not 
    21     if (!DataModelBuilder::getBuildProperty('builderSimpleJoins')) { 
     21    if (!DataModelBuilder::getBuildProperty('builderImplicitJoins')) { 
    2222      foreach ($this->getTable()->getColumns() as $column) { 
    2323        if ($column->isForeignKey()) {