2121 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2222 */
2323
24+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \Query \ManyToOnePartialQuery ;
25+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \Query \PartialQuery ;
26+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \StorageNode ;
2427use TheCodingMachine \TDBM \Schema \ForeignKeys ;
28+ use function array_pop ;
29+ use function count ;
2530
2631/**
2732 * Instances of this class represent a row in a database.
@@ -77,7 +82,7 @@ class DbRow
7782
7883 /**
7984 * The values of the primary key.
80- * This is set when the object is in "loaded" state.
85+ * This is set when the object is in "loaded" or "not loaded" state.
8186 *
8287 * @var array An array of column => value
8388 */
@@ -100,6 +105,10 @@ class DbRow
100105 * @var ForeignKeys
101106 */
102107 private $ foreignKeys ;
108+ /**
109+ * @var PartialQuery|null
110+ */
111+ private $ partialQuery ;
103112
104113 /**
105114 * You should never call the constructor directly. Instead, you should use the
@@ -115,11 +124,12 @@ class DbRow
115124 * @param mixed[] $dbRow
116125 * @throws TDBMException
117126 */
118- public function __construct (AbstractTDBMObject $ object , string $ tableName , ForeignKeys $ foreignKeys , array $ primaryKeys = array (), TDBMService $ tdbmService = null , array $ dbRow = [])
127+ public function __construct (AbstractTDBMObject $ object , string $ tableName , ForeignKeys $ foreignKeys , array $ primaryKeys = array (), TDBMService $ tdbmService = null , array $ dbRow = [], ? PartialQuery $ partialQuery = null )
119128 {
120129 $ this ->object = $ object ;
121130 $ this ->dbTableName = $ tableName ;
122131 $ this ->foreignKeys = $ foreignKeys ;
132+ $ this ->partialQuery = $ partialQuery ;
123133
124134 $ this ->status = TDBMObjectStateEnum::STATE_DETACHED ;
125135
@@ -175,6 +185,15 @@ public function _setStatus(string $state) : void
175185 }
176186 }
177187
188+ /**
189+ * When discarding a bean, we expect to reload data from the DB, not the cache.
190+ * Hence, we must disable smart eager load.
191+ */
192+ public function disableSmartEagerLoad (): void
193+ {
194+ $ this ->partialQuery = null ;
195+ }
196+
178197 /**
179198 * This is an internal method. You should not call this method yourself. The TDBM library will do it for you.
180199 * If the object is in state 'not loaded', this method performs a query in database to load the object.
@@ -190,12 +209,30 @@ public function _dbLoadIfNotLoaded(): void
190209 }
191210 $ connection = $ this ->tdbmService ->getConnection ();
192211
193- list ($ sql_where , $ parameters ) = $ this ->tdbmService ->buildFilterFromFilterBag ($ this ->primaryKeys , $ connection ->getDatabasePlatform ());
212+ if ($ this ->partialQuery !== null ) {
213+ $ this ->partialQuery ->registerDataLoader ($ connection );
194214
195- $ sql = 'SELECT * FROM ' .$ connection ->quoteIdentifier ($ this ->dbTableName ).' WHERE ' .$ sql_where ;
196- $ result = $ connection ->executeQuery ($ sql , $ parameters );
215+ // Let's get the data loader.
216+ $ dataLoader = $ this ->partialQuery ->getStorageNode ()->getManyToOneDataLoader ($ this ->partialQuery ->getKey ());
217+
218+ if (count ($ this ->primaryKeys ) !== 1 ) {
219+ throw new \RuntimeException ('Dataloader patterns only supports primary keys on one columns. Table " ' .$ this ->dbTableName .'" has a PK on ' .count ($ this ->primaryKeys ). ' columns ' );
220+ }
221+ $ pks = $ this ->primaryKeys ;
222+ $ pkId = array_pop ($ pks );
223+
224+ $ row = $ dataLoader ->get ((string ) $ pkId );
225+ } else {
226+ list ($ sql_where , $ parameters ) = $ this ->tdbmService ->buildFilterFromFilterBag ($ this ->primaryKeys , $ connection ->getDatabasePlatform ());
227+
228+ $ sql = 'SELECT * FROM ' .$ connection ->quoteIdentifier ($ this ->dbTableName ).' WHERE ' .$ sql_where ;
229+ $ result = $ connection ->executeQuery ($ sql , $ parameters );
230+
231+ $ row = $ result ->fetch (\PDO ::FETCH_ASSOC );
232+
233+ $ result ->closeCursor ();
234+ }
197235
198- $ row = $ result ->fetch (\PDO ::FETCH_ASSOC );
199236
200237 if ($ row === false ) {
201238 throw new TDBMException ("Could not retrieve object from table \"$ this ->dbTableName \" using filter \". $ sql_where. \" with data \"" .var_export ($ parameters , true )."\". " );
@@ -208,8 +245,6 @@ public function _dbLoadIfNotLoaded(): void
208245 $ this ->dbRow [$ key ] = $ types [$ key ]->convertToPHPValue ($ value , $ connection ->getDatabasePlatform ());
209246 }
210247
211- $ result ->closeCursor ();
212-
213248 $ this ->status = TDBMObjectStateEnum::STATE_LOADED ;
214249 }
215250 }
@@ -289,7 +324,8 @@ public function getRef(string $foreignKeyName) : ?AbstractTDBMObject
289324 $ fk = $ this ->foreignKeys ->getForeignKey ($ foreignKeyName );
290325
291326 $ values = [];
292- foreach ($ fk ->getUnquotedLocalColumns () as $ column ) {
327+ $ localColumns = $ fk ->getUnquotedLocalColumns ();
328+ foreach ($ localColumns as $ column ) {
293329 if (!isset ($ this ->dbRow [$ column ])) {
294330 return null ;
295331 }
@@ -303,10 +339,18 @@ public function getRef(string $foreignKeyName) : ?AbstractTDBMObject
303339
304340 // If the foreign key points to the primary key, let's use findObjectByPk
305341 if ($ this ->tdbmService ->getPrimaryKeyColumns ($ foreignTableName ) === $ foreignColumns ) {
306- return $ this ->tdbmService ->findObjectByPk ($ foreignTableName , $ filter , [], true );
342+ if ($ this ->partialQuery !== null && count ($ foreignColumns ) === 1 ) {
343+ // Optimisation: let's build the smart eager load query we need to fetch more than one object at once.
344+ $ newPartialQuery = new ManyToOnePartialQuery ($ this ->partialQuery , $ this ->dbTableName , $ fk ->getForeignTableName (), $ foreignColumns [0 ], $ localColumns [0 ]);
345+ } else {
346+ $ newPartialQuery = null ;
347+ }
348+ $ ref = $ this ->tdbmService ->findObjectByPk ($ foreignTableName , $ filter , [], true , null , $ newPartialQuery );
307349 } else {
308- return $ this ->tdbmService ->findObject ($ foreignTableName , $ filter );
350+ $ ref = $ this ->tdbmService ->findObject ($ foreignTableName , $ filter );
309351 }
352+ $ this ->references [$ foreignKeyName ] = $ ref ;
353+ return $ ref ;
310354 }
311355 }
312356
0 commit comments