21
21
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
22
*/
23
23
24
+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \Query \ManyToOnePartialQuery ;
25
+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \Query \PartialQuery ;
26
+ use TheCodingMachine \TDBM \QueryFactory \SmartEagerLoad \StorageNode ;
24
27
use TheCodingMachine \TDBM \Schema \ForeignKeys ;
28
+ use function array_pop ;
29
+ use function count ;
25
30
26
31
/**
27
32
* Instances of this class represent a row in a database.
@@ -77,7 +82,7 @@ class DbRow
77
82
78
83
/**
79
84
* 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.
81
86
*
82
87
* @var array An array of column => value
83
88
*/
@@ -100,6 +105,10 @@ class DbRow
100
105
* @var ForeignKeys
101
106
*/
102
107
private $ foreignKeys ;
108
+ /**
109
+ * @var PartialQuery|null
110
+ */
111
+ private $ partialQuery ;
103
112
104
113
/**
105
114
* You should never call the constructor directly. Instead, you should use the
@@ -115,11 +124,12 @@ class DbRow
115
124
* @param mixed[] $dbRow
116
125
* @throws TDBMException
117
126
*/
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 )
119
128
{
120
129
$ this ->object = $ object ;
121
130
$ this ->dbTableName = $ tableName ;
122
131
$ this ->foreignKeys = $ foreignKeys ;
132
+ $ this ->partialQuery = $ partialQuery ;
123
133
124
134
$ this ->status = TDBMObjectStateEnum::STATE_DETACHED ;
125
135
@@ -175,6 +185,15 @@ public function _setStatus(string $state) : void
175
185
}
176
186
}
177
187
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
+
178
197
/**
179
198
* This is an internal method. You should not call this method yourself. The TDBM library will do it for you.
180
199
* 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
190
209
}
191
210
$ connection = $ this ->tdbmService ->getConnection ();
192
211
193
- list ($ sql_where , $ parameters ) = $ this ->tdbmService ->buildFilterFromFilterBag ($ this ->primaryKeys , $ connection ->getDatabasePlatform ());
212
+ if ($ this ->partialQuery !== null ) {
213
+ $ this ->partialQuery ->registerDataLoader ($ connection );
194
214
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
+ }
197
235
198
- $ row = $ result ->fetch (\PDO ::FETCH_ASSOC );
199
236
200
237
if ($ row === false ) {
201
238
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
208
245
$ this ->dbRow [$ key ] = $ types [$ key ]->convertToPHPValue ($ value , $ connection ->getDatabasePlatform ());
209
246
}
210
247
211
- $ result ->closeCursor ();
212
-
213
248
$ this ->status = TDBMObjectStateEnum::STATE_LOADED ;
214
249
}
215
250
}
@@ -289,7 +324,8 @@ public function getRef(string $foreignKeyName) : ?AbstractTDBMObject
289
324
$ fk = $ this ->foreignKeys ->getForeignKey ($ foreignKeyName );
290
325
291
326
$ values = [];
292
- foreach ($ fk ->getUnquotedLocalColumns () as $ column ) {
327
+ $ localColumns = $ fk ->getUnquotedLocalColumns ();
328
+ foreach ($ localColumns as $ column ) {
293
329
if (!isset ($ this ->dbRow [$ column ])) {
294
330
return null ;
295
331
}
@@ -303,10 +339,18 @@ public function getRef(string $foreignKeyName) : ?AbstractTDBMObject
303
339
304
340
// If the foreign key points to the primary key, let's use findObjectByPk
305
341
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 );
307
349
} else {
308
- return $ this ->tdbmService ->findObject ($ foreignTableName , $ filter );
350
+ $ ref = $ this ->tdbmService ->findObject ($ foreignTableName , $ filter );
309
351
}
352
+ $ this ->references [$ foreignKeyName ] = $ ref ;
353
+ return $ ref ;
310
354
}
311
355
}
312
356
0 commit comments