| 9 | | In : config/propel.ini |
|---|
| 10 | | Replace : addon.propel.builder.Sf* |
|---|
| 11 | | With : plugins.nahoPropelOptimizerPlugin.lib.SfOptimized* |
|---|
| | 3 | This `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 | |
|---|
| | 48 | Your model classes have been regenerated with all the supported optimizations. You're ready to go ! |
|---|
| | 49 | |
|---|
| | 50 | Default behavior is enabling all supported optimizations, see the "Configuration" section to disable some of them if you want. |
|---|
| | 51 | |
|---|
| | 52 | == Optimizations == |
|---|
| | 53 | |
|---|
| | 54 | This section lists all the changes made by this custom builder. |
|---|
| | 55 | |
|---|
| | 56 | === Includes overhead (defect) === |
|---|
| | 57 | |
|---|
| | 58 | Default 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 | |
|---|
| | 64 | With 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 | |
|---|
| | 70 | Which is equivalent to : |
|---|
| | 71 | |
|---|
| | 72 | {{{ |
|---|
| | 73 | SELECT * FROM post INNER JOIN author ON (post.author_id = author.id) WHERE ... |
|---|
| | 74 | }}} |
|---|
| | 75 | |
|---|
| | 76 | This 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 | |
|---|
| | 80 | Due 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 | |
|---|
| | 84 | When you make a LEFT JOIN, Propel always hydrates the related object, even if there is *no* related object ! |
|---|
| | 85 | |
|---|
| | 86 | If there is no related object, you will get corrupted results. |
|---|
| | 87 | |
|---|
| | 88 | Let'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 | |
|---|
| | 119 | With 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 | |
|---|
| | 123 | This bug makes the overriding of plugins' model totally impossible, and adds a little overhead just like includes. |
|---|
| | 124 | |
|---|
| | 125 | If 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 | |
|---|
| | 127 | This 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 | |
|---|
| | 131 | All optimizations are activated when you don't specify anything. |
|---|
| | 132 | |
|---|
| | 133 | To 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 | }}} |
|---|