Passed
Pull Request — 4.2 (#140)
by David
05:21
created
src/Mouf/Database/TDBM/MoufConfiguration.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -17,21 +17,21 @@
 block discarded – undo
17 17
  */
18 18
 class MoufConfiguration extends Configuration
19 19
 {
20
-    private $daoFactoryInstanceName = 'daoFactory';
20
+	private $daoFactoryInstanceName = 'daoFactory';
21 21
 
22
-    /**
23
-     * @return string
24
-     */
25
-    public function getDaoFactoryInstanceName() : string
26
-    {
27
-        return $this->daoFactoryInstanceName;
28
-    }
22
+	/**
23
+	 * @return string
24
+	 */
25
+	public function getDaoFactoryInstanceName() : string
26
+	{
27
+		return $this->daoFactoryInstanceName;
28
+	}
29 29
 
30
-    /**
31
-     * @param string $daoFactoryInstanceName
32
-     */
33
-    public function setDaoFactoryInstanceName(string $daoFactoryInstanceName)
34
-    {
35
-        $this->daoFactoryInstanceName = $daoFactoryInstanceName;
36
-    }
30
+	/**
31
+	 * @param string $daoFactoryInstanceName
32
+	 */
33
+	public function setDaoFactoryInstanceName(string $daoFactoryInstanceName)
34
+	{
35
+		$this->daoFactoryInstanceName = $daoFactoryInstanceName;
36
+	}
37 37
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/TDBMObject.php 1 patch
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -33,42 +33,42 @@
 block discarded – undo
33 33
  */
34 34
 class TDBMObject extends AbstractTDBMObject
35 35
 {
36
-    public function getProperty($var, $tableName = null)
37
-    {
38
-        return $this->get($var, $tableName);
39
-    }
36
+	public function getProperty($var, $tableName = null)
37
+	{
38
+		return $this->get($var, $tableName);
39
+	}
40 40
 
41
-    public function setProperty($var, $value, $tableName = null)
42
-    {
43
-        $this->set($var, $value, $tableName);
44
-    }
41
+	public function setProperty($var, $value, $tableName = null)
42
+	{
43
+		$this->set($var, $value, $tableName);
44
+	}
45 45
 
46
-    /**
47
-     * Specify data which should be serialized to JSON.
48
-     *
49
-     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
50
-     *
51
-     * @return mixed data which can be serialized by <b>json_encode</b>,
52
-     *               which is a value of any type other than a resource
53
-     *
54
-     * @since 5.4.0
55
-     */
56
-    public function jsonSerialize()
57
-    {
58
-        throw new TDBMException('Json serialization is only implemented for generated beans.');
59
-    }
46
+	/**
47
+	 * Specify data which should be serialized to JSON.
48
+	 *
49
+	 * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
50
+	 *
51
+	 * @return mixed data which can be serialized by <b>json_encode</b>,
52
+	 *               which is a value of any type other than a resource
53
+	 *
54
+	 * @since 5.4.0
55
+	 */
56
+	public function jsonSerialize()
57
+	{
58
+		throw new TDBMException('Json serialization is only implemented for generated beans.');
59
+	}
60 60
 
61
-    /**
62
-     * Returns an array of used tables by this bean (from parent to child relationship).
63
-     *
64
-     * @return string[]
65
-     */
66
-    protected function getUsedTables() : array
67
-    {
68
-        $tableNames = array_keys($this->dbRows);
69
-        $tableNames = $this->tdbmService->_getLinkBetweenInheritedTables($tableNames);
70
-        $tableNames = array_reverse($tableNames);
61
+	/**
62
+	 * Returns an array of used tables by this bean (from parent to child relationship).
63
+	 *
64
+	 * @return string[]
65
+	 */
66
+	protected function getUsedTables() : array
67
+	{
68
+		$tableNames = array_keys($this->dbRows);
69
+		$tableNames = $this->tdbmService->_getLinkBetweenInheritedTables($tableNames);
70
+		$tableNames = array_reverse($tableNames);
71 71
 
72
-        return $tableNames;
73
-    }
72
+		return $tableNames;
73
+	}
74 74
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/AbstractTDBMObject.php 1 patch
Indentation   +606 added lines, -606 removed lines patch added patch discarded remove patch
@@ -31,615 +31,615 @@
 block discarded – undo
31 31
  */
32 32
 abstract class AbstractTDBMObject implements JsonSerializable
33 33
 {
34
-    /**
35
-     * The service this object is bound to.
36
-     *
37
-     * @var TDBMService
38
-     */
39
-    protected $tdbmService;
40
-
41
-    /**
42
-     * An array of DbRow, indexed by table name.
43
-     *
44
-     * @var DbRow[]
45
-     */
46
-    protected $dbRows = [];
47
-
48
-    /**
49
-     * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
50
-     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
51
-     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
52
-     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
53
-     *
54
-     * @var string
55
-     */
56
-    private $status;
57
-
58
-    /**
59
-     * Array storing beans related via many to many relationships (pivot tables).
60
-     *
61
-     * @var \SplObjectStorage[] Key: pivot table name, value: SplObjectStorage
62
-     */
63
-    private $relationships = [];
64
-
65
-    /**
66
-     * @var bool[] Key: pivot table name, value: whether a query was performed to load the data
67
-     */
68
-    private $loadedRelationships = [];
69
-
70
-    /**
71
-     * Array storing beans related via many to one relationships (this bean is pointed by external beans).
72
-     *
73
-     * @var AlterableResultIterator[] Key: [external_table]___[external_column], value: SplObjectStorage
74
-     */
75
-    private $manyToOneRelationships = [];
76
-
77
-    /**
78
-     * Used with $primaryKeys when we want to retrieve an existing object
79
-     * and $primaryKeys=[] if we want a new object.
80
-     *
81
-     * @param string      $tableName
82
-     * @param array       $primaryKeys
83
-     * @param TDBMService $tdbmService
84
-     *
85
-     * @throws TDBMException
86
-     * @throws TDBMInvalidOperationException
87
-     */
88
-    public function __construct($tableName = null, array $primaryKeys = [], TDBMService $tdbmService = null)
89
-    {
90
-        // FIXME: lazy loading should be forbidden on tables with inheritance and dynamic type assignation...
91
-        if (!empty($tableName)) {
92
-            $this->dbRows[$tableName] = new DbRow($this, $tableName, $primaryKeys, $tdbmService);
93
-        }
94
-
95
-        if ($tdbmService === null) {
96
-            $this->_setStatus(TDBMObjectStateEnum::STATE_DETACHED);
97
-        } else {
98
-            $this->_attach($tdbmService);
99
-            if (!empty($primaryKeys)) {
100
-                $this->_setStatus(TDBMObjectStateEnum::STATE_NOT_LOADED);
101
-            } else {
102
-                $this->_setStatus(TDBMObjectStateEnum::STATE_NEW);
103
-            }
104
-        }
105
-    }
106
-
107
-    /**
108
-     * Alternative constructor called when data is fetched from database via a SELECT.
109
-     *
110
-     * @param array       $beanData    array<table, array<column, value>>
111
-     * @param TDBMService $tdbmService
112
-     */
113
-    public function _constructFromData(array $beanData, TDBMService $tdbmService)
114
-    {
115
-        $this->tdbmService = $tdbmService;
116
-
117
-        foreach ($beanData as $table => $columns) {
118
-            $this->dbRows[$table] = new DbRow($this, $table, $tdbmService->_getPrimaryKeysFromObjectData($table, $columns), $tdbmService, $columns);
119
-        }
120
-
121
-        $this->status = TDBMObjectStateEnum::STATE_LOADED;
122
-    }
123
-
124
-    /**
125
-     * Alternative constructor called when bean is lazily loaded.
126
-     *
127
-     * @param string      $tableName
128
-     * @param array       $primaryKeys
129
-     * @param TDBMService $tdbmService
130
-     */
131
-    public function _constructLazy($tableName, array $primaryKeys, TDBMService $tdbmService)
132
-    {
133
-        $this->tdbmService = $tdbmService;
134
-
135
-        $this->dbRows[$tableName] = new DbRow($this, $tableName, $primaryKeys, $tdbmService);
136
-
137
-        $this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
138
-    }
139
-
140
-    public function _attach(TDBMService $tdbmService)
141
-    {
142
-        if ($this->status !== TDBMObjectStateEnum::STATE_DETACHED) {
143
-            throw new TDBMInvalidOperationException('Cannot attach an object that is already attached to TDBM.');
144
-        }
145
-        $this->tdbmService = $tdbmService;
146
-
147
-        // If we attach this object, we must work to make sure the tables are in ascending order (from low level to top level)
148
-        $tableNames = $this->getUsedTables();
149
-
150
-        $newDbRows = [];
151
-
152
-        foreach ($tableNames as $table) {
153
-            if (!isset($this->dbRows[$table])) {
154
-                $this->registerTable($table);
155
-            }
156
-            $newDbRows[$table] = $this->dbRows[$table];
157
-        }
158
-        $this->dbRows = $newDbRows;
159
-
160
-        $this->status = TDBMObjectStateEnum::STATE_NEW;
161
-        foreach ($this->dbRows as $dbRow) {
162
-            $dbRow->_attach($tdbmService);
163
-        }
164
-    }
165
-
166
-    /**
167
-     * Sets the state of the TDBM Object
168
-     * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
169
-     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
170
-     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
171
-     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
172
-     *
173
-     * @param string $state
174
-     */
175
-    public function _setStatus($state)
176
-    {
177
-        $this->status = $state;
178
-
179
-        // TODO: we might ignore the loaded => dirty state here! dirty status comes from the db_row itself.
180
-        foreach ($this->dbRows as $dbRow) {
181
-            $dbRow->_setStatus($state);
182
-        }
183
-
184
-        if ($state === TDBMObjectStateEnum::STATE_DELETED) {
185
-            $this->onDelete();
186
-        }
187
-    }
188
-
189
-    /**
190
-     * Checks that $tableName is ok, or returns the only possible table name if "$tableName = null"
191
-     * or throws an error.
192
-     *
193
-     * @param string $tableName
194
-     *
195
-     * @return string
196
-     */
197
-    private function checkTableName($tableName = null)
198
-    {
199
-        if ($tableName === null) {
200
-            if (count($this->dbRows) > 1) {
201
-                throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
202
-            } elseif (count($this->dbRows) === 1) {
203
-                $tableName = array_keys($this->dbRows)[0];
204
-            }
205
-        }
206
-
207
-        return $tableName;
208
-    }
209
-
210
-    protected function get($var, $tableName = null)
211
-    {
212
-        $tableName = $this->checkTableName($tableName);
213
-
214
-        if (!isset($this->dbRows[$tableName])) {
215
-            return;
216
-        }
217
-
218
-        return $this->dbRows[$tableName]->get($var);
219
-    }
220
-
221
-    protected function set($var, $value, $tableName = null)
222
-    {
223
-        if ($tableName === null) {
224
-            if (count($this->dbRows) > 1) {
225
-                throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
226
-            } elseif (count($this->dbRows) === 1) {
227
-                $tableName = array_keys($this->dbRows)[0];
228
-            } else {
229
-                throw new TDBMException('Please specify a table for this object.');
230
-            }
231
-        }
232
-
233
-        if (!isset($this->dbRows[$tableName])) {
234
-            $this->registerTable($tableName);
235
-        }
236
-
237
-        $this->dbRows[$tableName]->set($var, $value);
238
-        if ($this->dbRows[$tableName]->_getStatus() === TDBMObjectStateEnum::STATE_DIRTY) {
239
-            $this->status = TDBMObjectStateEnum::STATE_DIRTY;
240
-        }
241
-    }
242
-
243
-    /**
244
-     * @param string             $foreignKeyName
245
-     * @param AbstractTDBMObject $bean
246
-     */
247
-    protected function setRef($foreignKeyName, AbstractTDBMObject $bean = null, $tableName = null)
248
-    {
249
-        if ($tableName === null) {
250
-            if (count($this->dbRows) > 1) {
251
-                throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
252
-            } elseif (count($this->dbRows) === 1) {
253
-                $tableName = array_keys($this->dbRows)[0];
254
-            } else {
255
-                throw new TDBMException('Please specify a table for this object.');
256
-            }
257
-        }
258
-
259
-        if (!isset($this->dbRows[$tableName])) {
260
-            $this->registerTable($tableName);
261
-        }
262
-
263
-        $oldLinkedBean = $this->dbRows[$tableName]->getRef($foreignKeyName);
264
-        if ($oldLinkedBean !== null) {
265
-            $oldLinkedBean->removeManyToOneRelationship($tableName, $foreignKeyName, $this);
266
-        }
267
-
268
-        $this->dbRows[$tableName]->setRef($foreignKeyName, $bean);
269
-        if ($this->dbRows[$tableName]->_getStatus() === TDBMObjectStateEnum::STATE_DIRTY) {
270
-            $this->status = TDBMObjectStateEnum::STATE_DIRTY;
271
-        }
272
-
273
-        if ($bean !== null) {
274
-            $bean->setManyToOneRelationship($tableName, $foreignKeyName, $this);
275
-        }
276
-    }
277
-
278
-    /**
279
-     * @param string $foreignKeyName A unique name for this reference
280
-     *
281
-     * @return AbstractTDBMObject|null
282
-     */
283
-    protected function getRef($foreignKeyName, $tableName = null)
284
-    {
285
-        $tableName = $this->checkTableName($tableName);
286
-
287
-        if (!isset($this->dbRows[$tableName])) {
288
-            return;
289
-        }
290
-
291
-        return $this->dbRows[$tableName]->getRef($foreignKeyName);
292
-    }
293
-
294
-    /**
295
-     * Adds a many to many relationship to this bean.
296
-     *
297
-     * @param string             $pivotTableName
298
-     * @param AbstractTDBMObject $remoteBean
299
-     */
300
-    protected function addRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
301
-    {
302
-        $this->setRelationship($pivotTableName, $remoteBean, 'new');
303
-    }
304
-
305
-    /**
306
-     * Returns true if there is a relationship to this bean.
307
-     *
308
-     * @param string             $pivotTableName
309
-     * @param AbstractTDBMObject $remoteBean
310
-     *
311
-     * @return bool
312
-     */
313
-    protected function hasRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
314
-    {
315
-        $storage = $this->retrieveRelationshipsStorage($pivotTableName);
316
-
317
-        if ($storage->contains($remoteBean)) {
318
-            if ($storage[$remoteBean]['status'] !== 'delete') {
319
-                return true;
320
-            }
321
-        }
322
-
323
-        return false;
324
-    }
325
-
326
-    /**
327
-     * Internal TDBM method. Removes a many to many relationship from this bean.
328
-     *
329
-     * @param string             $pivotTableName
330
-     * @param AbstractTDBMObject $remoteBean
331
-     */
332
-    public function _removeRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
333
-    {
334
-        if (isset($this->relationships[$pivotTableName][$remoteBean]) && $this->relationships[$pivotTableName][$remoteBean]['status'] === 'new') {
335
-            unset($this->relationships[$pivotTableName][$remoteBean]);
336
-            unset($remoteBean->relationships[$pivotTableName][$this]);
337
-        } else {
338
-            $this->setRelationship($pivotTableName, $remoteBean, 'delete');
339
-        }
340
-    }
341
-
342
-    /**
343
-     * Sets many to many relationships for this bean.
344
-     * Adds new relationships and removes unused ones.
345
-     *
346
-     * @param $pivotTableName
347
-     * @param array $remoteBeans
348
-     */
349
-    protected function setRelationships($pivotTableName, array $remoteBeans)
350
-    {
351
-        $storage = $this->retrieveRelationshipsStorage($pivotTableName);
352
-
353
-        foreach ($storage as $oldRemoteBean) {
354
-            if (!in_array($oldRemoteBean, $remoteBeans, true)) {
355
-                // $oldRemoteBean must be removed
356
-                $this->_removeRelationship($pivotTableName, $oldRemoteBean);
357
-            }
358
-        }
359
-
360
-        foreach ($remoteBeans as $remoteBean) {
361
-            if (!$storage->contains($remoteBean) || $storage[$remoteBean]['status'] === 'delete') {
362
-                // $remoteBean must be added
363
-                $this->addRelationship($pivotTableName, $remoteBean);
364
-            }
365
-        }
366
-    }
367
-
368
-    /**
369
-     * Returns the list of objects linked to this bean via $pivotTableName.
370
-     *
371
-     * @param $pivotTableName
372
-     *
373
-     * @return \SplObjectStorage
374
-     */
375
-    private function retrieveRelationshipsStorage($pivotTableName)
376
-    {
377
-        $storage = $this->getRelationshipStorage($pivotTableName);
378
-        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->loadedRelationships[$pivotTableName]) && $this->loadedRelationships[$pivotTableName])) {
379
-            return $storage;
380
-        }
381
-
382
-        $beans = $this->tdbmService->_getRelatedBeans($pivotTableName, $this);
383
-        $this->loadedRelationships[$pivotTableName] = true;
384
-
385
-        foreach ($beans as $bean) {
386
-            if (isset($storage[$bean])) {
387
-                $oldStatus = $storage[$bean]['status'];
388
-                if ($oldStatus === 'delete') {
389
-                    // Keep deleted things deleted
390
-                    continue;
391
-                }
392
-            }
393
-            $this->setRelationship($pivotTableName, $bean, 'loaded');
394
-        }
395
-
396
-        return $storage;
397
-    }
398
-
399
-    /**
400
-     * Internal TDBM method. Returns the list of objects linked to this bean via $pivotTableName.
401
-     *
402
-     * @param $pivotTableName
403
-     *
404
-     * @return AbstractTDBMObject[]
405
-     */
406
-    public function _getRelationships($pivotTableName)
407
-    {
408
-        return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pivotTableName));
409
-    }
410
-
411
-    private function relationshipStorageToArray(\SplObjectStorage $storage)
412
-    {
413
-        $beans = [];
414
-        foreach ($storage as $bean) {
415
-            $statusArr = $storage[$bean];
416
-            if ($statusArr['status'] !== 'delete') {
417
-                $beans[] = $bean;
418
-            }
419
-        }
420
-
421
-        return $beans;
422
-    }
423
-
424
-    /**
425
-     * Declares a relationship between.
426
-     *
427
-     * @param string             $pivotTableName
428
-     * @param AbstractTDBMObject $remoteBean
429
-     * @param string             $status
430
-     */
431
-    private function setRelationship($pivotTableName, AbstractTDBMObject $remoteBean, $status)
432
-    {
433
-        $storage = $this->getRelationshipStorage($pivotTableName);
434
-        $storage->attach($remoteBean, ['status' => $status, 'reverse' => false]);
435
-        if ($this->status === TDBMObjectStateEnum::STATE_LOADED) {
436
-            $this->_setStatus(TDBMObjectStateEnum::STATE_DIRTY);
437
-        }
438
-
439
-        $remoteStorage = $remoteBean->getRelationshipStorage($pivotTableName);
440
-        $remoteStorage->attach($this, ['status' => $status, 'reverse' => true]);
441
-    }
442
-
443
-    /**
444
-     * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
445
-     *
446
-     * @param string $pivotTableName
447
-     *
448
-     * @return \SplObjectStorage
449
-     */
450
-    private function getRelationshipStorage(string $pivotTableName) : \SplObjectStorage
451
-    {
452
-        return $this->relationships[$pivotTableName] ?? $this->relationships[$pivotTableName] = new \SplObjectStorage();
453
-    }
454
-
455
-    /**
456
-     * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
457
-     *
458
-     * @param string $tableName
459
-     * @param string $foreignKeyName
460
-     *
461
-     * @return AlterableResultIterator
462
-     */
463
-    private function getManyToOneAlterableResultIterator(string $tableName, string $foreignKeyName) : AlterableResultIterator
464
-    {
465
-        $key = $tableName.'___'.$foreignKeyName;
466
-
467
-        return $this->manyToOneRelationships[$key] ?? $this->manyToOneRelationships[$key] = new AlterableResultIterator();
468
-    }
469
-
470
-    /**
471
-     * Declares a relationship between this bean and the bean pointing to it.
472
-     *
473
-     * @param string             $tableName
474
-     * @param string             $foreignKeyName
475
-     * @param AbstractTDBMObject $remoteBean
476
-     */
477
-    private function setManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
478
-    {
479
-        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
480
-        $alterableResultIterator->add($remoteBean);
481
-    }
482
-
483
-    /**
484
-     * Declares a relationship between this bean and the bean pointing to it.
485
-     *
486
-     * @param string             $tableName
487
-     * @param string             $foreignKeyName
488
-     * @param AbstractTDBMObject $remoteBean
489
-     */
490
-    private function removeManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
491
-    {
492
-        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
493
-        $alterableResultIterator->remove($remoteBean);
494
-    }
495
-
496
-    /**
497
-     * Returns the list of objects linked to this bean via a given foreign key.
498
-     *
499
-     * @param string $tableName
500
-     * @param string $foreignKeyName
501
-     * @param string $searchTableName
502
-     * @param array  $searchFilter
503
-     * @param string $orderString     The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column). WARNING : This parameter is not kept when there is an additionnal or removal object !
504
-     *
505
-     * @return AlterableResultIterator
506
-     */
507
-    protected function retrieveManyToOneRelationshipsStorage(string $tableName, string $foreignKeyName, string $searchTableName, array $searchFilter, $orderString = null) : AlterableResultIterator
508
-    {
509
-        $key = $tableName.'___'.$foreignKeyName;
510
-        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
511
-        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->manyToOneRelationships[$key]) && $this->manyToOneRelationships[$key]->getUnderlyingResultIterator() !== null)) {
512
-            return $alterableResultIterator;
513
-        }
514
-
515
-        $unalteredResultIterator = $this->tdbmService->findObjects($searchTableName, $searchFilter, [], $orderString);
516
-
517
-        $alterableResultIterator->setResultIterator($unalteredResultIterator->getIterator());
518
-
519
-        return $alterableResultIterator;
520
-    }
521
-
522
-    /**
523
-     * Reverts any changes made to the object and resumes it to its DB state.
524
-     * This can only be called on objects that come from database and that have not been deleted.
525
-     * Otherwise, this will throw an exception.
526
-     *
527
-     * @throws TDBMException
528
-     */
529
-    public function discardChanges()
530
-    {
531
-        if ($this->status === TDBMObjectStateEnum::STATE_NEW || $this->status === TDBMObjectStateEnum::STATE_DETACHED) {
532
-            throw new TDBMException("You cannot call discardChanges() on an object that has been created with the 'new' keyword and that has not yet been saved.");
533
-        }
534
-
535
-        if ($this->status === TDBMObjectStateEnum::STATE_DELETED) {
536
-            throw new TDBMException('You cannot call discardChanges() on an object that has been deleted.');
537
-        }
538
-
539
-        $this->_setStatus(TDBMObjectStateEnum::STATE_NOT_LOADED);
540
-    }
541
-
542
-    /**
543
-     * Method used internally by TDBM. You should not use it directly.
544
-     * This method returns the status of the TDBMObject.
545
-     * This is one of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
546
-     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
547
-     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
548
-     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
549
-     *
550
-     * @return string
551
-     */
552
-    public function _getStatus()
553
-    {
554
-        return $this->status;
555
-    }
556
-
557
-    /**
558
-     * Override the native php clone function for TDBMObjects.
559
-     */
560
-    public function __clone()
561
-    {
562
-        // Let's clone the many to many relationships
563
-        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED) {
564
-            $pivotTableList = array_keys($this->relationships);
565
-        } else {
566
-            $pivotTableList = $this->tdbmService->_getPivotTablesLinkedToBean($this);
567
-        }
568
-
569
-        foreach ($pivotTableList as $pivotTable) {
570
-            $storage = $this->retrieveRelationshipsStorage($pivotTable);
571
-
572
-            // Let's duplicate the reverse side of the relationship // This is useless: already done by "retrieveRelationshipsStorage"!!!
573
-            /*foreach ($storage as $remoteBean) {
34
+	/**
35
+	 * The service this object is bound to.
36
+	 *
37
+	 * @var TDBMService
38
+	 */
39
+	protected $tdbmService;
40
+
41
+	/**
42
+	 * An array of DbRow, indexed by table name.
43
+	 *
44
+	 * @var DbRow[]
45
+	 */
46
+	protected $dbRows = [];
47
+
48
+	/**
49
+	 * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
50
+	 * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
51
+	 * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
52
+	 * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
53
+	 *
54
+	 * @var string
55
+	 */
56
+	private $status;
57
+
58
+	/**
59
+	 * Array storing beans related via many to many relationships (pivot tables).
60
+	 *
61
+	 * @var \SplObjectStorage[] Key: pivot table name, value: SplObjectStorage
62
+	 */
63
+	private $relationships = [];
64
+
65
+	/**
66
+	 * @var bool[] Key: pivot table name, value: whether a query was performed to load the data
67
+	 */
68
+	private $loadedRelationships = [];
69
+
70
+	/**
71
+	 * Array storing beans related via many to one relationships (this bean is pointed by external beans).
72
+	 *
73
+	 * @var AlterableResultIterator[] Key: [external_table]___[external_column], value: SplObjectStorage
74
+	 */
75
+	private $manyToOneRelationships = [];
76
+
77
+	/**
78
+	 * Used with $primaryKeys when we want to retrieve an existing object
79
+	 * and $primaryKeys=[] if we want a new object.
80
+	 *
81
+	 * @param string      $tableName
82
+	 * @param array       $primaryKeys
83
+	 * @param TDBMService $tdbmService
84
+	 *
85
+	 * @throws TDBMException
86
+	 * @throws TDBMInvalidOperationException
87
+	 */
88
+	public function __construct($tableName = null, array $primaryKeys = [], TDBMService $tdbmService = null)
89
+	{
90
+		// FIXME: lazy loading should be forbidden on tables with inheritance and dynamic type assignation...
91
+		if (!empty($tableName)) {
92
+			$this->dbRows[$tableName] = new DbRow($this, $tableName, $primaryKeys, $tdbmService);
93
+		}
94
+
95
+		if ($tdbmService === null) {
96
+			$this->_setStatus(TDBMObjectStateEnum::STATE_DETACHED);
97
+		} else {
98
+			$this->_attach($tdbmService);
99
+			if (!empty($primaryKeys)) {
100
+				$this->_setStatus(TDBMObjectStateEnum::STATE_NOT_LOADED);
101
+			} else {
102
+				$this->_setStatus(TDBMObjectStateEnum::STATE_NEW);
103
+			}
104
+		}
105
+	}
106
+
107
+	/**
108
+	 * Alternative constructor called when data is fetched from database via a SELECT.
109
+	 *
110
+	 * @param array       $beanData    array<table, array<column, value>>
111
+	 * @param TDBMService $tdbmService
112
+	 */
113
+	public function _constructFromData(array $beanData, TDBMService $tdbmService)
114
+	{
115
+		$this->tdbmService = $tdbmService;
116
+
117
+		foreach ($beanData as $table => $columns) {
118
+			$this->dbRows[$table] = new DbRow($this, $table, $tdbmService->_getPrimaryKeysFromObjectData($table, $columns), $tdbmService, $columns);
119
+		}
120
+
121
+		$this->status = TDBMObjectStateEnum::STATE_LOADED;
122
+	}
123
+
124
+	/**
125
+	 * Alternative constructor called when bean is lazily loaded.
126
+	 *
127
+	 * @param string      $tableName
128
+	 * @param array       $primaryKeys
129
+	 * @param TDBMService $tdbmService
130
+	 */
131
+	public function _constructLazy($tableName, array $primaryKeys, TDBMService $tdbmService)
132
+	{
133
+		$this->tdbmService = $tdbmService;
134
+
135
+		$this->dbRows[$tableName] = new DbRow($this, $tableName, $primaryKeys, $tdbmService);
136
+
137
+		$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
138
+	}
139
+
140
+	public function _attach(TDBMService $tdbmService)
141
+	{
142
+		if ($this->status !== TDBMObjectStateEnum::STATE_DETACHED) {
143
+			throw new TDBMInvalidOperationException('Cannot attach an object that is already attached to TDBM.');
144
+		}
145
+		$this->tdbmService = $tdbmService;
146
+
147
+		// If we attach this object, we must work to make sure the tables are in ascending order (from low level to top level)
148
+		$tableNames = $this->getUsedTables();
149
+
150
+		$newDbRows = [];
151
+
152
+		foreach ($tableNames as $table) {
153
+			if (!isset($this->dbRows[$table])) {
154
+				$this->registerTable($table);
155
+			}
156
+			$newDbRows[$table] = $this->dbRows[$table];
157
+		}
158
+		$this->dbRows = $newDbRows;
159
+
160
+		$this->status = TDBMObjectStateEnum::STATE_NEW;
161
+		foreach ($this->dbRows as $dbRow) {
162
+			$dbRow->_attach($tdbmService);
163
+		}
164
+	}
165
+
166
+	/**
167
+	 * Sets the state of the TDBM Object
168
+	 * One of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
169
+	 * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
170
+	 * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
171
+	 * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
172
+	 *
173
+	 * @param string $state
174
+	 */
175
+	public function _setStatus($state)
176
+	{
177
+		$this->status = $state;
178
+
179
+		// TODO: we might ignore the loaded => dirty state here! dirty status comes from the db_row itself.
180
+		foreach ($this->dbRows as $dbRow) {
181
+			$dbRow->_setStatus($state);
182
+		}
183
+
184
+		if ($state === TDBMObjectStateEnum::STATE_DELETED) {
185
+			$this->onDelete();
186
+		}
187
+	}
188
+
189
+	/**
190
+	 * Checks that $tableName is ok, or returns the only possible table name if "$tableName = null"
191
+	 * or throws an error.
192
+	 *
193
+	 * @param string $tableName
194
+	 *
195
+	 * @return string
196
+	 */
197
+	private function checkTableName($tableName = null)
198
+	{
199
+		if ($tableName === null) {
200
+			if (count($this->dbRows) > 1) {
201
+				throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
202
+			} elseif (count($this->dbRows) === 1) {
203
+				$tableName = array_keys($this->dbRows)[0];
204
+			}
205
+		}
206
+
207
+		return $tableName;
208
+	}
209
+
210
+	protected function get($var, $tableName = null)
211
+	{
212
+		$tableName = $this->checkTableName($tableName);
213
+
214
+		if (!isset($this->dbRows[$tableName])) {
215
+			return;
216
+		}
217
+
218
+		return $this->dbRows[$tableName]->get($var);
219
+	}
220
+
221
+	protected function set($var, $value, $tableName = null)
222
+	{
223
+		if ($tableName === null) {
224
+			if (count($this->dbRows) > 1) {
225
+				throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
226
+			} elseif (count($this->dbRows) === 1) {
227
+				$tableName = array_keys($this->dbRows)[0];
228
+			} else {
229
+				throw new TDBMException('Please specify a table for this object.');
230
+			}
231
+		}
232
+
233
+		if (!isset($this->dbRows[$tableName])) {
234
+			$this->registerTable($tableName);
235
+		}
236
+
237
+		$this->dbRows[$tableName]->set($var, $value);
238
+		if ($this->dbRows[$tableName]->_getStatus() === TDBMObjectStateEnum::STATE_DIRTY) {
239
+			$this->status = TDBMObjectStateEnum::STATE_DIRTY;
240
+		}
241
+	}
242
+
243
+	/**
244
+	 * @param string             $foreignKeyName
245
+	 * @param AbstractTDBMObject $bean
246
+	 */
247
+	protected function setRef($foreignKeyName, AbstractTDBMObject $bean = null, $tableName = null)
248
+	{
249
+		if ($tableName === null) {
250
+			if (count($this->dbRows) > 1) {
251
+				throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
252
+			} elseif (count($this->dbRows) === 1) {
253
+				$tableName = array_keys($this->dbRows)[0];
254
+			} else {
255
+				throw new TDBMException('Please specify a table for this object.');
256
+			}
257
+		}
258
+
259
+		if (!isset($this->dbRows[$tableName])) {
260
+			$this->registerTable($tableName);
261
+		}
262
+
263
+		$oldLinkedBean = $this->dbRows[$tableName]->getRef($foreignKeyName);
264
+		if ($oldLinkedBean !== null) {
265
+			$oldLinkedBean->removeManyToOneRelationship($tableName, $foreignKeyName, $this);
266
+		}
267
+
268
+		$this->dbRows[$tableName]->setRef($foreignKeyName, $bean);
269
+		if ($this->dbRows[$tableName]->_getStatus() === TDBMObjectStateEnum::STATE_DIRTY) {
270
+			$this->status = TDBMObjectStateEnum::STATE_DIRTY;
271
+		}
272
+
273
+		if ($bean !== null) {
274
+			$bean->setManyToOneRelationship($tableName, $foreignKeyName, $this);
275
+		}
276
+	}
277
+
278
+	/**
279
+	 * @param string $foreignKeyName A unique name for this reference
280
+	 *
281
+	 * @return AbstractTDBMObject|null
282
+	 */
283
+	protected function getRef($foreignKeyName, $tableName = null)
284
+	{
285
+		$tableName = $this->checkTableName($tableName);
286
+
287
+		if (!isset($this->dbRows[$tableName])) {
288
+			return;
289
+		}
290
+
291
+		return $this->dbRows[$tableName]->getRef($foreignKeyName);
292
+	}
293
+
294
+	/**
295
+	 * Adds a many to many relationship to this bean.
296
+	 *
297
+	 * @param string             $pivotTableName
298
+	 * @param AbstractTDBMObject $remoteBean
299
+	 */
300
+	protected function addRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
301
+	{
302
+		$this->setRelationship($pivotTableName, $remoteBean, 'new');
303
+	}
304
+
305
+	/**
306
+	 * Returns true if there is a relationship to this bean.
307
+	 *
308
+	 * @param string             $pivotTableName
309
+	 * @param AbstractTDBMObject $remoteBean
310
+	 *
311
+	 * @return bool
312
+	 */
313
+	protected function hasRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
314
+	{
315
+		$storage = $this->retrieveRelationshipsStorage($pivotTableName);
316
+
317
+		if ($storage->contains($remoteBean)) {
318
+			if ($storage[$remoteBean]['status'] !== 'delete') {
319
+				return true;
320
+			}
321
+		}
322
+
323
+		return false;
324
+	}
325
+
326
+	/**
327
+	 * Internal TDBM method. Removes a many to many relationship from this bean.
328
+	 *
329
+	 * @param string             $pivotTableName
330
+	 * @param AbstractTDBMObject $remoteBean
331
+	 */
332
+	public function _removeRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
333
+	{
334
+		if (isset($this->relationships[$pivotTableName][$remoteBean]) && $this->relationships[$pivotTableName][$remoteBean]['status'] === 'new') {
335
+			unset($this->relationships[$pivotTableName][$remoteBean]);
336
+			unset($remoteBean->relationships[$pivotTableName][$this]);
337
+		} else {
338
+			$this->setRelationship($pivotTableName, $remoteBean, 'delete');
339
+		}
340
+	}
341
+
342
+	/**
343
+	 * Sets many to many relationships for this bean.
344
+	 * Adds new relationships and removes unused ones.
345
+	 *
346
+	 * @param $pivotTableName
347
+	 * @param array $remoteBeans
348
+	 */
349
+	protected function setRelationships($pivotTableName, array $remoteBeans)
350
+	{
351
+		$storage = $this->retrieveRelationshipsStorage($pivotTableName);
352
+
353
+		foreach ($storage as $oldRemoteBean) {
354
+			if (!in_array($oldRemoteBean, $remoteBeans, true)) {
355
+				// $oldRemoteBean must be removed
356
+				$this->_removeRelationship($pivotTableName, $oldRemoteBean);
357
+			}
358
+		}
359
+
360
+		foreach ($remoteBeans as $remoteBean) {
361
+			if (!$storage->contains($remoteBean) || $storage[$remoteBean]['status'] === 'delete') {
362
+				// $remoteBean must be added
363
+				$this->addRelationship($pivotTableName, $remoteBean);
364
+			}
365
+		}
366
+	}
367
+
368
+	/**
369
+	 * Returns the list of objects linked to this bean via $pivotTableName.
370
+	 *
371
+	 * @param $pivotTableName
372
+	 *
373
+	 * @return \SplObjectStorage
374
+	 */
375
+	private function retrieveRelationshipsStorage($pivotTableName)
376
+	{
377
+		$storage = $this->getRelationshipStorage($pivotTableName);
378
+		if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->loadedRelationships[$pivotTableName]) && $this->loadedRelationships[$pivotTableName])) {
379
+			return $storage;
380
+		}
381
+
382
+		$beans = $this->tdbmService->_getRelatedBeans($pivotTableName, $this);
383
+		$this->loadedRelationships[$pivotTableName] = true;
384
+
385
+		foreach ($beans as $bean) {
386
+			if (isset($storage[$bean])) {
387
+				$oldStatus = $storage[$bean]['status'];
388
+				if ($oldStatus === 'delete') {
389
+					// Keep deleted things deleted
390
+					continue;
391
+				}
392
+			}
393
+			$this->setRelationship($pivotTableName, $bean, 'loaded');
394
+		}
395
+
396
+		return $storage;
397
+	}
398
+
399
+	/**
400
+	 * Internal TDBM method. Returns the list of objects linked to this bean via $pivotTableName.
401
+	 *
402
+	 * @param $pivotTableName
403
+	 *
404
+	 * @return AbstractTDBMObject[]
405
+	 */
406
+	public function _getRelationships($pivotTableName)
407
+	{
408
+		return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pivotTableName));
409
+	}
410
+
411
+	private function relationshipStorageToArray(\SplObjectStorage $storage)
412
+	{
413
+		$beans = [];
414
+		foreach ($storage as $bean) {
415
+			$statusArr = $storage[$bean];
416
+			if ($statusArr['status'] !== 'delete') {
417
+				$beans[] = $bean;
418
+			}
419
+		}
420
+
421
+		return $beans;
422
+	}
423
+
424
+	/**
425
+	 * Declares a relationship between.
426
+	 *
427
+	 * @param string             $pivotTableName
428
+	 * @param AbstractTDBMObject $remoteBean
429
+	 * @param string             $status
430
+	 */
431
+	private function setRelationship($pivotTableName, AbstractTDBMObject $remoteBean, $status)
432
+	{
433
+		$storage = $this->getRelationshipStorage($pivotTableName);
434
+		$storage->attach($remoteBean, ['status' => $status, 'reverse' => false]);
435
+		if ($this->status === TDBMObjectStateEnum::STATE_LOADED) {
436
+			$this->_setStatus(TDBMObjectStateEnum::STATE_DIRTY);
437
+		}
438
+
439
+		$remoteStorage = $remoteBean->getRelationshipStorage($pivotTableName);
440
+		$remoteStorage->attach($this, ['status' => $status, 'reverse' => true]);
441
+	}
442
+
443
+	/**
444
+	 * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
445
+	 *
446
+	 * @param string $pivotTableName
447
+	 *
448
+	 * @return \SplObjectStorage
449
+	 */
450
+	private function getRelationshipStorage(string $pivotTableName) : \SplObjectStorage
451
+	{
452
+		return $this->relationships[$pivotTableName] ?? $this->relationships[$pivotTableName] = new \SplObjectStorage();
453
+	}
454
+
455
+	/**
456
+	 * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
457
+	 *
458
+	 * @param string $tableName
459
+	 * @param string $foreignKeyName
460
+	 *
461
+	 * @return AlterableResultIterator
462
+	 */
463
+	private function getManyToOneAlterableResultIterator(string $tableName, string $foreignKeyName) : AlterableResultIterator
464
+	{
465
+		$key = $tableName.'___'.$foreignKeyName;
466
+
467
+		return $this->manyToOneRelationships[$key] ?? $this->manyToOneRelationships[$key] = new AlterableResultIterator();
468
+	}
469
+
470
+	/**
471
+	 * Declares a relationship between this bean and the bean pointing to it.
472
+	 *
473
+	 * @param string             $tableName
474
+	 * @param string             $foreignKeyName
475
+	 * @param AbstractTDBMObject $remoteBean
476
+	 */
477
+	private function setManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
478
+	{
479
+		$alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
480
+		$alterableResultIterator->add($remoteBean);
481
+	}
482
+
483
+	/**
484
+	 * Declares a relationship between this bean and the bean pointing to it.
485
+	 *
486
+	 * @param string             $tableName
487
+	 * @param string             $foreignKeyName
488
+	 * @param AbstractTDBMObject $remoteBean
489
+	 */
490
+	private function removeManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
491
+	{
492
+		$alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
493
+		$alterableResultIterator->remove($remoteBean);
494
+	}
495
+
496
+	/**
497
+	 * Returns the list of objects linked to this bean via a given foreign key.
498
+	 *
499
+	 * @param string $tableName
500
+	 * @param string $foreignKeyName
501
+	 * @param string $searchTableName
502
+	 * @param array  $searchFilter
503
+	 * @param string $orderString     The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column). WARNING : This parameter is not kept when there is an additionnal or removal object !
504
+	 *
505
+	 * @return AlterableResultIterator
506
+	 */
507
+	protected function retrieveManyToOneRelationshipsStorage(string $tableName, string $foreignKeyName, string $searchTableName, array $searchFilter, $orderString = null) : AlterableResultIterator
508
+	{
509
+		$key = $tableName.'___'.$foreignKeyName;
510
+		$alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
511
+		if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->manyToOneRelationships[$key]) && $this->manyToOneRelationships[$key]->getUnderlyingResultIterator() !== null)) {
512
+			return $alterableResultIterator;
513
+		}
514
+
515
+		$unalteredResultIterator = $this->tdbmService->findObjects($searchTableName, $searchFilter, [], $orderString);
516
+
517
+		$alterableResultIterator->setResultIterator($unalteredResultIterator->getIterator());
518
+
519
+		return $alterableResultIterator;
520
+	}
521
+
522
+	/**
523
+	 * Reverts any changes made to the object and resumes it to its DB state.
524
+	 * This can only be called on objects that come from database and that have not been deleted.
525
+	 * Otherwise, this will throw an exception.
526
+	 *
527
+	 * @throws TDBMException
528
+	 */
529
+	public function discardChanges()
530
+	{
531
+		if ($this->status === TDBMObjectStateEnum::STATE_NEW || $this->status === TDBMObjectStateEnum::STATE_DETACHED) {
532
+			throw new TDBMException("You cannot call discardChanges() on an object that has been created with the 'new' keyword and that has not yet been saved.");
533
+		}
534
+
535
+		if ($this->status === TDBMObjectStateEnum::STATE_DELETED) {
536
+			throw new TDBMException('You cannot call discardChanges() on an object that has been deleted.');
537
+		}
538
+
539
+		$this->_setStatus(TDBMObjectStateEnum::STATE_NOT_LOADED);
540
+	}
541
+
542
+	/**
543
+	 * Method used internally by TDBM. You should not use it directly.
544
+	 * This method returns the status of the TDBMObject.
545
+	 * This is one of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
546
+	 * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
547
+	 * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
548
+	 * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
549
+	 *
550
+	 * @return string
551
+	 */
552
+	public function _getStatus()
553
+	{
554
+		return $this->status;
555
+	}
556
+
557
+	/**
558
+	 * Override the native php clone function for TDBMObjects.
559
+	 */
560
+	public function __clone()
561
+	{
562
+		// Let's clone the many to many relationships
563
+		if ($this->status === TDBMObjectStateEnum::STATE_DETACHED) {
564
+			$pivotTableList = array_keys($this->relationships);
565
+		} else {
566
+			$pivotTableList = $this->tdbmService->_getPivotTablesLinkedToBean($this);
567
+		}
568
+
569
+		foreach ($pivotTableList as $pivotTable) {
570
+			$storage = $this->retrieveRelationshipsStorage($pivotTable);
571
+
572
+			// Let's duplicate the reverse side of the relationship // This is useless: already done by "retrieveRelationshipsStorage"!!!
573
+			/*foreach ($storage as $remoteBean) {
574 574
                 $metadata = $storage[$remoteBean];
575 575
 
576 576
                 $remoteStorage = $remoteBean->getRelationshipStorage($pivotTable);
577 577
                 $remoteStorage->attach($this, ['status' => $metadata['status'], 'reverse' => !$metadata['reverse']]);
578 578
             }*/
579
-        }
580
-
581
-        // Let's clone each row
582
-        foreach ($this->dbRows as $key => &$dbRow) {
583
-            $dbRow = clone $dbRow;
584
-            $dbRow->setTDBMObject($this);
585
-        }
586
-
587
-        $this->manyToOneRelationships = [];
588
-
589
-        // Let's set the status to new (to enter the save function)
590
-        $this->status = TDBMObjectStateEnum::STATE_DETACHED;
591
-    }
592
-
593
-    /**
594
-     * Returns raw database rows.
595
-     *
596
-     * @return DbRow[] Key: table name, Value: DbRow object
597
-     */
598
-    public function _getDbRows()
599
-    {
600
-        return $this->dbRows;
601
-    }
602
-
603
-    private function registerTable($tableName)
604
-    {
605
-        $dbRow = new DbRow($this, $tableName);
606
-
607
-        if (in_array($this->status, [TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DIRTY])) {
608
-            // Let's get the primary key for the new table
609
-            $anotherDbRow = array_values($this->dbRows)[0];
610
-            /* @var $anotherDbRow DbRow */
611
-            $indexedPrimaryKeys = array_values($anotherDbRow->_getPrimaryKeys());
612
-            $primaryKeys = $this->tdbmService->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $indexedPrimaryKeys);
613
-            $dbRow->_setPrimaryKeys($primaryKeys);
614
-        }
615
-
616
-        $dbRow->_setStatus($this->status);
617
-
618
-        $this->dbRows[$tableName] = $dbRow;
619
-        // TODO: look at status (if not new)=> get primary key from tdbmservice
620
-    }
621
-
622
-    /**
623
-     * Internal function: return the list of relationships.
624
-     *
625
-     * @return \SplObjectStorage[]
626
-     */
627
-    public function _getCachedRelationships()
628
-    {
629
-        return $this->relationships;
630
-    }
631
-
632
-    /**
633
-     * Returns an array of used tables by this bean (from parent to child relationship).
634
-     *
635
-     * @return string[]
636
-     */
637
-    abstract protected function getUsedTables() : array;
638
-
639
-    /**
640
-     * Method called when the bean is removed from database.
641
-     */
642
-    protected function onDelete() : void
643
-    {
644
-    }
579
+		}
580
+
581
+		// Let's clone each row
582
+		foreach ($this->dbRows as $key => &$dbRow) {
583
+			$dbRow = clone $dbRow;
584
+			$dbRow->setTDBMObject($this);
585
+		}
586
+
587
+		$this->manyToOneRelationships = [];
588
+
589
+		// Let's set the status to new (to enter the save function)
590
+		$this->status = TDBMObjectStateEnum::STATE_DETACHED;
591
+	}
592
+
593
+	/**
594
+	 * Returns raw database rows.
595
+	 *
596
+	 * @return DbRow[] Key: table name, Value: DbRow object
597
+	 */
598
+	public function _getDbRows()
599
+	{
600
+		return $this->dbRows;
601
+	}
602
+
603
+	private function registerTable($tableName)
604
+	{
605
+		$dbRow = new DbRow($this, $tableName);
606
+
607
+		if (in_array($this->status, [TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DIRTY])) {
608
+			// Let's get the primary key for the new table
609
+			$anotherDbRow = array_values($this->dbRows)[0];
610
+			/* @var $anotherDbRow DbRow */
611
+			$indexedPrimaryKeys = array_values($anotherDbRow->_getPrimaryKeys());
612
+			$primaryKeys = $this->tdbmService->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $indexedPrimaryKeys);
613
+			$dbRow->_setPrimaryKeys($primaryKeys);
614
+		}
615
+
616
+		$dbRow->_setStatus($this->status);
617
+
618
+		$this->dbRows[$tableName] = $dbRow;
619
+		// TODO: look at status (if not new)=> get primary key from tdbmservice
620
+	}
621
+
622
+	/**
623
+	 * Internal function: return the list of relationships.
624
+	 *
625
+	 * @return \SplObjectStorage[]
626
+	 */
627
+	public function _getCachedRelationships()
628
+	{
629
+		return $this->relationships;
630
+	}
631
+
632
+	/**
633
+	 * Returns an array of used tables by this bean (from parent to child relationship).
634
+	 *
635
+	 * @return string[]
636
+	 */
637
+	abstract protected function getUsedTables() : array;
638
+
639
+	/**
640
+	 * Method called when the bean is removed from database.
641
+	 */
642
+	protected function onDelete() : void
643
+	{
644
+	}
645 645
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/TDBMService.php 1 patch
Indentation   +1364 added lines, -1364 removed lines patch added patch discarded remove patch
@@ -48,384 +48,384 @@  discard block
 block discarded – undo
48 48
  */
49 49
 class TDBMService
50 50
 {
51
-    const MODE_CURSOR = 1;
52
-    const MODE_ARRAY = 2;
53
-
54
-    /**
55
-     * The database connection.
56
-     *
57
-     * @var Connection
58
-     */
59
-    private $connection;
60
-
61
-    /**
62
-     * @var SchemaAnalyzer
63
-     */
64
-    private $schemaAnalyzer;
65
-
66
-    /**
67
-     * @var MagicQuery
68
-     */
69
-    private $magicQuery;
70
-
71
-    /**
72
-     * @var TDBMSchemaAnalyzer
73
-     */
74
-    private $tdbmSchemaAnalyzer;
75
-
76
-    /**
77
-     * @var string
78
-     */
79
-    private $cachePrefix;
80
-
81
-    /**
82
-     * Cache of table of primary keys.
83
-     * Primary keys are stored by tables, as an array of column.
84
-     * For instance $primary_key['my_table'][0] will return the first column of the primary key of table 'my_table'.
85
-     *
86
-     * @var string[]
87
-     */
88
-    private $primaryKeysColumns;
89
-
90
-    /**
91
-     * Service storing objects in memory.
92
-     * Access is done by table name and then by primary key.
93
-     * If the primary key is split on several columns, access is done by an array of columns, serialized.
94
-     *
95
-     * @var StandardObjectStorage|WeakrefObjectStorage
96
-     */
97
-    private $objectStorage;
98
-
99
-    /**
100
-     * The fetch mode of the result sets returned by `getObjects`.
101
-     * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY or TDBMObjectArray::MODE_COMPATIBLE_ARRAY.
102
-     *
103
-     * In 'MODE_ARRAY' mode (default), the result is an array. Use this mode by default (unless the list returned is very big).
104
-     * In 'MODE_CURSOR' mode, the result is a Generator which is an iterable collection that can be scanned only once (only one "foreach") on it,
105
-     * and it cannot be accessed via key. Use this mode for large datasets processed by batch.
106
-     * In 'MODE_COMPATIBLE_ARRAY' mode, the result is an old TDBMObjectArray (used up to TDBM 3.2).
107
-     * You can access the array by key, or using foreach, several times.
108
-     *
109
-     * @var int
110
-     */
111
-    private $mode = self::MODE_ARRAY;
112
-
113
-    /**
114
-     * Table of new objects not yet inserted in database or objects modified that must be saved.
115
-     *
116
-     * @var \SplObjectStorage of DbRow objects
117
-     */
118
-    private $toSaveObjects;
119
-
120
-    /**
121
-     * A cache service to be used.
122
-     *
123
-     * @var Cache|null
124
-     */
125
-    private $cache;
126
-
127
-    /**
128
-     * Map associating a table name to a fully qualified Bean class name.
129
-     *
130
-     * @var array
131
-     */
132
-    private $tableToBeanMap = [];
133
-
134
-    /**
135
-     * @var \ReflectionClass[]
136
-     */
137
-    private $reflectionClassCache = array();
138
-
139
-    /**
140
-     * @var LoggerInterface
141
-     */
142
-    private $rootLogger;
143
-
144
-    /**
145
-     * @var LevelFilter|NullLogger
146
-     */
147
-    private $logger;
148
-
149
-    /**
150
-     * @var OrderByAnalyzer
151
-     */
152
-    private $orderByAnalyzer;
153
-
154
-    /**
155
-     * @var string
156
-     */
157
-    private $beanNamespace;
158
-
159
-    /**
160
-     * @var NamingStrategyInterface
161
-     */
162
-    private $namingStrategy;
163
-    /**
164
-     * @var ConfigurationInterface
165
-     */
166
-    private $configuration;
167
-
168
-    /**
169
-     * @param ConfigurationInterface $configuration The configuration object
170
-     */
171
-    public function __construct(ConfigurationInterface $configuration /*Connection $connection, Cache $cache = null, SchemaAnalyzer $schemaAnalyzer = null, LoggerInterface $logger = null*/)
172
-    {
173
-        if (extension_loaded('weakref')) {
174
-            $this->objectStorage = new WeakrefObjectStorage();
175
-        } else {
176
-            $this->objectStorage = new StandardObjectStorage();
177
-        }
178
-        $this->connection = $configuration->getConnection();
179
-        $this->cache = $configuration->getCache();
180
-        $this->schemaAnalyzer = $configuration->getSchemaAnalyzer();
181
-
182
-        $this->magicQuery = new MagicQuery($this->connection, $this->cache, $this->schemaAnalyzer);
183
-
184
-        $this->tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->connection, $this->cache, $this->schemaAnalyzer);
185
-        $this->cachePrefix = $this->tdbmSchemaAnalyzer->getCachePrefix();
186
-
187
-        $this->toSaveObjects = new \SplObjectStorage();
188
-        $logger = $configuration->getLogger();
189
-        if ($logger === null) {
190
-            $this->logger = new NullLogger();
191
-            $this->rootLogger = new NullLogger();
192
-        } else {
193
-            $this->rootLogger = $logger;
194
-            $this->setLogLevel(LogLevel::WARNING);
195
-        }
196
-        $this->orderByAnalyzer = new OrderByAnalyzer($this->cache, $this->cachePrefix);
197
-        $this->beanNamespace = $configuration->getBeanNamespace();
198
-        $this->namingStrategy = $configuration->getNamingStrategy();
199
-        $this->configuration = $configuration;
200
-    }
201
-
202
-    /**
203
-     * Returns the object used to connect to the database.
204
-     *
205
-     * @return Connection
206
-     */
207
-    public function getConnection(): Connection
208
-    {
209
-        return $this->connection;
210
-    }
211
-
212
-    /**
213
-     * Sets the default fetch mode of the result sets returned by `findObjects`.
214
-     * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY.
215
-     *
216
-     * In 'MODE_ARRAY' mode (default), the result is a ResultIterator object that behaves like an array. Use this mode by default (unless the list returned is very big).
217
-     * In 'MODE_CURSOR' mode, the result is a ResultIterator object. If you scan it many times (by calling several time a foreach loop), the query will be run
218
-     * several times. In cursor mode, you cannot access the result set by key. Use this mode for large datasets processed by batch.
219
-     *
220
-     * @param int $mode
221
-     *
222
-     * @return $this
223
-     *
224
-     * @throws TDBMException
225
-     */
226
-    public function setFetchMode($mode)
227
-    {
228
-        if ($mode !== self::MODE_CURSOR && $mode !== self::MODE_ARRAY) {
229
-            throw new TDBMException("Unknown fetch mode: '".$this->mode."'");
230
-        }
231
-        $this->mode = $mode;
232
-
233
-        return $this;
234
-    }
235
-
236
-    /**
237
-     * Removes the given object from database.
238
-     * This cannot be called on an object that is not attached to this TDBMService
239
-     * (will throw a TDBMInvalidOperationException).
240
-     *
241
-     * @param AbstractTDBMObject $object the object to delete
242
-     *
243
-     * @throws TDBMException
244
-     * @throws TDBMInvalidOperationException
245
-     */
246
-    public function delete(AbstractTDBMObject $object)
247
-    {
248
-        switch ($object->_getStatus()) {
249
-            case TDBMObjectStateEnum::STATE_DELETED:
250
-                // Nothing to do, object already deleted.
251
-                return;
252
-            case TDBMObjectStateEnum::STATE_DETACHED:
253
-                throw new TDBMInvalidOperationException('Cannot delete a detached object');
254
-            case TDBMObjectStateEnum::STATE_NEW:
255
-                $this->deleteManyToManyRelationships($object);
256
-                foreach ($object->_getDbRows() as $dbRow) {
257
-                    $this->removeFromToSaveObjectList($dbRow);
258
-                }
259
-                break;
260
-            case TDBMObjectStateEnum::STATE_DIRTY:
261
-                foreach ($object->_getDbRows() as $dbRow) {
262
-                    $this->removeFromToSaveObjectList($dbRow);
263
-                }
264
-                // And continue deleting...
265
-            case TDBMObjectStateEnum::STATE_NOT_LOADED:
266
-            case TDBMObjectStateEnum::STATE_LOADED:
267
-                $this->deleteManyToManyRelationships($object);
268
-                // Let's delete db rows, in reverse order.
269
-                foreach (array_reverse($object->_getDbRows()) as $dbRow) {
270
-                    $tableName = $dbRow->_getDbTableName();
271
-                    $primaryKeys = $dbRow->_getPrimaryKeys();
272
-                    $this->connection->delete($tableName, $primaryKeys);
273
-                    $this->objectStorage->remove($dbRow->_getDbTableName(), $this->getObjectHash($primaryKeys));
274
-                }
275
-                break;
276
-            // @codeCoverageIgnoreStart
277
-            default:
278
-                throw new TDBMInvalidOperationException('Unexpected status for bean');
279
-            // @codeCoverageIgnoreEnd
280
-        }
281
-
282
-        $object->_setStatus(TDBMObjectStateEnum::STATE_DELETED);
283
-    }
284
-
285
-    /**
286
-     * Removes all many to many relationships for this object.
287
-     *
288
-     * @param AbstractTDBMObject $object
289
-     */
290
-    private function deleteManyToManyRelationships(AbstractTDBMObject $object)
291
-    {
292
-        foreach ($object->_getDbRows() as $tableName => $dbRow) {
293
-            $pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
294
-            foreach ($pivotTables as $pivotTable) {
295
-                $remoteBeans = $object->_getRelationships($pivotTable);
296
-                foreach ($remoteBeans as $remoteBean) {
297
-                    $object->_removeRelationship($pivotTable, $remoteBean);
298
-                }
299
-            }
300
-        }
301
-        $this->persistManyToManyRelationships($object);
302
-    }
303
-
304
-    /**
305
-     * This function removes the given object from the database. It will also remove all objects relied to the one given
306
-     * by parameter before all.
307
-     *
308
-     * Notice: if the object has a multiple primary key, the function will not work.
309
-     *
310
-     * @param AbstractTDBMObject $objToDelete
311
-     */
312
-    public function deleteCascade(AbstractTDBMObject $objToDelete)
313
-    {
314
-        $this->deleteAllConstraintWithThisObject($objToDelete);
315
-        $this->delete($objToDelete);
316
-    }
317
-
318
-    /**
319
-     * This function is used only in TDBMService (private function)
320
-     * It will call deleteCascade function foreach object relied with a foreign key to the object given by parameter.
321
-     *
322
-     * @param AbstractTDBMObject $obj
323
-     */
324
-    private function deleteAllConstraintWithThisObject(AbstractTDBMObject $obj)
325
-    {
326
-        $dbRows = $obj->_getDbRows();
327
-        foreach ($dbRows as $dbRow) {
328
-            $tableName = $dbRow->_getDbTableName();
329
-            $pks = array_values($dbRow->_getPrimaryKeys());
330
-            if (!empty($pks)) {
331
-                $incomingFks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($tableName);
332
-
333
-                foreach ($incomingFks as $incomingFk) {
334
-                    $filter = array_combine($incomingFk->getLocalColumns(), $pks);
335
-
336
-                    $results = $this->findObjects($incomingFk->getLocalTableName(), $filter);
337
-
338
-                    foreach ($results as $bean) {
339
-                        $this->deleteCascade($bean);
340
-                    }
341
-                }
342
-            }
343
-        }
344
-    }
345
-
346
-    /**
347
-     * This function performs a save() of all the objects that have been modified.
348
-     */
349
-    public function completeSave()
350
-    {
351
-        foreach ($this->toSaveObjects as $dbRow) {
352
-            $this->save($dbRow->getTDBMObject());
353
-        }
354
-    }
355
-
356
-    /**
357
-     * Takes in input a filter_bag (which can be about anything from a string to an array of TDBMObjects... see above from documentation),
358
-     * and gives back a proper Filter object.
359
-     *
360
-     * @param mixed $filter_bag
361
-     * @param int   $counter
362
-     *
363
-     * @return array First item: filter string, second item: parameters
364
-     *
365
-     * @throws TDBMException
366
-     */
367
-    public function buildFilterFromFilterBag($filter_bag, $counter = 1)
368
-    {
369
-        if ($filter_bag === null) {
370
-            return ['', []];
371
-        } elseif (is_string($filter_bag)) {
372
-            return [$filter_bag, []];
373
-        } elseif (is_array($filter_bag)) {
374
-            $sqlParts = [];
375
-            $parameters = [];
376
-            foreach ($filter_bag as $column => $value) {
377
-                if (is_int($column)) {
378
-                    list($subSqlPart, $subParameters) = $this->buildFilterFromFilterBag($value, $counter);
379
-                    $sqlParts[] = $subSqlPart;
380
-                    $parameters += $subParameters;
381
-                } else {
382
-                    $paramName = 'tdbmparam'.$counter;
383
-                    if (is_array($value)) {
384
-                        $sqlParts[] = $this->connection->quoteIdentifier($column).' IN :'.$paramName;
385
-                    } else {
386
-                        $sqlParts[] = $this->connection->quoteIdentifier($column).' = :'.$paramName;
387
-                    }
388
-                    $parameters[$paramName] = $value;
389
-                    ++$counter;
390
-                }
391
-            }
392
-
393
-            return [implode(' AND ', $sqlParts), $parameters];
394
-        } elseif ($filter_bag instanceof AbstractTDBMObject) {
395
-            $sqlParts = [];
396
-            $parameters = [];
397
-            $dbRows = $filter_bag->_getDbRows();
398
-            $dbRow = reset($dbRows);
399
-            $primaryKeys = $dbRow->_getPrimaryKeys();
400
-
401
-            foreach ($primaryKeys as $column => $value) {
402
-                $paramName = 'tdbmparam'.$counter;
403
-                $sqlParts[] = $this->connection->quoteIdentifier($dbRow->_getDbTableName()).'.'.$this->connection->quoteIdentifier($column).' = :'.$paramName;
404
-                $parameters[$paramName] = $value;
405
-                ++$counter;
406
-            }
407
-
408
-            return [implode(' AND ', $sqlParts), $parameters];
409
-        } elseif ($filter_bag instanceof \Iterator) {
410
-            return $this->buildFilterFromFilterBag(iterator_to_array($filter_bag), $counter);
411
-        } else {
412
-            throw new TDBMException('Error in filter. An object has been passed that is neither a SQL string, nor an array, nor a bean, nor null.');
413
-        }
414
-    }
415
-
416
-    /**
417
-     * @param string $table
418
-     *
419
-     * @return string[]
420
-     */
421
-    public function getPrimaryKeyColumns($table)
422
-    {
423
-        if (!isset($this->primaryKeysColumns[$table])) {
424
-            $this->primaryKeysColumns[$table] = $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getPrimaryKeyColumns();
425
-
426
-            // TODO TDBM4: See if we need to improve error reporting if table name does not exist.
427
-
428
-            /*$arr = array();
51
+	const MODE_CURSOR = 1;
52
+	const MODE_ARRAY = 2;
53
+
54
+	/**
55
+	 * The database connection.
56
+	 *
57
+	 * @var Connection
58
+	 */
59
+	private $connection;
60
+
61
+	/**
62
+	 * @var SchemaAnalyzer
63
+	 */
64
+	private $schemaAnalyzer;
65
+
66
+	/**
67
+	 * @var MagicQuery
68
+	 */
69
+	private $magicQuery;
70
+
71
+	/**
72
+	 * @var TDBMSchemaAnalyzer
73
+	 */
74
+	private $tdbmSchemaAnalyzer;
75
+
76
+	/**
77
+	 * @var string
78
+	 */
79
+	private $cachePrefix;
80
+
81
+	/**
82
+	 * Cache of table of primary keys.
83
+	 * Primary keys are stored by tables, as an array of column.
84
+	 * For instance $primary_key['my_table'][0] will return the first column of the primary key of table 'my_table'.
85
+	 *
86
+	 * @var string[]
87
+	 */
88
+	private $primaryKeysColumns;
89
+
90
+	/**
91
+	 * Service storing objects in memory.
92
+	 * Access is done by table name and then by primary key.
93
+	 * If the primary key is split on several columns, access is done by an array of columns, serialized.
94
+	 *
95
+	 * @var StandardObjectStorage|WeakrefObjectStorage
96
+	 */
97
+	private $objectStorage;
98
+
99
+	/**
100
+	 * The fetch mode of the result sets returned by `getObjects`.
101
+	 * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY or TDBMObjectArray::MODE_COMPATIBLE_ARRAY.
102
+	 *
103
+	 * In 'MODE_ARRAY' mode (default), the result is an array. Use this mode by default (unless the list returned is very big).
104
+	 * In 'MODE_CURSOR' mode, the result is a Generator which is an iterable collection that can be scanned only once (only one "foreach") on it,
105
+	 * and it cannot be accessed via key. Use this mode for large datasets processed by batch.
106
+	 * In 'MODE_COMPATIBLE_ARRAY' mode, the result is an old TDBMObjectArray (used up to TDBM 3.2).
107
+	 * You can access the array by key, or using foreach, several times.
108
+	 *
109
+	 * @var int
110
+	 */
111
+	private $mode = self::MODE_ARRAY;
112
+
113
+	/**
114
+	 * Table of new objects not yet inserted in database or objects modified that must be saved.
115
+	 *
116
+	 * @var \SplObjectStorage of DbRow objects
117
+	 */
118
+	private $toSaveObjects;
119
+
120
+	/**
121
+	 * A cache service to be used.
122
+	 *
123
+	 * @var Cache|null
124
+	 */
125
+	private $cache;
126
+
127
+	/**
128
+	 * Map associating a table name to a fully qualified Bean class name.
129
+	 *
130
+	 * @var array
131
+	 */
132
+	private $tableToBeanMap = [];
133
+
134
+	/**
135
+	 * @var \ReflectionClass[]
136
+	 */
137
+	private $reflectionClassCache = array();
138
+
139
+	/**
140
+	 * @var LoggerInterface
141
+	 */
142
+	private $rootLogger;
143
+
144
+	/**
145
+	 * @var LevelFilter|NullLogger
146
+	 */
147
+	private $logger;
148
+
149
+	/**
150
+	 * @var OrderByAnalyzer
151
+	 */
152
+	private $orderByAnalyzer;
153
+
154
+	/**
155
+	 * @var string
156
+	 */
157
+	private $beanNamespace;
158
+
159
+	/**
160
+	 * @var NamingStrategyInterface
161
+	 */
162
+	private $namingStrategy;
163
+	/**
164
+	 * @var ConfigurationInterface
165
+	 */
166
+	private $configuration;
167
+
168
+	/**
169
+	 * @param ConfigurationInterface $configuration The configuration object
170
+	 */
171
+	public function __construct(ConfigurationInterface $configuration /*Connection $connection, Cache $cache = null, SchemaAnalyzer $schemaAnalyzer = null, LoggerInterface $logger = null*/)
172
+	{
173
+		if (extension_loaded('weakref')) {
174
+			$this->objectStorage = new WeakrefObjectStorage();
175
+		} else {
176
+			$this->objectStorage = new StandardObjectStorage();
177
+		}
178
+		$this->connection = $configuration->getConnection();
179
+		$this->cache = $configuration->getCache();
180
+		$this->schemaAnalyzer = $configuration->getSchemaAnalyzer();
181
+
182
+		$this->magicQuery = new MagicQuery($this->connection, $this->cache, $this->schemaAnalyzer);
183
+
184
+		$this->tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->connection, $this->cache, $this->schemaAnalyzer);
185
+		$this->cachePrefix = $this->tdbmSchemaAnalyzer->getCachePrefix();
186
+
187
+		$this->toSaveObjects = new \SplObjectStorage();
188
+		$logger = $configuration->getLogger();
189
+		if ($logger === null) {
190
+			$this->logger = new NullLogger();
191
+			$this->rootLogger = new NullLogger();
192
+		} else {
193
+			$this->rootLogger = $logger;
194
+			$this->setLogLevel(LogLevel::WARNING);
195
+		}
196
+		$this->orderByAnalyzer = new OrderByAnalyzer($this->cache, $this->cachePrefix);
197
+		$this->beanNamespace = $configuration->getBeanNamespace();
198
+		$this->namingStrategy = $configuration->getNamingStrategy();
199
+		$this->configuration = $configuration;
200
+	}
201
+
202
+	/**
203
+	 * Returns the object used to connect to the database.
204
+	 *
205
+	 * @return Connection
206
+	 */
207
+	public function getConnection(): Connection
208
+	{
209
+		return $this->connection;
210
+	}
211
+
212
+	/**
213
+	 * Sets the default fetch mode of the result sets returned by `findObjects`.
214
+	 * Can be one of: TDBMObjectArray::MODE_CURSOR or TDBMObjectArray::MODE_ARRAY.
215
+	 *
216
+	 * In 'MODE_ARRAY' mode (default), the result is a ResultIterator object that behaves like an array. Use this mode by default (unless the list returned is very big).
217
+	 * In 'MODE_CURSOR' mode, the result is a ResultIterator object. If you scan it many times (by calling several time a foreach loop), the query will be run
218
+	 * several times. In cursor mode, you cannot access the result set by key. Use this mode for large datasets processed by batch.
219
+	 *
220
+	 * @param int $mode
221
+	 *
222
+	 * @return $this
223
+	 *
224
+	 * @throws TDBMException
225
+	 */
226
+	public function setFetchMode($mode)
227
+	{
228
+		if ($mode !== self::MODE_CURSOR && $mode !== self::MODE_ARRAY) {
229
+			throw new TDBMException("Unknown fetch mode: '".$this->mode."'");
230
+		}
231
+		$this->mode = $mode;
232
+
233
+		return $this;
234
+	}
235
+
236
+	/**
237
+	 * Removes the given object from database.
238
+	 * This cannot be called on an object that is not attached to this TDBMService
239
+	 * (will throw a TDBMInvalidOperationException).
240
+	 *
241
+	 * @param AbstractTDBMObject $object the object to delete
242
+	 *
243
+	 * @throws TDBMException
244
+	 * @throws TDBMInvalidOperationException
245
+	 */
246
+	public function delete(AbstractTDBMObject $object)
247
+	{
248
+		switch ($object->_getStatus()) {
249
+			case TDBMObjectStateEnum::STATE_DELETED:
250
+				// Nothing to do, object already deleted.
251
+				return;
252
+			case TDBMObjectStateEnum::STATE_DETACHED:
253
+				throw new TDBMInvalidOperationException('Cannot delete a detached object');
254
+			case TDBMObjectStateEnum::STATE_NEW:
255
+				$this->deleteManyToManyRelationships($object);
256
+				foreach ($object->_getDbRows() as $dbRow) {
257
+					$this->removeFromToSaveObjectList($dbRow);
258
+				}
259
+				break;
260
+			case TDBMObjectStateEnum::STATE_DIRTY:
261
+				foreach ($object->_getDbRows() as $dbRow) {
262
+					$this->removeFromToSaveObjectList($dbRow);
263
+				}
264
+				// And continue deleting...
265
+			case TDBMObjectStateEnum::STATE_NOT_LOADED:
266
+			case TDBMObjectStateEnum::STATE_LOADED:
267
+				$this->deleteManyToManyRelationships($object);
268
+				// Let's delete db rows, in reverse order.
269
+				foreach (array_reverse($object->_getDbRows()) as $dbRow) {
270
+					$tableName = $dbRow->_getDbTableName();
271
+					$primaryKeys = $dbRow->_getPrimaryKeys();
272
+					$this->connection->delete($tableName, $primaryKeys);
273
+					$this->objectStorage->remove($dbRow->_getDbTableName(), $this->getObjectHash($primaryKeys));
274
+				}
275
+				break;
276
+			// @codeCoverageIgnoreStart
277
+			default:
278
+				throw new TDBMInvalidOperationException('Unexpected status for bean');
279
+			// @codeCoverageIgnoreEnd
280
+		}
281
+
282
+		$object->_setStatus(TDBMObjectStateEnum::STATE_DELETED);
283
+	}
284
+
285
+	/**
286
+	 * Removes all many to many relationships for this object.
287
+	 *
288
+	 * @param AbstractTDBMObject $object
289
+	 */
290
+	private function deleteManyToManyRelationships(AbstractTDBMObject $object)
291
+	{
292
+		foreach ($object->_getDbRows() as $tableName => $dbRow) {
293
+			$pivotTables = $this->tdbmSchemaAnalyzer->getPivotTableLinkedToTable($tableName);
294
+			foreach ($pivotTables as $pivotTable) {
295
+				$remoteBeans = $object->_getRelationships($pivotTable);
296
+				foreach ($remoteBeans as $remoteBean) {
297
+					$object->_removeRelationship($pivotTable, $remoteBean);
298
+				}
299
+			}
300
+		}
301
+		$this->persistManyToManyRelationships($object);
302
+	}
303
+
304
+	/**
305
+	 * This function removes the given object from the database. It will also remove all objects relied to the one given
306
+	 * by parameter before all.
307
+	 *
308
+	 * Notice: if the object has a multiple primary key, the function will not work.
309
+	 *
310
+	 * @param AbstractTDBMObject $objToDelete
311
+	 */
312
+	public function deleteCascade(AbstractTDBMObject $objToDelete)
313
+	{
314
+		$this->deleteAllConstraintWithThisObject($objToDelete);
315
+		$this->delete($objToDelete);
316
+	}
317
+
318
+	/**
319
+	 * This function is used only in TDBMService (private function)
320
+	 * It will call deleteCascade function foreach object relied with a foreign key to the object given by parameter.
321
+	 *
322
+	 * @param AbstractTDBMObject $obj
323
+	 */
324
+	private function deleteAllConstraintWithThisObject(AbstractTDBMObject $obj)
325
+	{
326
+		$dbRows = $obj->_getDbRows();
327
+		foreach ($dbRows as $dbRow) {
328
+			$tableName = $dbRow->_getDbTableName();
329
+			$pks = array_values($dbRow->_getPrimaryKeys());
330
+			if (!empty($pks)) {
331
+				$incomingFks = $this->tdbmSchemaAnalyzer->getIncomingForeignKeys($tableName);
332
+
333
+				foreach ($incomingFks as $incomingFk) {
334
+					$filter = array_combine($incomingFk->getLocalColumns(), $pks);
335
+
336
+					$results = $this->findObjects($incomingFk->getLocalTableName(), $filter);
337
+
338
+					foreach ($results as $bean) {
339
+						$this->deleteCascade($bean);
340
+					}
341
+				}
342
+			}
343
+		}
344
+	}
345
+
346
+	/**
347
+	 * This function performs a save() of all the objects that have been modified.
348
+	 */
349
+	public function completeSave()
350
+	{
351
+		foreach ($this->toSaveObjects as $dbRow) {
352
+			$this->save($dbRow->getTDBMObject());
353
+		}
354
+	}
355
+
356
+	/**
357
+	 * Takes in input a filter_bag (which can be about anything from a string to an array of TDBMObjects... see above from documentation),
358
+	 * and gives back a proper Filter object.
359
+	 *
360
+	 * @param mixed $filter_bag
361
+	 * @param int   $counter
362
+	 *
363
+	 * @return array First item: filter string, second item: parameters
364
+	 *
365
+	 * @throws TDBMException
366
+	 */
367
+	public function buildFilterFromFilterBag($filter_bag, $counter = 1)
368
+	{
369
+		if ($filter_bag === null) {
370
+			return ['', []];
371
+		} elseif (is_string($filter_bag)) {
372
+			return [$filter_bag, []];
373
+		} elseif (is_array($filter_bag)) {
374
+			$sqlParts = [];
375
+			$parameters = [];
376
+			foreach ($filter_bag as $column => $value) {
377
+				if (is_int($column)) {
378
+					list($subSqlPart, $subParameters) = $this->buildFilterFromFilterBag($value, $counter);
379
+					$sqlParts[] = $subSqlPart;
380
+					$parameters += $subParameters;
381
+				} else {
382
+					$paramName = 'tdbmparam'.$counter;
383
+					if (is_array($value)) {
384
+						$sqlParts[] = $this->connection->quoteIdentifier($column).' IN :'.$paramName;
385
+					} else {
386
+						$sqlParts[] = $this->connection->quoteIdentifier($column).' = :'.$paramName;
387
+					}
388
+					$parameters[$paramName] = $value;
389
+					++$counter;
390
+				}
391
+			}
392
+
393
+			return [implode(' AND ', $sqlParts), $parameters];
394
+		} elseif ($filter_bag instanceof AbstractTDBMObject) {
395
+			$sqlParts = [];
396
+			$parameters = [];
397
+			$dbRows = $filter_bag->_getDbRows();
398
+			$dbRow = reset($dbRows);
399
+			$primaryKeys = $dbRow->_getPrimaryKeys();
400
+
401
+			foreach ($primaryKeys as $column => $value) {
402
+				$paramName = 'tdbmparam'.$counter;
403
+				$sqlParts[] = $this->connection->quoteIdentifier($dbRow->_getDbTableName()).'.'.$this->connection->quoteIdentifier($column).' = :'.$paramName;
404
+				$parameters[$paramName] = $value;
405
+				++$counter;
406
+			}
407
+
408
+			return [implode(' AND ', $sqlParts), $parameters];
409
+		} elseif ($filter_bag instanceof \Iterator) {
410
+			return $this->buildFilterFromFilterBag(iterator_to_array($filter_bag), $counter);
411
+		} else {
412
+			throw new TDBMException('Error in filter. An object has been passed that is neither a SQL string, nor an array, nor a bean, nor null.');
413
+		}
414
+	}
415
+
416
+	/**
417
+	 * @param string $table
418
+	 *
419
+	 * @return string[]
420
+	 */
421
+	public function getPrimaryKeyColumns($table)
422
+	{
423
+		if (!isset($this->primaryKeysColumns[$table])) {
424
+			$this->primaryKeysColumns[$table] = $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getPrimaryKeyColumns();
425
+
426
+			// TODO TDBM4: See if we need to improve error reporting if table name does not exist.
427
+
428
+			/*$arr = array();
429 429
             foreach ($this->connection->getPrimaryKey($table) as $col) {
430 430
                 $arr[] = $col->name;
431 431
             }
@@ -446,161 +446,161 @@  discard block
 block discarded – undo
446 446
                     throw new TDBMException($str);
447 447
                 }
448 448
             }*/
449
-        }
450
-
451
-        return $this->primaryKeysColumns[$table];
452
-    }
453
-
454
-    /**
455
-     * This is an internal function, you should not use it in your application.
456
-     * This is used internally by TDBM to add an object to the object cache.
457
-     *
458
-     * @param DbRow $dbRow
459
-     */
460
-    public function _addToCache(DbRow $dbRow)
461
-    {
462
-        $primaryKey = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
463
-        $hash = $this->getObjectHash($primaryKey);
464
-        $this->objectStorage->set($dbRow->_getDbTableName(), $hash, $dbRow);
465
-    }
466
-
467
-    /**
468
-     * This is an internal function, you should not use it in your application.
469
-     * This is used internally by TDBM to remove the object from the list of objects that have been
470
-     * created/updated but not saved yet.
471
-     *
472
-     * @param DbRow $myObject
473
-     */
474
-    private function removeFromToSaveObjectList(DbRow $myObject)
475
-    {
476
-        unset($this->toSaveObjects[$myObject]);
477
-    }
478
-
479
-    /**
480
-     * This is an internal function, you should not use it in your application.
481
-     * This is used internally by TDBM to add an object to the list of objects that have been
482
-     * created/updated but not saved yet.
483
-     *
484
-     * @param DbRow $myObject
485
-     */
486
-    public function _addToToSaveObjectList(DbRow $myObject)
487
-    {
488
-        $this->toSaveObjects[$myObject] = true;
489
-    }
490
-
491
-    /**
492
-     * Generates all the daos and beans.
493
-     *
494
-     * @param string $composerFile        If it's set, location of custom Composer file. Relative to project root
495
-     *
496
-     * @return \string[] the list of tables (key) and bean name (value)
497
-     */
498
-    public function generateAllDaosAndBeans($composerFile = null)
499
-    {
500
-        // Purge cache before generating anything.
501
-        $this->cache->deleteAll();
502
-
503
-        $tdbmDaoGenerator = new TDBMDaoGenerator($this->configuration, $this->tdbmSchemaAnalyzer->getSchema(), $this->tdbmSchemaAnalyzer);
504
-        if (null !== $composerFile) {
505
-            $tdbmDaoGenerator->setComposerFile(__DIR__.'/../../../../../../../'.$composerFile);
506
-        }
507
-
508
-        $tdbmDaoGenerator->generateAllDaosAndBeans();
509
-    }
510
-
511
-    /**
512
-     * Returns the fully qualified class name of the bean associated with table $tableName.
513
-     *
514
-     *
515
-     * @param string $tableName
516
-     *
517
-     * @return string
518
-     */
519
-    public function getBeanClassName(string $tableName) : string
520
-    {
521
-        if (isset($this->tableToBeanMap[$tableName])) {
522
-            return $this->tableToBeanMap[$tableName];
523
-        } else {
524
-            $className = $this->beanNamespace.'\\'.$this->namingStrategy->getBeanClassName($tableName);
525
-
526
-            if (!class_exists($className)) {
527
-                throw new TDBMInvalidArgumentException(sprintf('Could not find class "%s". Does table "%s" exist? If yes, consider regenerating the DAOs and beans.', $className, $tableName));
528
-            }
529
-
530
-            $this->tableToBeanMap[$tableName] = $className;
531
-            return $className;
532
-        }
533
-    }
534
-
535
-    /**
536
-     * Saves $object by INSERTing or UPDAT(E)ing it in the database.
537
-     *
538
-     * @param AbstractTDBMObject $object
539
-     *
540
-     * @throws TDBMException
541
-     */
542
-    public function save(AbstractTDBMObject $object)
543
-    {
544
-        $status = $object->_getStatus();
545
-
546
-        if ($status === null) {
547
-            throw new TDBMException(sprintf('Your bean for class %s has no status. It is likely that you overloaded the __construct method and forgot to call parent::__construct.', get_class($object)));
548
-        }
549
-
550
-        // Let's attach this object if it is in detached state.
551
-        if ($status === TDBMObjectStateEnum::STATE_DETACHED) {
552
-            $object->_attach($this);
553
-            $status = $object->_getStatus();
554
-        }
555
-
556
-        if ($status === TDBMObjectStateEnum::STATE_NEW) {
557
-            $dbRows = $object->_getDbRows();
558
-
559
-            $unindexedPrimaryKeys = array();
560
-
561
-            foreach ($dbRows as $dbRow) {
562
-                if ($dbRow->_getStatus() == TDBMObjectStateEnum::STATE_SAVING) {
563
-                    throw TDBMCyclicReferenceException::createCyclicReference($dbRow->_getDbTableName(), $object);
564
-                }
565
-                $dbRow->_setStatus(TDBMObjectStateEnum::STATE_SAVING);
566
-                $tableName = $dbRow->_getDbTableName();
567
-
568
-                $schema = $this->tdbmSchemaAnalyzer->getSchema();
569
-                $tableDescriptor = $schema->getTable($tableName);
570
-
571
-                $primaryKeyColumns = $this->getPrimaryKeyColumns($tableName);
572
-
573
-                $references = $dbRow->_getReferences();
574
-
575
-                // Let's save all references in NEW or DETACHED state (we need their primary key)
576
-                foreach ($references as $fkName => $reference) {
577
-                    if ($reference !== null) {
578
-                        $refStatus = $reference->_getStatus();
579
-                        if ($refStatus === TDBMObjectStateEnum::STATE_NEW || $refStatus === TDBMObjectStateEnum::STATE_DETACHED) {
580
-                            try {
581
-                                $this->save($reference);
582
-                            } catch (TDBMCyclicReferenceException $e) {
583
-                                throw TDBMCyclicReferenceException::extendCyclicReference($e, $dbRow->_getDbTableName(), $object, $fkName);
584
-                            }
585
-                        }
586
-                    }
587
-                }
588
-
589
-                if (empty($unindexedPrimaryKeys)) {
590
-                    $primaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
591
-                } else {
592
-                    // First insert, the children must have the same primary key as the parent.
593
-                    $primaryKeys = $this->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $unindexedPrimaryKeys);
594
-                    $dbRow->_setPrimaryKeys($primaryKeys);
595
-                }
596
-
597
-                $dbRowData = $dbRow->_getDbRow();
598
-
599
-                // Let's see if the columns for primary key have been set before inserting.
600
-                // We assume that if one of the value of the PK has been set, the PK is set.
601
-                $isPkSet = !empty($primaryKeys);
602
-
603
-                /*if (!$isPkSet) {
449
+		}
450
+
451
+		return $this->primaryKeysColumns[$table];
452
+	}
453
+
454
+	/**
455
+	 * This is an internal function, you should not use it in your application.
456
+	 * This is used internally by TDBM to add an object to the object cache.
457
+	 *
458
+	 * @param DbRow $dbRow
459
+	 */
460
+	public function _addToCache(DbRow $dbRow)
461
+	{
462
+		$primaryKey = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
463
+		$hash = $this->getObjectHash($primaryKey);
464
+		$this->objectStorage->set($dbRow->_getDbTableName(), $hash, $dbRow);
465
+	}
466
+
467
+	/**
468
+	 * This is an internal function, you should not use it in your application.
469
+	 * This is used internally by TDBM to remove the object from the list of objects that have been
470
+	 * created/updated but not saved yet.
471
+	 *
472
+	 * @param DbRow $myObject
473
+	 */
474
+	private function removeFromToSaveObjectList(DbRow $myObject)
475
+	{
476
+		unset($this->toSaveObjects[$myObject]);
477
+	}
478
+
479
+	/**
480
+	 * This is an internal function, you should not use it in your application.
481
+	 * This is used internally by TDBM to add an object to the list of objects that have been
482
+	 * created/updated but not saved yet.
483
+	 *
484
+	 * @param DbRow $myObject
485
+	 */
486
+	public function _addToToSaveObjectList(DbRow $myObject)
487
+	{
488
+		$this->toSaveObjects[$myObject] = true;
489
+	}
490
+
491
+	/**
492
+	 * Generates all the daos and beans.
493
+	 *
494
+	 * @param string $composerFile        If it's set, location of custom Composer file. Relative to project root
495
+	 *
496
+	 * @return \string[] the list of tables (key) and bean name (value)
497
+	 */
498
+	public function generateAllDaosAndBeans($composerFile = null)
499
+	{
500
+		// Purge cache before generating anything.
501
+		$this->cache->deleteAll();
502
+
503
+		$tdbmDaoGenerator = new TDBMDaoGenerator($this->configuration, $this->tdbmSchemaAnalyzer->getSchema(), $this->tdbmSchemaAnalyzer);
504
+		if (null !== $composerFile) {
505
+			$tdbmDaoGenerator->setComposerFile(__DIR__.'/../../../../../../../'.$composerFile);
506
+		}
507
+
508
+		$tdbmDaoGenerator->generateAllDaosAndBeans();
509
+	}
510
+
511
+	/**
512
+	 * Returns the fully qualified class name of the bean associated with table $tableName.
513
+	 *
514
+	 *
515
+	 * @param string $tableName
516
+	 *
517
+	 * @return string
518
+	 */
519
+	public function getBeanClassName(string $tableName) : string
520
+	{
521
+		if (isset($this->tableToBeanMap[$tableName])) {
522
+			return $this->tableToBeanMap[$tableName];
523
+		} else {
524
+			$className = $this->beanNamespace.'\\'.$this->namingStrategy->getBeanClassName($tableName);
525
+
526
+			if (!class_exists($className)) {
527
+				throw new TDBMInvalidArgumentException(sprintf('Could not find class "%s". Does table "%s" exist? If yes, consider regenerating the DAOs and beans.', $className, $tableName));
528
+			}
529
+
530
+			$this->tableToBeanMap[$tableName] = $className;
531
+			return $className;
532
+		}
533
+	}
534
+
535
+	/**
536
+	 * Saves $object by INSERTing or UPDAT(E)ing it in the database.
537
+	 *
538
+	 * @param AbstractTDBMObject $object
539
+	 *
540
+	 * @throws TDBMException
541
+	 */
542
+	public function save(AbstractTDBMObject $object)
543
+	{
544
+		$status = $object->_getStatus();
545
+
546
+		if ($status === null) {
547
+			throw new TDBMException(sprintf('Your bean for class %s has no status. It is likely that you overloaded the __construct method and forgot to call parent::__construct.', get_class($object)));
548
+		}
549
+
550
+		// Let's attach this object if it is in detached state.
551
+		if ($status === TDBMObjectStateEnum::STATE_DETACHED) {
552
+			$object->_attach($this);
553
+			$status = $object->_getStatus();
554
+		}
555
+
556
+		if ($status === TDBMObjectStateEnum::STATE_NEW) {
557
+			$dbRows = $object->_getDbRows();
558
+
559
+			$unindexedPrimaryKeys = array();
560
+
561
+			foreach ($dbRows as $dbRow) {
562
+				if ($dbRow->_getStatus() == TDBMObjectStateEnum::STATE_SAVING) {
563
+					throw TDBMCyclicReferenceException::createCyclicReference($dbRow->_getDbTableName(), $object);
564
+				}
565
+				$dbRow->_setStatus(TDBMObjectStateEnum::STATE_SAVING);
566
+				$tableName = $dbRow->_getDbTableName();
567
+
568
+				$schema = $this->tdbmSchemaAnalyzer->getSchema();
569
+				$tableDescriptor = $schema->getTable($tableName);
570
+
571
+				$primaryKeyColumns = $this->getPrimaryKeyColumns($tableName);
572
+
573
+				$references = $dbRow->_getReferences();
574
+
575
+				// Let's save all references in NEW or DETACHED state (we need their primary key)
576
+				foreach ($references as $fkName => $reference) {
577
+					if ($reference !== null) {
578
+						$refStatus = $reference->_getStatus();
579
+						if ($refStatus === TDBMObjectStateEnum::STATE_NEW || $refStatus === TDBMObjectStateEnum::STATE_DETACHED) {
580
+							try {
581
+								$this->save($reference);
582
+							} catch (TDBMCyclicReferenceException $e) {
583
+								throw TDBMCyclicReferenceException::extendCyclicReference($e, $dbRow->_getDbTableName(), $object, $fkName);
584
+							}
585
+						}
586
+					}
587
+				}
588
+
589
+				if (empty($unindexedPrimaryKeys)) {
590
+					$primaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
591
+				} else {
592
+					// First insert, the children must have the same primary key as the parent.
593
+					$primaryKeys = $this->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $unindexedPrimaryKeys);
594
+					$dbRow->_setPrimaryKeys($primaryKeys);
595
+				}
596
+
597
+				$dbRowData = $dbRow->_getDbRow();
598
+
599
+				// Let's see if the columns for primary key have been set before inserting.
600
+				// We assume that if one of the value of the PK has been set, the PK is set.
601
+				$isPkSet = !empty($primaryKeys);
602
+
603
+				/*if (!$isPkSet) {
604 604
                     // if there is no autoincrement and no pk set, let's go in error.
605 605
                     $isAutoIncrement = true;
606 606
 
@@ -618,30 +618,30 @@  discard block
 block discarded – undo
618 618
 
619 619
                 }*/
620 620
 
621
-                $types = [];
622
-                $escapedDbRowData = [];
621
+				$types = [];
622
+				$escapedDbRowData = [];
623 623
 
624
-                foreach ($dbRowData as $columnName => $value) {
625
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
626
-                    $types[] = $columnDescriptor->getType();
627
-                    $escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
628
-                }
624
+				foreach ($dbRowData as $columnName => $value) {
625
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
626
+					$types[] = $columnDescriptor->getType();
627
+					$escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
628
+				}
629 629
 
630
-                $this->connection->insert($tableName, $escapedDbRowData, $types);
630
+				$this->connection->insert($tableName, $escapedDbRowData, $types);
631 631
 
632
-                if (!$isPkSet && count($primaryKeyColumns) == 1) {
633
-                    $id = $this->connection->lastInsertId();
634
-                    $pkColumn = $primaryKeyColumns[0];
635
-                    // lastInsertId returns a string but the column type is usually a int. Let's convert it back to the correct type.
636
-                    $id = $tableDescriptor->getColumn($pkColumn)->getType()->convertToPHPValue($id, $this->getConnection()->getDatabasePlatform());
637
-                    $primaryKeys[$pkColumn] = $id;
638
-                }
632
+				if (!$isPkSet && count($primaryKeyColumns) == 1) {
633
+					$id = $this->connection->lastInsertId();
634
+					$pkColumn = $primaryKeyColumns[0];
635
+					// lastInsertId returns a string but the column type is usually a int. Let's convert it back to the correct type.
636
+					$id = $tableDescriptor->getColumn($pkColumn)->getType()->convertToPHPValue($id, $this->getConnection()->getDatabasePlatform());
637
+					$primaryKeys[$pkColumn] = $id;
638
+				}
639 639
 
640
-                // TODO: change this to some private magic accessor in future
641
-                $dbRow->_setPrimaryKeys($primaryKeys);
642
-                $unindexedPrimaryKeys = array_values($primaryKeys);
640
+				// TODO: change this to some private magic accessor in future
641
+				$dbRow->_setPrimaryKeys($primaryKeys);
642
+				$unindexedPrimaryKeys = array_values($primaryKeys);
643 643
 
644
-                /*
644
+				/*
645 645
                  * When attached, on "save", we check if the column updated is part of a primary key
646 646
                  * If this is part of a primary key, we call the _update_id method that updates the id in the list of known objects.
647 647
                  * This method should first verify that the id is not already used (and is not auto-incremented)
@@ -651,7 +651,7 @@  discard block
 block discarded – undo
651 651
                  *
652 652
                  */
653 653
 
654
-                /*try {
654
+				/*try {
655 655
                     $this->db_connection->exec($sql);
656 656
                 } catch (TDBMException $e) {
657 657
                     $this->db_onerror = true;
@@ -670,405 +670,405 @@  discard block
 block discarded – undo
670 670
                     }
671 671
                 }*/
672 672
 
673
-                // Let's remove this object from the $new_objects static table.
674
-                $this->removeFromToSaveObjectList($dbRow);
675
-
676
-                // TODO: change this behaviour to something more sensible performance-wise
677
-                // Maybe a setting to trigger this globally?
678
-                //$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
679
-                //$this->db_modified_state = false;
680
-                //$dbRow = array();
681
-
682
-                // Let's add this object to the list of objects in cache.
683
-                $this->_addToCache($dbRow);
684
-            }
685
-
686
-            $object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
687
-        } elseif ($status === TDBMObjectStateEnum::STATE_DIRTY) {
688
-            $dbRows = $object->_getDbRows();
689
-
690
-            foreach ($dbRows as $dbRow) {
691
-                $references = $dbRow->_getReferences();
692
-
693
-                // Let's save all references in NEW state (we need their primary key)
694
-                foreach ($references as $fkName => $reference) {
695
-                    if ($reference !== null && $reference->_getStatus() === TDBMObjectStateEnum::STATE_NEW) {
696
-                        $this->save($reference);
697
-                    }
698
-                }
699
-
700
-                // Let's first get the primary keys
701
-                $tableName = $dbRow->_getDbTableName();
702
-                $dbRowData = $dbRow->_getDbRow();
703
-
704
-                $schema = $this->tdbmSchemaAnalyzer->getSchema();
705
-                $tableDescriptor = $schema->getTable($tableName);
706
-
707
-                $primaryKeys = $dbRow->_getPrimaryKeys();
708
-
709
-                $types = [];
710
-                $escapedDbRowData = [];
711
-                $escapedPrimaryKeys = [];
712
-
713
-                foreach ($dbRowData as $columnName => $value) {
714
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
715
-                    $types[] = $columnDescriptor->getType();
716
-                    $escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
717
-                }
718
-                foreach ($primaryKeys as $columnName => $value) {
719
-                    $columnDescriptor = $tableDescriptor->getColumn($columnName);
720
-                    $types[] = $columnDescriptor->getType();
721
-                    $escapedPrimaryKeys[$this->connection->quoteIdentifier($columnName)] = $value;
722
-                }
723
-
724
-                $this->connection->update($tableName, $escapedDbRowData, $escapedPrimaryKeys, $types);
725
-
726
-                // Let's check if the primary key has been updated...
727
-                $needsUpdatePk = false;
728
-                foreach ($primaryKeys as $column => $value) {
729
-                    if (!isset($dbRowData[$column]) || $dbRowData[$column] != $value) {
730
-                        $needsUpdatePk = true;
731
-                        break;
732
-                    }
733
-                }
734
-                if ($needsUpdatePk) {
735
-                    $this->objectStorage->remove($tableName, $this->getObjectHash($primaryKeys));
736
-                    $newPrimaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
737
-                    $dbRow->_setPrimaryKeys($newPrimaryKeys);
738
-                    $this->objectStorage->set($tableName, $this->getObjectHash($primaryKeys), $dbRow);
739
-                }
740
-
741
-                // Let's remove this object from the list of objects to save.
742
-                $this->removeFromToSaveObjectList($dbRow);
743
-            }
744
-
745
-            $object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
746
-        } elseif ($status === TDBMObjectStateEnum::STATE_DELETED) {
747
-            throw new TDBMInvalidOperationException('This object has been deleted. It cannot be saved.');
748
-        }
749
-
750
-        // Finally, let's save all the many to many relationships to this bean.
751
-        $this->persistManyToManyRelationships($object);
752
-    }
753
-
754
-    private function persistManyToManyRelationships(AbstractTDBMObject $object)
755
-    {
756
-        foreach ($object->_getCachedRelationships() as $pivotTableName => $storage) {
757
-            $tableDescriptor = $this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName);
758
-            list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $object);
759
-
760
-            $toRemoveFromStorage = [];
761
-
762
-            foreach ($storage as $remoteBean) {
763
-                /* @var $remoteBean AbstractTDBMObject */
764
-                $statusArr = $storage[$remoteBean];
765
-                $status = $statusArr['status'];
766
-                $reverse = $statusArr['reverse'];
767
-                if ($reverse) {
768
-                    continue;
769
-                }
770
-
771
-                if ($status === 'new') {
772
-                    $remoteBeanStatus = $remoteBean->_getStatus();
773
-                    if ($remoteBeanStatus === TDBMObjectStateEnum::STATE_NEW || $remoteBeanStatus === TDBMObjectStateEnum::STATE_DETACHED) {
774
-                        // Let's save remote bean if needed.
775
-                        $this->save($remoteBean);
776
-                    }
777
-
778
-                    $filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
779
-
780
-                    $types = [];
781
-                    $escapedFilters = [];
782
-
783
-                    foreach ($filters as $columnName => $value) {
784
-                        $columnDescriptor = $tableDescriptor->getColumn($columnName);
785
-                        $types[] = $columnDescriptor->getType();
786
-                        $escapedFilters[$this->connection->quoteIdentifier($columnName)] = $value;
787
-                    }
788
-
789
-                    $this->connection->insert($pivotTableName, $escapedFilters, $types);
790
-
791
-                    // Finally, let's mark relationships as saved.
792
-                    $statusArr['status'] = 'loaded';
793
-                    $storage[$remoteBean] = $statusArr;
794
-                    $remoteStorage = $remoteBean->_getCachedRelationships()[$pivotTableName];
795
-                    $remoteStatusArr = $remoteStorage[$object];
796
-                    $remoteStatusArr['status'] = 'loaded';
797
-                    $remoteStorage[$object] = $remoteStatusArr;
798
-                } elseif ($status === 'delete') {
799
-                    $filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
800
-
801
-                    $types = [];
802
-
803
-                    foreach ($filters as $columnName => $value) {
804
-                        $columnDescriptor = $tableDescriptor->getColumn($columnName);
805
-                        $types[] = $columnDescriptor->getType();
806
-                    }
807
-
808
-                    $this->connection->delete($pivotTableName, $filters, $types);
809
-
810
-                    // Finally, let's remove relationships completely from bean.
811
-                    $toRemoveFromStorage[] = $remoteBean;
812
-
813
-                    $remoteBean->_getCachedRelationships()[$pivotTableName]->detach($object);
814
-                }
815
-            }
816
-
817
-            // Note: due to https://bugs.php.net/bug.php?id=65629, we cannot delete an element inside a foreach loop on a SplStorageObject.
818
-            // Therefore, we cache elements in the $toRemoveFromStorage to remove them at a later stage.
819
-            foreach ($toRemoveFromStorage as $remoteBean) {
820
-                $storage->detach($remoteBean);
821
-            }
822
-        }
823
-    }
824
-
825
-    private function getPivotFilters(AbstractTDBMObject $localBean, AbstractTDBMObject $remoteBean, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk)
826
-    {
827
-        $localBeanPk = $this->getPrimaryKeyValues($localBean);
828
-        $remoteBeanPk = $this->getPrimaryKeyValues($remoteBean);
829
-        $localColumns = $localFk->getLocalColumns();
830
-        $remoteColumns = $remoteFk->getLocalColumns();
831
-
832
-        $localFilters = array_combine($localColumns, $localBeanPk);
833
-        $remoteFilters = array_combine($remoteColumns, $remoteBeanPk);
834
-
835
-        return array_merge($localFilters, $remoteFilters);
836
-    }
837
-
838
-    /**
839
-     * Returns the "values" of the primary key.
840
-     * This returns the primary key from the $primaryKey attribute, not the one stored in the columns.
841
-     *
842
-     * @param AbstractTDBMObject $bean
843
-     *
844
-     * @return array numerically indexed array of values
845
-     */
846
-    private function getPrimaryKeyValues(AbstractTDBMObject $bean)
847
-    {
848
-        $dbRows = $bean->_getDbRows();
849
-        $dbRow = reset($dbRows);
850
-
851
-        return array_values($dbRow->_getPrimaryKeys());
852
-    }
853
-
854
-    /**
855
-     * Returns a unique hash used to store the object based on its primary key.
856
-     * If the array contains only one value, then the value is returned.
857
-     * Otherwise, a hash representing the array is returned.
858
-     *
859
-     * @param array $primaryKeys An array of columns => values forming the primary key
860
-     *
861
-     * @return string
862
-     */
863
-    public function getObjectHash(array $primaryKeys)
864
-    {
865
-        if (count($primaryKeys) === 1) {
866
-            return reset($primaryKeys);
867
-        } else {
868
-            ksort($primaryKeys);
869
-
870
-            return md5(json_encode($primaryKeys));
871
-        }
872
-    }
873
-
874
-    /**
875
-     * Returns an array of primary keys from the object.
876
-     * The primary keys are extracted from the object columns and not from the primary keys stored in the
877
-     * $primaryKeys variable of the object.
878
-     *
879
-     * @param DbRow $dbRow
880
-     *
881
-     * @return array Returns an array of column => value
882
-     */
883
-    public function getPrimaryKeysForObjectFromDbRow(DbRow $dbRow)
884
-    {
885
-        $table = $dbRow->_getDbTableName();
886
-        $dbRowData = $dbRow->_getDbRow();
887
-
888
-        return $this->_getPrimaryKeysFromObjectData($table, $dbRowData);
889
-    }
890
-
891
-    /**
892
-     * Returns an array of primary keys for the given row.
893
-     * The primary keys are extracted from the object columns.
894
-     *
895
-     * @param $table
896
-     * @param array $columns
897
-     *
898
-     * @return array
899
-     */
900
-    public function _getPrimaryKeysFromObjectData($table, array $columns)
901
-    {
902
-        $primaryKeyColumns = $this->getPrimaryKeyColumns($table);
903
-        $values = array();
904
-        foreach ($primaryKeyColumns as $column) {
905
-            if (isset($columns[$column])) {
906
-                $values[$column] = $columns[$column];
907
-            }
908
-        }
909
-
910
-        return $values;
911
-    }
912
-
913
-    /**
914
-     * Attaches $object to this TDBMService.
915
-     * The $object must be in DETACHED state and will pass in NEW state.
916
-     *
917
-     * @param AbstractTDBMObject $object
918
-     *
919
-     * @throws TDBMInvalidOperationException
920
-     */
921
-    public function attach(AbstractTDBMObject $object)
922
-    {
923
-        $object->_attach($this);
924
-    }
925
-
926
-    /**
927
-     * Returns an associative array (column => value) for the primary keys from the table name and an
928
-     * indexed array of primary key values.
929
-     *
930
-     * @param string $tableName
931
-     * @param array  $indexedPrimaryKeys
932
-     */
933
-    public function _getPrimaryKeysFromIndexedPrimaryKeys($tableName, array $indexedPrimaryKeys)
934
-    {
935
-        $primaryKeyColumns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getPrimaryKeyColumns();
936
-
937
-        if (count($primaryKeyColumns) !== count($indexedPrimaryKeys)) {
938
-            throw new TDBMException(sprintf('Wrong number of columns passed for primary key. Expected %s columns for table "%s",
673
+				// Let's remove this object from the $new_objects static table.
674
+				$this->removeFromToSaveObjectList($dbRow);
675
+
676
+				// TODO: change this behaviour to something more sensible performance-wise
677
+				// Maybe a setting to trigger this globally?
678
+				//$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
679
+				//$this->db_modified_state = false;
680
+				//$dbRow = array();
681
+
682
+				// Let's add this object to the list of objects in cache.
683
+				$this->_addToCache($dbRow);
684
+			}
685
+
686
+			$object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
687
+		} elseif ($status === TDBMObjectStateEnum::STATE_DIRTY) {
688
+			$dbRows = $object->_getDbRows();
689
+
690
+			foreach ($dbRows as $dbRow) {
691
+				$references = $dbRow->_getReferences();
692
+
693
+				// Let's save all references in NEW state (we need their primary key)
694
+				foreach ($references as $fkName => $reference) {
695
+					if ($reference !== null && $reference->_getStatus() === TDBMObjectStateEnum::STATE_NEW) {
696
+						$this->save($reference);
697
+					}
698
+				}
699
+
700
+				// Let's first get the primary keys
701
+				$tableName = $dbRow->_getDbTableName();
702
+				$dbRowData = $dbRow->_getDbRow();
703
+
704
+				$schema = $this->tdbmSchemaAnalyzer->getSchema();
705
+				$tableDescriptor = $schema->getTable($tableName);
706
+
707
+				$primaryKeys = $dbRow->_getPrimaryKeys();
708
+
709
+				$types = [];
710
+				$escapedDbRowData = [];
711
+				$escapedPrimaryKeys = [];
712
+
713
+				foreach ($dbRowData as $columnName => $value) {
714
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
715
+					$types[] = $columnDescriptor->getType();
716
+					$escapedDbRowData[$this->connection->quoteIdentifier($columnName)] = $value;
717
+				}
718
+				foreach ($primaryKeys as $columnName => $value) {
719
+					$columnDescriptor = $tableDescriptor->getColumn($columnName);
720
+					$types[] = $columnDescriptor->getType();
721
+					$escapedPrimaryKeys[$this->connection->quoteIdentifier($columnName)] = $value;
722
+				}
723
+
724
+				$this->connection->update($tableName, $escapedDbRowData, $escapedPrimaryKeys, $types);
725
+
726
+				// Let's check if the primary key has been updated...
727
+				$needsUpdatePk = false;
728
+				foreach ($primaryKeys as $column => $value) {
729
+					if (!isset($dbRowData[$column]) || $dbRowData[$column] != $value) {
730
+						$needsUpdatePk = true;
731
+						break;
732
+					}
733
+				}
734
+				if ($needsUpdatePk) {
735
+					$this->objectStorage->remove($tableName, $this->getObjectHash($primaryKeys));
736
+					$newPrimaryKeys = $this->getPrimaryKeysForObjectFromDbRow($dbRow);
737
+					$dbRow->_setPrimaryKeys($newPrimaryKeys);
738
+					$this->objectStorage->set($tableName, $this->getObjectHash($primaryKeys), $dbRow);
739
+				}
740
+
741
+				// Let's remove this object from the list of objects to save.
742
+				$this->removeFromToSaveObjectList($dbRow);
743
+			}
744
+
745
+			$object->_setStatus(TDBMObjectStateEnum::STATE_LOADED);
746
+		} elseif ($status === TDBMObjectStateEnum::STATE_DELETED) {
747
+			throw new TDBMInvalidOperationException('This object has been deleted. It cannot be saved.');
748
+		}
749
+
750
+		// Finally, let's save all the many to many relationships to this bean.
751
+		$this->persistManyToManyRelationships($object);
752
+	}
753
+
754
+	private function persistManyToManyRelationships(AbstractTDBMObject $object)
755
+	{
756
+		foreach ($object->_getCachedRelationships() as $pivotTableName => $storage) {
757
+			$tableDescriptor = $this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName);
758
+			list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $object);
759
+
760
+			$toRemoveFromStorage = [];
761
+
762
+			foreach ($storage as $remoteBean) {
763
+				/* @var $remoteBean AbstractTDBMObject */
764
+				$statusArr = $storage[$remoteBean];
765
+				$status = $statusArr['status'];
766
+				$reverse = $statusArr['reverse'];
767
+				if ($reverse) {
768
+					continue;
769
+				}
770
+
771
+				if ($status === 'new') {
772
+					$remoteBeanStatus = $remoteBean->_getStatus();
773
+					if ($remoteBeanStatus === TDBMObjectStateEnum::STATE_NEW || $remoteBeanStatus === TDBMObjectStateEnum::STATE_DETACHED) {
774
+						// Let's save remote bean if needed.
775
+						$this->save($remoteBean);
776
+					}
777
+
778
+					$filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
779
+
780
+					$types = [];
781
+					$escapedFilters = [];
782
+
783
+					foreach ($filters as $columnName => $value) {
784
+						$columnDescriptor = $tableDescriptor->getColumn($columnName);
785
+						$types[] = $columnDescriptor->getType();
786
+						$escapedFilters[$this->connection->quoteIdentifier($columnName)] = $value;
787
+					}
788
+
789
+					$this->connection->insert($pivotTableName, $escapedFilters, $types);
790
+
791
+					// Finally, let's mark relationships as saved.
792
+					$statusArr['status'] = 'loaded';
793
+					$storage[$remoteBean] = $statusArr;
794
+					$remoteStorage = $remoteBean->_getCachedRelationships()[$pivotTableName];
795
+					$remoteStatusArr = $remoteStorage[$object];
796
+					$remoteStatusArr['status'] = 'loaded';
797
+					$remoteStorage[$object] = $remoteStatusArr;
798
+				} elseif ($status === 'delete') {
799
+					$filters = $this->getPivotFilters($object, $remoteBean, $localFk, $remoteFk);
800
+
801
+					$types = [];
802
+
803
+					foreach ($filters as $columnName => $value) {
804
+						$columnDescriptor = $tableDescriptor->getColumn($columnName);
805
+						$types[] = $columnDescriptor->getType();
806
+					}
807
+
808
+					$this->connection->delete($pivotTableName, $filters, $types);
809
+
810
+					// Finally, let's remove relationships completely from bean.
811
+					$toRemoveFromStorage[] = $remoteBean;
812
+
813
+					$remoteBean->_getCachedRelationships()[$pivotTableName]->detach($object);
814
+				}
815
+			}
816
+
817
+			// Note: due to https://bugs.php.net/bug.php?id=65629, we cannot delete an element inside a foreach loop on a SplStorageObject.
818
+			// Therefore, we cache elements in the $toRemoveFromStorage to remove them at a later stage.
819
+			foreach ($toRemoveFromStorage as $remoteBean) {
820
+				$storage->detach($remoteBean);
821
+			}
822
+		}
823
+	}
824
+
825
+	private function getPivotFilters(AbstractTDBMObject $localBean, AbstractTDBMObject $remoteBean, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk)
826
+	{
827
+		$localBeanPk = $this->getPrimaryKeyValues($localBean);
828
+		$remoteBeanPk = $this->getPrimaryKeyValues($remoteBean);
829
+		$localColumns = $localFk->getLocalColumns();
830
+		$remoteColumns = $remoteFk->getLocalColumns();
831
+
832
+		$localFilters = array_combine($localColumns, $localBeanPk);
833
+		$remoteFilters = array_combine($remoteColumns, $remoteBeanPk);
834
+
835
+		return array_merge($localFilters, $remoteFilters);
836
+	}
837
+
838
+	/**
839
+	 * Returns the "values" of the primary key.
840
+	 * This returns the primary key from the $primaryKey attribute, not the one stored in the columns.
841
+	 *
842
+	 * @param AbstractTDBMObject $bean
843
+	 *
844
+	 * @return array numerically indexed array of values
845
+	 */
846
+	private function getPrimaryKeyValues(AbstractTDBMObject $bean)
847
+	{
848
+		$dbRows = $bean->_getDbRows();
849
+		$dbRow = reset($dbRows);
850
+
851
+		return array_values($dbRow->_getPrimaryKeys());
852
+	}
853
+
854
+	/**
855
+	 * Returns a unique hash used to store the object based on its primary key.
856
+	 * If the array contains only one value, then the value is returned.
857
+	 * Otherwise, a hash representing the array is returned.
858
+	 *
859
+	 * @param array $primaryKeys An array of columns => values forming the primary key
860
+	 *
861
+	 * @return string
862
+	 */
863
+	public function getObjectHash(array $primaryKeys)
864
+	{
865
+		if (count($primaryKeys) === 1) {
866
+			return reset($primaryKeys);
867
+		} else {
868
+			ksort($primaryKeys);
869
+
870
+			return md5(json_encode($primaryKeys));
871
+		}
872
+	}
873
+
874
+	/**
875
+	 * Returns an array of primary keys from the object.
876
+	 * The primary keys are extracted from the object columns and not from the primary keys stored in the
877
+	 * $primaryKeys variable of the object.
878
+	 *
879
+	 * @param DbRow $dbRow
880
+	 *
881
+	 * @return array Returns an array of column => value
882
+	 */
883
+	public function getPrimaryKeysForObjectFromDbRow(DbRow $dbRow)
884
+	{
885
+		$table = $dbRow->_getDbTableName();
886
+		$dbRowData = $dbRow->_getDbRow();
887
+
888
+		return $this->_getPrimaryKeysFromObjectData($table, $dbRowData);
889
+	}
890
+
891
+	/**
892
+	 * Returns an array of primary keys for the given row.
893
+	 * The primary keys are extracted from the object columns.
894
+	 *
895
+	 * @param $table
896
+	 * @param array $columns
897
+	 *
898
+	 * @return array
899
+	 */
900
+	public function _getPrimaryKeysFromObjectData($table, array $columns)
901
+	{
902
+		$primaryKeyColumns = $this->getPrimaryKeyColumns($table);
903
+		$values = array();
904
+		foreach ($primaryKeyColumns as $column) {
905
+			if (isset($columns[$column])) {
906
+				$values[$column] = $columns[$column];
907
+			}
908
+		}
909
+
910
+		return $values;
911
+	}
912
+
913
+	/**
914
+	 * Attaches $object to this TDBMService.
915
+	 * The $object must be in DETACHED state and will pass in NEW state.
916
+	 *
917
+	 * @param AbstractTDBMObject $object
918
+	 *
919
+	 * @throws TDBMInvalidOperationException
920
+	 */
921
+	public function attach(AbstractTDBMObject $object)
922
+	{
923
+		$object->_attach($this);
924
+	}
925
+
926
+	/**
927
+	 * Returns an associative array (column => value) for the primary keys from the table name and an
928
+	 * indexed array of primary key values.
929
+	 *
930
+	 * @param string $tableName
931
+	 * @param array  $indexedPrimaryKeys
932
+	 */
933
+	public function _getPrimaryKeysFromIndexedPrimaryKeys($tableName, array $indexedPrimaryKeys)
934
+	{
935
+		$primaryKeyColumns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getPrimaryKeyColumns();
936
+
937
+		if (count($primaryKeyColumns) !== count($indexedPrimaryKeys)) {
938
+			throw new TDBMException(sprintf('Wrong number of columns passed for primary key. Expected %s columns for table "%s",
939 939
 			got %s instead.', count($primaryKeyColumns), $tableName, count($indexedPrimaryKeys)));
940
-        }
941
-
942
-        return array_combine($primaryKeyColumns, $indexedPrimaryKeys);
943
-    }
944
-
945
-    /**
946
-     * Return the list of tables (from child to parent) joining the tables passed in parameter.
947
-     * Tables must be in a single line of inheritance. The method will find missing tables.
948
-     *
949
-     * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
950
-     * we must be able to find all other tables.
951
-     *
952
-     * @param string[] $tables
953
-     *
954
-     * @return string[]
955
-     */
956
-    public function _getLinkBetweenInheritedTables(array $tables)
957
-    {
958
-        sort($tables);
959
-
960
-        return $this->fromCache($this->cachePrefix.'_linkbetweeninheritedtables_'.implode('__split__', $tables),
961
-            function () use ($tables) {
962
-                return $this->_getLinkBetweenInheritedTablesWithoutCache($tables);
963
-            });
964
-    }
965
-
966
-    /**
967
-     * Return the list of tables (from child to parent) joining the tables passed in parameter.
968
-     * Tables must be in a single line of inheritance. The method will find missing tables.
969
-     *
970
-     * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
971
-     * we must be able to find all other tables.
972
-     *
973
-     * @param string[] $tables
974
-     *
975
-     * @return string[]
976
-     */
977
-    private function _getLinkBetweenInheritedTablesWithoutCache(array $tables)
978
-    {
979
-        $schemaAnalyzer = $this->schemaAnalyzer;
980
-
981
-        foreach ($tables as $currentTable) {
982
-            $allParents = [$currentTable];
983
-            while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
984
-                $currentTable = $currentFk->getForeignTableName();
985
-                $allParents[] = $currentTable;
986
-            }
987
-
988
-            // Now, does the $allParents contain all the tables we want?
989
-            $notFoundTables = array_diff($tables, $allParents);
990
-            if (empty($notFoundTables)) {
991
-                // We have a winner!
992
-                return $allParents;
993
-            }
994
-        }
995
-
996
-        throw TDBMInheritanceException::create($tables);
997
-    }
998
-
999
-    /**
1000
-     * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1001
-     *
1002
-     * @param string $table
1003
-     *
1004
-     * @return string[]
1005
-     */
1006
-    public function _getRelatedTablesByInheritance($table)
1007
-    {
1008
-        return $this->fromCache($this->cachePrefix.'_relatedtables_'.$table, function () use ($table) {
1009
-            return $this->_getRelatedTablesByInheritanceWithoutCache($table);
1010
-        });
1011
-    }
1012
-
1013
-    /**
1014
-     * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1015
-     *
1016
-     * @param string $table
1017
-     *
1018
-     * @return string[]
1019
-     */
1020
-    private function _getRelatedTablesByInheritanceWithoutCache($table)
1021
-    {
1022
-        $schemaAnalyzer = $this->schemaAnalyzer;
1023
-
1024
-        // Let's scan the parent tables
1025
-        $currentTable = $table;
1026
-
1027
-        $parentTables = [];
1028
-
1029
-        // Get parent relationship
1030
-        while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
1031
-            $currentTable = $currentFk->getForeignTableName();
1032
-            $parentTables[] = $currentTable;
1033
-        }
1034
-
1035
-        // Let's recurse in children
1036
-        $childrenTables = $this->exploreChildrenTablesRelationships($schemaAnalyzer, $table);
1037
-
1038
-        return array_merge(array_reverse($parentTables), $childrenTables);
1039
-    }
1040
-
1041
-    /**
1042
-     * Explore all the children and descendant of $table and returns ForeignKeyConstraints on those.
1043
-     *
1044
-     * @param string $table
1045
-     *
1046
-     * @return string[]
1047
-     */
1048
-    private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyzer, $table)
1049
-    {
1050
-        $tables = [$table];
1051
-        $keys = $schemaAnalyzer->getChildrenRelationships($table);
1052
-
1053
-        foreach ($keys as $key) {
1054
-            $tables = array_merge($tables, $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName()));
1055
-        }
1056
-
1057
-        return $tables;
1058
-    }
1059
-
1060
-    /**
1061
-     * Casts a foreign key into SQL, assuming table name is used with no alias.
1062
-     * The returned value does contain only one table. For instance:.
1063
-     *
1064
-     * " LEFT JOIN table2 ON table1.id = table2.table1_id"
1065
-     *
1066
-     * @param ForeignKeyConstraint $fk
1067
-     * @param bool                 $leftTableIsLocal
1068
-     *
1069
-     * @return string
1070
-     */
1071
-    /*private function foreignKeyToSql(ForeignKeyConstraint $fk, $leftTableIsLocal) {
940
+		}
941
+
942
+		return array_combine($primaryKeyColumns, $indexedPrimaryKeys);
943
+	}
944
+
945
+	/**
946
+	 * Return the list of tables (from child to parent) joining the tables passed in parameter.
947
+	 * Tables must be in a single line of inheritance. The method will find missing tables.
948
+	 *
949
+	 * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
950
+	 * we must be able to find all other tables.
951
+	 *
952
+	 * @param string[] $tables
953
+	 *
954
+	 * @return string[]
955
+	 */
956
+	public function _getLinkBetweenInheritedTables(array $tables)
957
+	{
958
+		sort($tables);
959
+
960
+		return $this->fromCache($this->cachePrefix.'_linkbetweeninheritedtables_'.implode('__split__', $tables),
961
+			function () use ($tables) {
962
+				return $this->_getLinkBetweenInheritedTablesWithoutCache($tables);
963
+			});
964
+	}
965
+
966
+	/**
967
+	 * Return the list of tables (from child to parent) joining the tables passed in parameter.
968
+	 * Tables must be in a single line of inheritance. The method will find missing tables.
969
+	 *
970
+	 * Algorithm: one of those tables is the ultimate child. From this child, by recursively getting the parent,
971
+	 * we must be able to find all other tables.
972
+	 *
973
+	 * @param string[] $tables
974
+	 *
975
+	 * @return string[]
976
+	 */
977
+	private function _getLinkBetweenInheritedTablesWithoutCache(array $tables)
978
+	{
979
+		$schemaAnalyzer = $this->schemaAnalyzer;
980
+
981
+		foreach ($tables as $currentTable) {
982
+			$allParents = [$currentTable];
983
+			while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
984
+				$currentTable = $currentFk->getForeignTableName();
985
+				$allParents[] = $currentTable;
986
+			}
987
+
988
+			// Now, does the $allParents contain all the tables we want?
989
+			$notFoundTables = array_diff($tables, $allParents);
990
+			if (empty($notFoundTables)) {
991
+				// We have a winner!
992
+				return $allParents;
993
+			}
994
+		}
995
+
996
+		throw TDBMInheritanceException::create($tables);
997
+	}
998
+
999
+	/**
1000
+	 * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1001
+	 *
1002
+	 * @param string $table
1003
+	 *
1004
+	 * @return string[]
1005
+	 */
1006
+	public function _getRelatedTablesByInheritance($table)
1007
+	{
1008
+		return $this->fromCache($this->cachePrefix.'_relatedtables_'.$table, function () use ($table) {
1009
+			return $this->_getRelatedTablesByInheritanceWithoutCache($table);
1010
+		});
1011
+	}
1012
+
1013
+	/**
1014
+	 * Returns the list of tables related to this table (via a parent or child inheritance relationship).
1015
+	 *
1016
+	 * @param string $table
1017
+	 *
1018
+	 * @return string[]
1019
+	 */
1020
+	private function _getRelatedTablesByInheritanceWithoutCache($table)
1021
+	{
1022
+		$schemaAnalyzer = $this->schemaAnalyzer;
1023
+
1024
+		// Let's scan the parent tables
1025
+		$currentTable = $table;
1026
+
1027
+		$parentTables = [];
1028
+
1029
+		// Get parent relationship
1030
+		while ($currentFk = $schemaAnalyzer->getParentRelationship($currentTable)) {
1031
+			$currentTable = $currentFk->getForeignTableName();
1032
+			$parentTables[] = $currentTable;
1033
+		}
1034
+
1035
+		// Let's recurse in children
1036
+		$childrenTables = $this->exploreChildrenTablesRelationships($schemaAnalyzer, $table);
1037
+
1038
+		return array_merge(array_reverse($parentTables), $childrenTables);
1039
+	}
1040
+
1041
+	/**
1042
+	 * Explore all the children and descendant of $table and returns ForeignKeyConstraints on those.
1043
+	 *
1044
+	 * @param string $table
1045
+	 *
1046
+	 * @return string[]
1047
+	 */
1048
+	private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyzer, $table)
1049
+	{
1050
+		$tables = [$table];
1051
+		$keys = $schemaAnalyzer->getChildrenRelationships($table);
1052
+
1053
+		foreach ($keys as $key) {
1054
+			$tables = array_merge($tables, $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName()));
1055
+		}
1056
+
1057
+		return $tables;
1058
+	}
1059
+
1060
+	/**
1061
+	 * Casts a foreign key into SQL, assuming table name is used with no alias.
1062
+	 * The returned value does contain only one table. For instance:.
1063
+	 *
1064
+	 * " LEFT JOIN table2 ON table1.id = table2.table1_id"
1065
+	 *
1066
+	 * @param ForeignKeyConstraint $fk
1067
+	 * @param bool                 $leftTableIsLocal
1068
+	 *
1069
+	 * @return string
1070
+	 */
1071
+	/*private function foreignKeyToSql(ForeignKeyConstraint $fk, $leftTableIsLocal) {
1072 1072
         $onClauses = [];
1073 1073
         $foreignTableName = $this->connection->quoteIdentifier($fk->getForeignTableName());
1074 1074
         $foreignColumns = $fk->getForeignColumns();
@@ -1094,417 +1094,417 @@  discard block
 block discarded – undo
1094 1094
         }
1095 1095
     }*/
1096 1096
 
1097
-    /**
1098
-     * Returns a `ResultIterator` object representing filtered records of "$mainTable" .
1099
-     *
1100
-     * The findObjects method should be the most used query method in TDBM if you want to query the database for objects.
1101
-     * (Note: if you want to query the database for an object by its primary key, use the findObjectByPk method).
1102
-     *
1103
-     * The findObjects method takes in parameter:
1104
-     * 	- mainTable: the kind of bean you want to retrieve. In TDBM, a bean matches a database row, so the
1105
-     * 			`$mainTable` parameter should be the name of an existing table in database.
1106
-     *  - filter: The filter is a filter bag. It is what you use to filter your request (the WHERE part in SQL).
1107
-     *          It can be a string (SQL Where clause), or even a bean or an associative array (key = column to filter, value = value to find)
1108
-     *  - parameters: The parameters used in the filter. If you pass a SQL string as a filter, be sure to avoid
1109
-     *          concatenating parameters in the string (this leads to SQL injection and also to poor caching performance).
1110
-     *          Instead, please consider passing parameters (see documentation for more details).
1111
-     *  - additionalTablesFetch: An array of SQL tables names. The beans related to those tables will be fetched along
1112
-     *          the main table. This is useful to avoid hitting the database with numerous subqueries.
1113
-     *  - mode: The fetch mode of the result. See `setFetchMode()` method for more details.
1114
-     *
1115
-     * The `findObjects` method will return a `ResultIterator`. A `ResultIterator` is an object that behaves as an array
1116
-     * (in ARRAY mode) at least. It can be iterated using a `foreach` loop.
1117
-     *
1118
-     * Finally, if filter_bag is null, the whole table is returned.
1119
-     *
1120
-     * @param string                       $mainTable             The name of the table queried
1121
-     * @param string|array|null            $filter                The SQL filters to apply to the query (the WHERE part). Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1122
-     * @param array                        $parameters
1123
-     * @param string|UncheckedOrderBy|null $orderString           The ORDER BY part of the query. Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1124
-     * @param array                        $additionalTablesFetch
1125
-     * @param int                          $mode
1126
-     * @param string                       $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1127
-     *
1128
-     * @return ResultIterator An object representing an array of results
1129
-     *
1130
-     * @throws TDBMException
1131
-     */
1132
-    public function findObjects(string $mainTable, $filter = null, array $parameters = array(), $orderString = null, array $additionalTablesFetch = array(), $mode = null, string $className = null)
1133
-    {
1134
-        // $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1135
-        if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1136
-            throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1137
-        }
1138
-
1139
-        $mode = $mode ?: $this->mode;
1140
-
1141
-        list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1142
-
1143
-        $parameters = array_merge($parameters, $additionalParameters);
1144
-
1145
-        $queryFactory = new FindObjectsQueryFactory($mainTable, $additionalTablesFetch, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer);
1146
-
1147
-        return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1148
-    }
1149
-
1150
-    /**
1151
-     * @param string                       $mainTable   The name of the table queried
1152
-     * @param string                       $from        The from sql statement
1153
-     * @param string|array|null            $filter      The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1154
-     * @param array                        $parameters
1155
-     * @param string|UncheckedOrderBy|null $orderString The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column)
1156
-     * @param int                          $mode
1157
-     * @param string                       $className   Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1158
-     *
1159
-     * @return ResultIterator An object representing an array of results
1160
-     *
1161
-     * @throws TDBMException
1162
-     */
1163
-    public function findObjectsFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), $orderString = null, $mode = null, string $className = null)
1164
-    {
1165
-        // $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1166
-        if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1167
-            throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1168
-        }
1169
-
1170
-        $mode = $mode ?: $this->mode;
1171
-
1172
-        list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1173
-
1174
-        $parameters = array_merge($parameters, $additionalParameters);
1175
-
1176
-        $queryFactory = new FindObjectsFromSqlQueryFactory($mainTable, $from, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer, $this->schemaAnalyzer, $this->cache, $this->cachePrefix);
1177
-
1178
-        return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1179
-    }
1180
-
1181
-    /**
1182
-     * @param $table
1183
-     * @param array  $primaryKeys
1184
-     * @param array  $additionalTablesFetch
1185
-     * @param bool   $lazy                  Whether to perform lazy loading on this object or not
1186
-     * @param string $className
1187
-     *
1188
-     * @return AbstractTDBMObject
1189
-     *
1190
-     * @throws TDBMException
1191
-     */
1192
-    public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch = array(), bool $lazy = false, string $className = null)
1193
-    {
1194
-        $primaryKeys = $this->_getPrimaryKeysFromObjectData($table, $primaryKeys);
1195
-        $hash = $this->getObjectHash($primaryKeys);
1196
-
1197
-        if ($this->objectStorage->has($table, $hash)) {
1198
-            $dbRow = $this->objectStorage->get($table, $hash);
1199
-            $bean = $dbRow->getTDBMObject();
1200
-            if ($className !== null && !is_a($bean, $className)) {
1201
-                throw new TDBMException("TDBM cannot create a bean of class '".$className."'. The requested object was already loaded and its class is '".get_class($bean)."'");
1202
-            }
1203
-
1204
-            return $bean;
1205
-        }
1206
-
1207
-        // Are we performing lazy fetching?
1208
-        if ($lazy === true) {
1209
-            // Can we perform lazy fetching?
1210
-            $tables = $this->_getRelatedTablesByInheritance($table);
1211
-            // Only allowed if no inheritance.
1212
-            if (count($tables) === 1) {
1213
-                if ($className === null) {
1214
-                    try {
1215
-                        $className = $this->getBeanClassName($table);
1216
-                    } catch (TDBMInvalidArgumentException $e) {
1217
-                        $className = TDBMObject::class;
1218
-                    }
1219
-                }
1220
-
1221
-                // Let's construct the bean
1222
-                if (!isset($this->reflectionClassCache[$className])) {
1223
-                    $this->reflectionClassCache[$className] = new \ReflectionClass($className);
1224
-                }
1225
-                // Let's bypass the constructor when creating the bean!
1226
-                $bean = $this->reflectionClassCache[$className]->newInstanceWithoutConstructor();
1227
-                /* @var $bean AbstractTDBMObject */
1228
-                $bean->_constructLazy($table, $primaryKeys, $this);
1229
-
1230
-                return $bean;
1231
-            }
1232
-        }
1233
-
1234
-        // Did not find the object in cache? Let's query it!
1235
-        return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className);
1236
-    }
1237
-
1238
-    /**
1239
-     * Returns a unique bean (or null) according to the filters passed in parameter.
1240
-     *
1241
-     * @param string            $mainTable             The name of the table queried
1242
-     * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1243
-     * @param array             $parameters
1244
-     * @param array             $additionalTablesFetch
1245
-     * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1246
-     *
1247
-     * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1248
-     *
1249
-     * @throws TDBMException
1250
-     */
1251
-    public function findObject(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1252
-    {
1253
-        $objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className);
1254
-        $page = $objects->take(0, 2);
1255
-        $count = $page->count();
1256
-        if ($count > 1) {
1257
-            throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1258
-        } elseif ($count === 0) {
1259
-            return;
1260
-        }
1261
-
1262
-        return $page[0];
1263
-    }
1264
-
1265
-    /**
1266
-     * Returns a unique bean (or null) according to the filters passed in parameter.
1267
-     *
1268
-     * @param string            $mainTable  The name of the table queried
1269
-     * @param string            $from       The from sql statement
1270
-     * @param string|array|null $filter     The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1271
-     * @param array             $parameters
1272
-     * @param string            $className  Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1273
-     *
1274
-     * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1275
-     *
1276
-     * @throws TDBMException
1277
-     */
1278
-    public function findObjectFromSql($mainTable, $from, $filter = null, array $parameters = array(), $className = null)
1279
-    {
1280
-        $objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className);
1281
-        $page = $objects->take(0, 2);
1282
-        $count = $page->count();
1283
-        if ($count > 1) {
1284
-            throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1285
-        } elseif ($count === 0) {
1286
-            return;
1287
-        }
1288
-
1289
-        return $page[0];
1290
-    }
1291
-
1292
-    /**
1293
-     * Returns a unique bean according to the filters passed in parameter.
1294
-     * Throws a NoBeanFoundException if no bean was found for the filter passed in parameter.
1295
-     *
1296
-     * @param string            $mainTable             The name of the table queried
1297
-     * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1298
-     * @param array             $parameters
1299
-     * @param array             $additionalTablesFetch
1300
-     * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1301
-     *
1302
-     * @return AbstractTDBMObject The object we want
1303
-     *
1304
-     * @throws TDBMException
1305
-     */
1306
-    public function findObjectOrFail(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1307
-    {
1308
-        $bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className);
1309
-        if ($bean === null) {
1310
-            throw new NoBeanFoundException("No result found for query on table '".$mainTable."'");
1311
-        }
1312
-
1313
-        return $bean;
1314
-    }
1315
-
1316
-    /**
1317
-     * @param array $beanData An array of data: array<table, array<column, value>>
1318
-     *
1319
-     * @return array an array with first item = class name, second item = table name and third item = list of tables needed
1320
-     *
1321
-     * @throws TDBMInheritanceException
1322
-     */
1323
-    public function _getClassNameFromBeanData(array $beanData)
1324
-    {
1325
-        if (count($beanData) === 1) {
1326
-            $tableName = array_keys($beanData)[0];
1327
-            $allTables = [$tableName];
1328
-        } else {
1329
-            $tables = [];
1330
-            foreach ($beanData as $table => $row) {
1331
-                $primaryKeyColumns = $this->getPrimaryKeyColumns($table);
1332
-                $pkSet = false;
1333
-                foreach ($primaryKeyColumns as $columnName) {
1334
-                    if ($row[$columnName] !== null) {
1335
-                        $pkSet = true;
1336
-                        break;
1337
-                    }
1338
-                }
1339
-                if ($pkSet) {
1340
-                    $tables[] = $table;
1341
-                }
1342
-            }
1343
-
1344
-            // $tables contains the tables for this bean. Let's view the top most part of the hierarchy
1345
-            try {
1346
-                $allTables = $this->_getLinkBetweenInheritedTables($tables);
1347
-            } catch (TDBMInheritanceException $e) {
1348
-                throw TDBMInheritanceException::extendException($e, $this, $beanData);
1349
-            }
1350
-            $tableName = $allTables[0];
1351
-        }
1352
-
1353
-        // Only one table in this bean. Life is sweat, let's look at its type:
1354
-        try {
1355
-            $className = $this->getBeanClassName($tableName);
1356
-        } catch (TDBMInvalidArgumentException $e) {
1357
-            $className = 'Mouf\\Database\\TDBM\\TDBMObject';
1358
-        }
1359
-
1360
-        return [$className, $tableName, $allTables];
1361
-    }
1362
-
1363
-    /**
1364
-     * Returns an item from cache or computes it using $closure and puts it in cache.
1365
-     *
1366
-     * @param string   $key
1367
-     * @param callable $closure
1368
-     *
1369
-     * @return mixed
1370
-     */
1371
-    private function fromCache(string $key, callable $closure)
1372
-    {
1373
-        $item = $this->cache->fetch($key);
1374
-        if ($item === false) {
1375
-            $item = $closure();
1376
-            $this->cache->save($key, $item);
1377
-        }
1378
-
1379
-        return $item;
1380
-    }
1381
-
1382
-    /**
1383
-     * Returns the foreign key object.
1384
-     *
1385
-     * @param string $table
1386
-     * @param string $fkName
1387
-     *
1388
-     * @return ForeignKeyConstraint
1389
-     */
1390
-    public function _getForeignKeyByName(string $table, string $fkName)
1391
-    {
1392
-        return $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getForeignKey($fkName);
1393
-    }
1394
-
1395
-    /**
1396
-     * @param $pivotTableName
1397
-     * @param AbstractTDBMObject $bean
1398
-     *
1399
-     * @return AbstractTDBMObject[]
1400
-     */
1401
-    public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean)
1402
-    {
1403
-        list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $bean);
1404
-        /* @var $localFk ForeignKeyConstraint */
1405
-        /* @var $remoteFk ForeignKeyConstraint */
1406
-        $remoteTable = $remoteFk->getForeignTableName();
1407
-
1408
-        $primaryKeys = $this->getPrimaryKeyValues($bean);
1409
-        $columnNames = array_map(function ($name) use ($pivotTableName) {
1410
-            return $pivotTableName.'.'.$name;
1411
-        }, $localFk->getLocalColumns());
1412
-
1413
-        $filter = array_combine($columnNames, $primaryKeys);
1414
-
1415
-        return $this->findObjects($remoteTable, $filter);
1416
-    }
1417
-
1418
-    /**
1419
-     * @param $pivotTableName
1420
-     * @param AbstractTDBMObject $bean The LOCAL bean
1421
-     *
1422
-     * @return ForeignKeyConstraint[] First item: the LOCAL bean, second item: the REMOTE bean
1423
-     *
1424
-     * @throws TDBMException
1425
-     */
1426
-    private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMObject $bean)
1427
-    {
1428
-        $fks = array_values($this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName)->getForeignKeys());
1429
-        $table1 = $fks[0]->getForeignTableName();
1430
-        $table2 = $fks[1]->getForeignTableName();
1431
-
1432
-        $beanTables = array_map(function (DbRow $dbRow) {
1433
-            return $dbRow->_getDbTableName();
1434
-        }, $bean->_getDbRows());
1435
-
1436
-        if (in_array($table1, $beanTables)) {
1437
-            return [$fks[0], $fks[1]];
1438
-        } elseif (in_array($table2, $beanTables)) {
1439
-            return [$fks[1], $fks[0]];
1440
-        } else {
1441
-            throw new TDBMException("Unexpected bean type in getPivotTableForeignKeys. Awaiting beans from table {$table1} and {$table2} for pivot table {$pivotTableName}");
1442
-        }
1443
-    }
1444
-
1445
-    /**
1446
-     * Returns a list of pivot tables linked to $bean.
1447
-     *
1448
-     * @param AbstractTDBMObject $bean
1449
-     *
1450
-     * @return string[]
1451
-     */
1452
-    public function _getPivotTablesLinkedToBean(AbstractTDBMObject $bean)
1453
-    {
1454
-        $junctionTables = [];
1455
-        $allJunctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
1456
-        foreach ($bean->_getDbRows() as $dbRow) {
1457
-            foreach ($allJunctionTables as $table) {
1458
-                // There are exactly 2 FKs since this is a pivot table.
1459
-                $fks = array_values($table->getForeignKeys());
1460
-
1461
-                if ($fks[0]->getForeignTableName() === $dbRow->_getDbTableName() || $fks[1]->getForeignTableName() === $dbRow->_getDbTableName()) {
1462
-                    $junctionTables[] = $table->getName();
1463
-                }
1464
-            }
1465
-        }
1466
-
1467
-        return $junctionTables;
1468
-    }
1469
-
1470
-    /**
1471
-     * Array of types for tables.
1472
-     * Key: table name
1473
-     * Value: array of types indexed by column.
1474
-     *
1475
-     * @var array[]
1476
-     */
1477
-    private $typesForTable = [];
1478
-
1479
-    /**
1480
-     * @internal
1481
-     *
1482
-     * @param string $tableName
1483
-     *
1484
-     * @return Type[]
1485
-     */
1486
-    public function _getColumnTypesForTable(string $tableName)
1487
-    {
1488
-        if (!isset($typesForTable[$tableName])) {
1489
-            $columns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getColumns();
1490
-            $typesForTable[$tableName] = array_map(function (Column $column) {
1491
-                return $column->getType();
1492
-            }, $columns);
1493
-        }
1494
-
1495
-        return $typesForTable[$tableName];
1496
-    }
1497
-
1498
-    /**
1499
-     * Sets the minimum log level.
1500
-     * $level must be one of Psr\Log\LogLevel::xxx.
1501
-     *
1502
-     * Defaults to LogLevel::WARNING
1503
-     *
1504
-     * @param string $level
1505
-     */
1506
-    public function setLogLevel(string $level)
1507
-    {
1508
-        $this->logger = new LevelFilter($this->rootLogger, $level);
1509
-    }
1097
+	/**
1098
+	 * Returns a `ResultIterator` object representing filtered records of "$mainTable" .
1099
+	 *
1100
+	 * The findObjects method should be the most used query method in TDBM if you want to query the database for objects.
1101
+	 * (Note: if you want to query the database for an object by its primary key, use the findObjectByPk method).
1102
+	 *
1103
+	 * The findObjects method takes in parameter:
1104
+	 * 	- mainTable: the kind of bean you want to retrieve. In TDBM, a bean matches a database row, so the
1105
+	 * 			`$mainTable` parameter should be the name of an existing table in database.
1106
+	 *  - filter: The filter is a filter bag. It is what you use to filter your request (the WHERE part in SQL).
1107
+	 *          It can be a string (SQL Where clause), or even a bean or an associative array (key = column to filter, value = value to find)
1108
+	 *  - parameters: The parameters used in the filter. If you pass a SQL string as a filter, be sure to avoid
1109
+	 *          concatenating parameters in the string (this leads to SQL injection and also to poor caching performance).
1110
+	 *          Instead, please consider passing parameters (see documentation for more details).
1111
+	 *  - additionalTablesFetch: An array of SQL tables names. The beans related to those tables will be fetched along
1112
+	 *          the main table. This is useful to avoid hitting the database with numerous subqueries.
1113
+	 *  - mode: The fetch mode of the result. See `setFetchMode()` method for more details.
1114
+	 *
1115
+	 * The `findObjects` method will return a `ResultIterator`. A `ResultIterator` is an object that behaves as an array
1116
+	 * (in ARRAY mode) at least. It can be iterated using a `foreach` loop.
1117
+	 *
1118
+	 * Finally, if filter_bag is null, the whole table is returned.
1119
+	 *
1120
+	 * @param string                       $mainTable             The name of the table queried
1121
+	 * @param string|array|null            $filter                The SQL filters to apply to the query (the WHERE part). Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1122
+	 * @param array                        $parameters
1123
+	 * @param string|UncheckedOrderBy|null $orderString           The ORDER BY part of the query. Columns from tables different from $mainTable must be prefixed by the table name (in the form: table.column)
1124
+	 * @param array                        $additionalTablesFetch
1125
+	 * @param int                          $mode
1126
+	 * @param string                       $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1127
+	 *
1128
+	 * @return ResultIterator An object representing an array of results
1129
+	 *
1130
+	 * @throws TDBMException
1131
+	 */
1132
+	public function findObjects(string $mainTable, $filter = null, array $parameters = array(), $orderString = null, array $additionalTablesFetch = array(), $mode = null, string $className = null)
1133
+	{
1134
+		// $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1135
+		if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1136
+			throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1137
+		}
1138
+
1139
+		$mode = $mode ?: $this->mode;
1140
+
1141
+		list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1142
+
1143
+		$parameters = array_merge($parameters, $additionalParameters);
1144
+
1145
+		$queryFactory = new FindObjectsQueryFactory($mainTable, $additionalTablesFetch, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer);
1146
+
1147
+		return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1148
+	}
1149
+
1150
+	/**
1151
+	 * @param string                       $mainTable   The name of the table queried
1152
+	 * @param string                       $from        The from sql statement
1153
+	 * @param string|array|null            $filter      The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1154
+	 * @param array                        $parameters
1155
+	 * @param string|UncheckedOrderBy|null $orderString The ORDER BY part of the query. All columns must be prefixed by the table name (in the form: table.column)
1156
+	 * @param int                          $mode
1157
+	 * @param string                       $className   Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1158
+	 *
1159
+	 * @return ResultIterator An object representing an array of results
1160
+	 *
1161
+	 * @throws TDBMException
1162
+	 */
1163
+	public function findObjectsFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), $orderString = null, $mode = null, string $className = null)
1164
+	{
1165
+		// $mainTable is not secured in MagicJoin, let's add a bit of security to avoid SQL injection.
1166
+		if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $mainTable)) {
1167
+			throw new TDBMException(sprintf("Invalid table name: '%s'", $mainTable));
1168
+		}
1169
+
1170
+		$mode = $mode ?: $this->mode;
1171
+
1172
+		list($filterString, $additionalParameters) = $this->buildFilterFromFilterBag($filter);
1173
+
1174
+		$parameters = array_merge($parameters, $additionalParameters);
1175
+
1176
+		$queryFactory = new FindObjectsFromSqlQueryFactory($mainTable, $from, $filterString, $orderString, $this, $this->tdbmSchemaAnalyzer->getSchema(), $this->orderByAnalyzer, $this->schemaAnalyzer, $this->cache, $this->cachePrefix);
1177
+
1178
+		return new ResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
1179
+	}
1180
+
1181
+	/**
1182
+	 * @param $table
1183
+	 * @param array  $primaryKeys
1184
+	 * @param array  $additionalTablesFetch
1185
+	 * @param bool   $lazy                  Whether to perform lazy loading on this object or not
1186
+	 * @param string $className
1187
+	 *
1188
+	 * @return AbstractTDBMObject
1189
+	 *
1190
+	 * @throws TDBMException
1191
+	 */
1192
+	public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch = array(), bool $lazy = false, string $className = null)
1193
+	{
1194
+		$primaryKeys = $this->_getPrimaryKeysFromObjectData($table, $primaryKeys);
1195
+		$hash = $this->getObjectHash($primaryKeys);
1196
+
1197
+		if ($this->objectStorage->has($table, $hash)) {
1198
+			$dbRow = $this->objectStorage->get($table, $hash);
1199
+			$bean = $dbRow->getTDBMObject();
1200
+			if ($className !== null && !is_a($bean, $className)) {
1201
+				throw new TDBMException("TDBM cannot create a bean of class '".$className."'. The requested object was already loaded and its class is '".get_class($bean)."'");
1202
+			}
1203
+
1204
+			return $bean;
1205
+		}
1206
+
1207
+		// Are we performing lazy fetching?
1208
+		if ($lazy === true) {
1209
+			// Can we perform lazy fetching?
1210
+			$tables = $this->_getRelatedTablesByInheritance($table);
1211
+			// Only allowed if no inheritance.
1212
+			if (count($tables) === 1) {
1213
+				if ($className === null) {
1214
+					try {
1215
+						$className = $this->getBeanClassName($table);
1216
+					} catch (TDBMInvalidArgumentException $e) {
1217
+						$className = TDBMObject::class;
1218
+					}
1219
+				}
1220
+
1221
+				// Let's construct the bean
1222
+				if (!isset($this->reflectionClassCache[$className])) {
1223
+					$this->reflectionClassCache[$className] = new \ReflectionClass($className);
1224
+				}
1225
+				// Let's bypass the constructor when creating the bean!
1226
+				$bean = $this->reflectionClassCache[$className]->newInstanceWithoutConstructor();
1227
+				/* @var $bean AbstractTDBMObject */
1228
+				$bean->_constructLazy($table, $primaryKeys, $this);
1229
+
1230
+				return $bean;
1231
+			}
1232
+		}
1233
+
1234
+		// Did not find the object in cache? Let's query it!
1235
+		return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className);
1236
+	}
1237
+
1238
+	/**
1239
+	 * Returns a unique bean (or null) according to the filters passed in parameter.
1240
+	 *
1241
+	 * @param string            $mainTable             The name of the table queried
1242
+	 * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1243
+	 * @param array             $parameters
1244
+	 * @param array             $additionalTablesFetch
1245
+	 * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1246
+	 *
1247
+	 * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1248
+	 *
1249
+	 * @throws TDBMException
1250
+	 */
1251
+	public function findObject(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1252
+	{
1253
+		$objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className);
1254
+		$page = $objects->take(0, 2);
1255
+		$count = $page->count();
1256
+		if ($count > 1) {
1257
+			throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1258
+		} elseif ($count === 0) {
1259
+			return;
1260
+		}
1261
+
1262
+		return $page[0];
1263
+	}
1264
+
1265
+	/**
1266
+	 * Returns a unique bean (or null) according to the filters passed in parameter.
1267
+	 *
1268
+	 * @param string            $mainTable  The name of the table queried
1269
+	 * @param string            $from       The from sql statement
1270
+	 * @param string|array|null $filter     The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1271
+	 * @param array             $parameters
1272
+	 * @param string            $className  Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1273
+	 *
1274
+	 * @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
1275
+	 *
1276
+	 * @throws TDBMException
1277
+	 */
1278
+	public function findObjectFromSql($mainTable, $from, $filter = null, array $parameters = array(), $className = null)
1279
+	{
1280
+		$objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className);
1281
+		$page = $objects->take(0, 2);
1282
+		$count = $page->count();
1283
+		if ($count > 1) {
1284
+			throw new DuplicateRowException("Error while querying an object for table '$mainTable': More than 1 row have been returned, but we should have received at most one.");
1285
+		} elseif ($count === 0) {
1286
+			return;
1287
+		}
1288
+
1289
+		return $page[0];
1290
+	}
1291
+
1292
+	/**
1293
+	 * Returns a unique bean according to the filters passed in parameter.
1294
+	 * Throws a NoBeanFoundException if no bean was found for the filter passed in parameter.
1295
+	 *
1296
+	 * @param string            $mainTable             The name of the table queried
1297
+	 * @param string|array|null $filter                The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
1298
+	 * @param array             $parameters
1299
+	 * @param array             $additionalTablesFetch
1300
+	 * @param string            $className             Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
1301
+	 *
1302
+	 * @return AbstractTDBMObject The object we want
1303
+	 *
1304
+	 * @throws TDBMException
1305
+	 */
1306
+	public function findObjectOrFail(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null)
1307
+	{
1308
+		$bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className);
1309
+		if ($bean === null) {
1310
+			throw new NoBeanFoundException("No result found for query on table '".$mainTable."'");
1311
+		}
1312
+
1313
+		return $bean;
1314
+	}
1315
+
1316
+	/**
1317
+	 * @param array $beanData An array of data: array<table, array<column, value>>
1318
+	 *
1319
+	 * @return array an array with first item = class name, second item = table name and third item = list of tables needed
1320
+	 *
1321
+	 * @throws TDBMInheritanceException
1322
+	 */
1323
+	public function _getClassNameFromBeanData(array $beanData)
1324
+	{
1325
+		if (count($beanData) === 1) {
1326
+			$tableName = array_keys($beanData)[0];
1327
+			$allTables = [$tableName];
1328
+		} else {
1329
+			$tables = [];
1330
+			foreach ($beanData as $table => $row) {
1331
+				$primaryKeyColumns = $this->getPrimaryKeyColumns($table);
1332
+				$pkSet = false;
1333
+				foreach ($primaryKeyColumns as $columnName) {
1334
+					if ($row[$columnName] !== null) {
1335
+						$pkSet = true;
1336
+						break;
1337
+					}
1338
+				}
1339
+				if ($pkSet) {
1340
+					$tables[] = $table;
1341
+				}
1342
+			}
1343
+
1344
+			// $tables contains the tables for this bean. Let's view the top most part of the hierarchy
1345
+			try {
1346
+				$allTables = $this->_getLinkBetweenInheritedTables($tables);
1347
+			} catch (TDBMInheritanceException $e) {
1348
+				throw TDBMInheritanceException::extendException($e, $this, $beanData);
1349
+			}
1350
+			$tableName = $allTables[0];
1351
+		}
1352
+
1353
+		// Only one table in this bean. Life is sweat, let's look at its type:
1354
+		try {
1355
+			$className = $this->getBeanClassName($tableName);
1356
+		} catch (TDBMInvalidArgumentException $e) {
1357
+			$className = 'Mouf\\Database\\TDBM\\TDBMObject';
1358
+		}
1359
+
1360
+		return [$className, $tableName, $allTables];
1361
+	}
1362
+
1363
+	/**
1364
+	 * Returns an item from cache or computes it using $closure and puts it in cache.
1365
+	 *
1366
+	 * @param string   $key
1367
+	 * @param callable $closure
1368
+	 *
1369
+	 * @return mixed
1370
+	 */
1371
+	private function fromCache(string $key, callable $closure)
1372
+	{
1373
+		$item = $this->cache->fetch($key);
1374
+		if ($item === false) {
1375
+			$item = $closure();
1376
+			$this->cache->save($key, $item);
1377
+		}
1378
+
1379
+		return $item;
1380
+	}
1381
+
1382
+	/**
1383
+	 * Returns the foreign key object.
1384
+	 *
1385
+	 * @param string $table
1386
+	 * @param string $fkName
1387
+	 *
1388
+	 * @return ForeignKeyConstraint
1389
+	 */
1390
+	public function _getForeignKeyByName(string $table, string $fkName)
1391
+	{
1392
+		return $this->tdbmSchemaAnalyzer->getSchema()->getTable($table)->getForeignKey($fkName);
1393
+	}
1394
+
1395
+	/**
1396
+	 * @param $pivotTableName
1397
+	 * @param AbstractTDBMObject $bean
1398
+	 *
1399
+	 * @return AbstractTDBMObject[]
1400
+	 */
1401
+	public function _getRelatedBeans(string $pivotTableName, AbstractTDBMObject $bean)
1402
+	{
1403
+		list($localFk, $remoteFk) = $this->getPivotTableForeignKeys($pivotTableName, $bean);
1404
+		/* @var $localFk ForeignKeyConstraint */
1405
+		/* @var $remoteFk ForeignKeyConstraint */
1406
+		$remoteTable = $remoteFk->getForeignTableName();
1407
+
1408
+		$primaryKeys = $this->getPrimaryKeyValues($bean);
1409
+		$columnNames = array_map(function ($name) use ($pivotTableName) {
1410
+			return $pivotTableName.'.'.$name;
1411
+		}, $localFk->getLocalColumns());
1412
+
1413
+		$filter = array_combine($columnNames, $primaryKeys);
1414
+
1415
+		return $this->findObjects($remoteTable, $filter);
1416
+	}
1417
+
1418
+	/**
1419
+	 * @param $pivotTableName
1420
+	 * @param AbstractTDBMObject $bean The LOCAL bean
1421
+	 *
1422
+	 * @return ForeignKeyConstraint[] First item: the LOCAL bean, second item: the REMOTE bean
1423
+	 *
1424
+	 * @throws TDBMException
1425
+	 */
1426
+	private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMObject $bean)
1427
+	{
1428
+		$fks = array_values($this->tdbmSchemaAnalyzer->getSchema()->getTable($pivotTableName)->getForeignKeys());
1429
+		$table1 = $fks[0]->getForeignTableName();
1430
+		$table2 = $fks[1]->getForeignTableName();
1431
+
1432
+		$beanTables = array_map(function (DbRow $dbRow) {
1433
+			return $dbRow->_getDbTableName();
1434
+		}, $bean->_getDbRows());
1435
+
1436
+		if (in_array($table1, $beanTables)) {
1437
+			return [$fks[0], $fks[1]];
1438
+		} elseif (in_array($table2, $beanTables)) {
1439
+			return [$fks[1], $fks[0]];
1440
+		} else {
1441
+			throw new TDBMException("Unexpected bean type in getPivotTableForeignKeys. Awaiting beans from table {$table1} and {$table2} for pivot table {$pivotTableName}");
1442
+		}
1443
+	}
1444
+
1445
+	/**
1446
+	 * Returns a list of pivot tables linked to $bean.
1447
+	 *
1448
+	 * @param AbstractTDBMObject $bean
1449
+	 *
1450
+	 * @return string[]
1451
+	 */
1452
+	public function _getPivotTablesLinkedToBean(AbstractTDBMObject $bean)
1453
+	{
1454
+		$junctionTables = [];
1455
+		$allJunctionTables = $this->schemaAnalyzer->detectJunctionTables(true);
1456
+		foreach ($bean->_getDbRows() as $dbRow) {
1457
+			foreach ($allJunctionTables as $table) {
1458
+				// There are exactly 2 FKs since this is a pivot table.
1459
+				$fks = array_values($table->getForeignKeys());
1460
+
1461
+				if ($fks[0]->getForeignTableName() === $dbRow->_getDbTableName() || $fks[1]->getForeignTableName() === $dbRow->_getDbTableName()) {
1462
+					$junctionTables[] = $table->getName();
1463
+				}
1464
+			}
1465
+		}
1466
+
1467
+		return $junctionTables;
1468
+	}
1469
+
1470
+	/**
1471
+	 * Array of types for tables.
1472
+	 * Key: table name
1473
+	 * Value: array of types indexed by column.
1474
+	 *
1475
+	 * @var array[]
1476
+	 */
1477
+	private $typesForTable = [];
1478
+
1479
+	/**
1480
+	 * @internal
1481
+	 *
1482
+	 * @param string $tableName
1483
+	 *
1484
+	 * @return Type[]
1485
+	 */
1486
+	public function _getColumnTypesForTable(string $tableName)
1487
+	{
1488
+		if (!isset($typesForTable[$tableName])) {
1489
+			$columns = $this->tdbmSchemaAnalyzer->getSchema()->getTable($tableName)->getColumns();
1490
+			$typesForTable[$tableName] = array_map(function (Column $column) {
1491
+				return $column->getType();
1492
+			}, $columns);
1493
+		}
1494
+
1495
+		return $typesForTable[$tableName];
1496
+	}
1497
+
1498
+	/**
1499
+	 * Sets the minimum log level.
1500
+	 * $level must be one of Psr\Log\LogLevel::xxx.
1501
+	 *
1502
+	 * Defaults to LogLevel::WARNING
1503
+	 *
1504
+	 * @param string $level
1505
+	 */
1506
+	public function setLogLevel(string $level)
1507
+	{
1508
+		$this->logger = new LevelFilter($this->rootLogger, $level);
1509
+	}
1510 1510
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Controllers/TdbmController.php 1 patch
Indentation   +125 added lines, -125 removed lines patch added patch discarded remove patch
@@ -18,129 +18,129 @@
 block discarded – undo
18 18
  */
19 19
 class TdbmController extends AbstractMoufInstanceController
20 20
 {
21
-    /**
22
-     * @var HtmlBlock
23
-     */
24
-    public $content;
25
-
26
-    protected $daoNamespace;
27
-    protected $beanNamespace;
28
-    protected $daoFactoryInstanceName;
29
-    protected $autoloadDetected;
30
-    ///protected $storeInUtc;
31
-    protected $useCustomComposer;
32
-    protected $composerFile;
33
-
34
-    /**
35
-     * Admin page used to display the DAO generation form.
36
-     *
37
-     * @Action
38
-     */
39
-    public function defaultAction($name, $selfedit = 'false')
40
-    {
41
-        $this->initController($name, $selfedit);
42
-
43
-        // Fill variables
44
-        $this->daoNamespace = self::getFromConfiguration($this->moufManager, $name, 'daoNamespace');
45
-        $this->beanNamespace = self::getFromConfiguration($this->moufManager, $name, 'beanNamespace');
46
-        $this->daoFactoryInstanceName = self::getFromConfiguration($this->moufManager, $name, 'daoFactoryInstanceName');
47
-        //$this->storeInUtc = self::getFromConfiguration($this->moufManager, $name, 'storeInUtc');
48
-        $this->composerFile = self::getFromConfiguration($this->moufManager, $name, 'customComposerFile');
49
-        $this->useCustomComposer = $this->composerFile ? true : false;
50
-
51
-        if ($this->daoNamespace == null && $this->beanNamespace == null) {
52
-            $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json');
53
-
54
-            $autoloadNamespaces = $classNameMapper->getManagedNamespaces();
55
-            if ($autoloadNamespaces) {
56
-                $this->autoloadDetected = true;
57
-                $rootNamespace = $autoloadNamespaces[0];
58
-                $this->daoNamespace = $rootNamespace.'Dao';
59
-                $this->beanNamespace = $rootNamespace.'Dao\\Bean';
60
-            } else {
61
-                $this->autoloadDetected = false;
62
-                $this->daoNamespace = 'YourApplication\\Dao';
63
-                $this->beanNamespace = 'YourApplication\\Dao\\Bean';
64
-            }
65
-        } else {
66
-            $this->autoloadDetected = true;
67
-        }
68
-
69
-        $this->content->addFile(__DIR__.'/../../../../views/tdbmGenerate.php', $this);
70
-        $this->template->toHtml();
71
-    }
72
-
73
-    /**
74
-     * This action generates the DAOs and Beans for the TDBM service passed in parameter.
75
-     *
76
-     * @Action
77
-     *
78
-     * @param string $name
79
-     * @param bool   $selfedit
80
-     */
81
-    public function generate($name, $daonamespace, $beannamespace, $daofactoryinstancename, /*$storeInUtc = 0,*/ $selfedit = 'false', $useCustomComposer = false, $composerFile = '')
82
-    {
83
-        $this->initController($name, $selfedit);
84
-
85
-        self::generateDaos($this->moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit, /*$storeInUtc,*/ $useCustomComposer, $composerFile);
86
-
87
-        // TODO: better: we should redirect to a screen that list the number of DAOs generated, etc...
88
-        header('Location: '.ROOT_URL.'ajaxinstance/?name='.urlencode($name).'&selfedit='.$selfedit);
89
-    }
90
-
91
-    /**
92
-     * This function generates the DAOs and Beans for the TDBM service passed in parameter.
93
-     *
94
-     * @param MoufManager $moufManager
95
-     * @param string      $name
96
-     * @param string      $daonamespace
97
-     * @param string      $beannamespace
98
-     * @param string      $daofactoryclassname
99
-     * @param string      $daofactoryinstancename
100
-     * @param string      $selfedit
101
-     *
102
-     * @throws \Mouf\MoufException
103
-     */
104
-    public static function generateDaos(MoufManager $moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit = 'false', /*$storeInUtc = null,*/ $useCustomComposer = null, $composerFile = null)
105
-    {
106
-        self::setInConfiguration($moufManager, $name, 'daoNamespace', $daonamespace);
107
-        self::setInConfiguration($moufManager, $name, 'beanNamespace', $beannamespace);
108
-        self::setInConfiguration($moufManager, $name, 'daoFactoryInstanceName', $daofactoryinstancename);
109
-        //self::setInConfiguration($moufManager, $name, 'storeInUtc', $storeInUtc);
110
-        if ($useCustomComposer) {
111
-            self::setInConfiguration($moufManager, $name, 'customComposerFile', $composerFile);
112
-        } else {
113
-            self::setInConfiguration($moufManager, $name, 'customComposerFile', null);
114
-        }
115
-        // Let's rewrite before calling the DAO generator
116
-        $moufManager->rewriteMouf();
117
-
118
-
119
-        $tdbmService = new InstanceProxy($name);
120
-        /* @var $tdbmService TDBMService */
121
-        $tdbmService->generateAllDaosAndBeans(($useCustomComposer ? $composerFile : null));
122
-    }
123
-
124
-    private static function getConfigurationDescriptor(MoufManager $moufManager, string $tdbmInstanceName)
125
-    {
126
-        return $moufManager->getInstanceDescriptor($tdbmInstanceName)->getConstructorArgumentProperty('configuration')->getValue();
127
-    }
128
-
129
-    private static function getFromConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property)
130
-    {
131
-        $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName);
132
-        if ($configuration === null) {
133
-            throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.');
134
-        }
135
-        return $configuration->getProperty($property)->getValue();
136
-    }
137
-
138
-    private static function setInConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property, ?string $value)
139
-    {
140
-        $configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName);
141
-        if ($configuration === null) {
142
-            throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.');
143
-        }
144
-        $configuration->getProperty($property)->setValue($value);
145
-    }
21
+	/**
22
+	 * @var HtmlBlock
23
+	 */
24
+	public $content;
25
+
26
+	protected $daoNamespace;
27
+	protected $beanNamespace;
28
+	protected $daoFactoryInstanceName;
29
+	protected $autoloadDetected;
30
+	///protected $storeInUtc;
31
+	protected $useCustomComposer;
32
+	protected $composerFile;
33
+
34
+	/**
35
+	 * Admin page used to display the DAO generation form.
36
+	 *
37
+	 * @Action
38
+	 */
39
+	public function defaultAction($name, $selfedit = 'false')
40
+	{
41
+		$this->initController($name, $selfedit);
42
+
43
+		// Fill variables
44
+		$this->daoNamespace = self::getFromConfiguration($this->moufManager, $name, 'daoNamespace');
45
+		$this->beanNamespace = self::getFromConfiguration($this->moufManager, $name, 'beanNamespace');
46
+		$this->daoFactoryInstanceName = self::getFromConfiguration($this->moufManager, $name, 'daoFactoryInstanceName');
47
+		//$this->storeInUtc = self::getFromConfiguration($this->moufManager, $name, 'storeInUtc');
48
+		$this->composerFile = self::getFromConfiguration($this->moufManager, $name, 'customComposerFile');
49
+		$this->useCustomComposer = $this->composerFile ? true : false;
50
+
51
+		if ($this->daoNamespace == null && $this->beanNamespace == null) {
52
+			$classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json');
53
+
54
+			$autoloadNamespaces = $classNameMapper->getManagedNamespaces();
55
+			if ($autoloadNamespaces) {
56
+				$this->autoloadDetected = true;
57
+				$rootNamespace = $autoloadNamespaces[0];
58
+				$this->daoNamespace = $rootNamespace.'Dao';
59
+				$this->beanNamespace = $rootNamespace.'Dao\\Bean';
60
+			} else {
61
+				$this->autoloadDetected = false;
62
+				$this->daoNamespace = 'YourApplication\\Dao';
63
+				$this->beanNamespace = 'YourApplication\\Dao\\Bean';
64
+			}
65
+		} else {
66
+			$this->autoloadDetected = true;
67
+		}
68
+
69
+		$this->content->addFile(__DIR__.'/../../../../views/tdbmGenerate.php', $this);
70
+		$this->template->toHtml();
71
+	}
72
+
73
+	/**
74
+	 * This action generates the DAOs and Beans for the TDBM service passed in parameter.
75
+	 *
76
+	 * @Action
77
+	 *
78
+	 * @param string $name
79
+	 * @param bool   $selfedit
80
+	 */
81
+	public function generate($name, $daonamespace, $beannamespace, $daofactoryinstancename, /*$storeInUtc = 0,*/ $selfedit = 'false', $useCustomComposer = false, $composerFile = '')
82
+	{
83
+		$this->initController($name, $selfedit);
84
+
85
+		self::generateDaos($this->moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit, /*$storeInUtc,*/ $useCustomComposer, $composerFile);
86
+
87
+		// TODO: better: we should redirect to a screen that list the number of DAOs generated, etc...
88
+		header('Location: '.ROOT_URL.'ajaxinstance/?name='.urlencode($name).'&selfedit='.$selfedit);
89
+	}
90
+
91
+	/**
92
+	 * This function generates the DAOs and Beans for the TDBM service passed in parameter.
93
+	 *
94
+	 * @param MoufManager $moufManager
95
+	 * @param string      $name
96
+	 * @param string      $daonamespace
97
+	 * @param string      $beannamespace
98
+	 * @param string      $daofactoryclassname
99
+	 * @param string      $daofactoryinstancename
100
+	 * @param string      $selfedit
101
+	 *
102
+	 * @throws \Mouf\MoufException
103
+	 */
104
+	public static function generateDaos(MoufManager $moufManager, $name, $daonamespace, $beannamespace, $daofactoryinstancename, $selfedit = 'false', /*$storeInUtc = null,*/ $useCustomComposer = null, $composerFile = null)
105
+	{
106
+		self::setInConfiguration($moufManager, $name, 'daoNamespace', $daonamespace);
107
+		self::setInConfiguration($moufManager, $name, 'beanNamespace', $beannamespace);
108
+		self::setInConfiguration($moufManager, $name, 'daoFactoryInstanceName', $daofactoryinstancename);
109
+		//self::setInConfiguration($moufManager, $name, 'storeInUtc', $storeInUtc);
110
+		if ($useCustomComposer) {
111
+			self::setInConfiguration($moufManager, $name, 'customComposerFile', $composerFile);
112
+		} else {
113
+			self::setInConfiguration($moufManager, $name, 'customComposerFile', null);
114
+		}
115
+		// Let's rewrite before calling the DAO generator
116
+		$moufManager->rewriteMouf();
117
+
118
+
119
+		$tdbmService = new InstanceProxy($name);
120
+		/* @var $tdbmService TDBMService */
121
+		$tdbmService->generateAllDaosAndBeans(($useCustomComposer ? $composerFile : null));
122
+	}
123
+
124
+	private static function getConfigurationDescriptor(MoufManager $moufManager, string $tdbmInstanceName)
125
+	{
126
+		return $moufManager->getInstanceDescriptor($tdbmInstanceName)->getConstructorArgumentProperty('configuration')->getValue();
127
+	}
128
+
129
+	private static function getFromConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property)
130
+	{
131
+		$configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName);
132
+		if ($configuration === null) {
133
+			throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.');
134
+		}
135
+		return $configuration->getProperty($property)->getValue();
136
+	}
137
+
138
+	private static function setInConfiguration(MoufManager $moufManager, string $tdbmInstanceName, string $property, ?string $value)
139
+	{
140
+		$configuration = self::getConfigurationDescriptor($moufManager, $tdbmInstanceName);
141
+		if ($configuration === null) {
142
+			throw new \RuntimeException('Unable to find the configuration object linked to TDBMService.');
143
+		}
144
+		$configuration->getProperty($property)->setValue($value);
145
+	}
146 146
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Utils/MoufDiListener.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -11,58 +11,58 @@
 block discarded – undo
11 11
 class MoufDiListener implements GeneratorListenerInterface
12 12
 {
13 13
 
14
-    /**
15
-     * @param ConfigurationInterface $configuration
16
-     * @param BeanDescriptorInterface[] $beanDescriptors
17
-     */
18
-    public function onGenerate(ConfigurationInterface $configuration, array $beanDescriptors): void
19
-    {
20
-        // Let's generate the needed instance in Mouf.
21
-        $moufManager = MoufManager::getMoufManager();
14
+	/**
15
+	 * @param ConfigurationInterface $configuration
16
+	 * @param BeanDescriptorInterface[] $beanDescriptors
17
+	 */
18
+	public function onGenerate(ConfigurationInterface $configuration, array $beanDescriptors): void
19
+	{
20
+		// Let's generate the needed instance in Mouf.
21
+		$moufManager = MoufManager::getMoufManager();
22 22
 
23
-        $daoFactoryInstanceName = null;
24
-        if ($configuration instanceof MoufConfiguration) {
25
-            $daoFactoryInstanceName = $configuration->getDaoFactoryInstanceName();
26
-            $daoFactoryClassName = $configuration->getDaoNamespace().'\\Generated\\'.$configuration->getNamingStrategy()->getDaoFactoryClassName();
27
-            $moufManager->declareComponent($daoFactoryInstanceName, $daoFactoryClassName, false, MoufManager::DECLARE_ON_EXIST_KEEP_INCOMING_LINKS);
28
-        }
23
+		$daoFactoryInstanceName = null;
24
+		if ($configuration instanceof MoufConfiguration) {
25
+			$daoFactoryInstanceName = $configuration->getDaoFactoryInstanceName();
26
+			$daoFactoryClassName = $configuration->getDaoNamespace().'\\Generated\\'.$configuration->getNamingStrategy()->getDaoFactoryClassName();
27
+			$moufManager->declareComponent($daoFactoryInstanceName, $daoFactoryClassName, false, MoufManager::DECLARE_ON_EXIST_KEEP_INCOMING_LINKS);
28
+		}
29 29
 
30
-        $tdbmServiceInstanceName = $this->getTdbmInstanceName($configuration);
30
+		$tdbmServiceInstanceName = $this->getTdbmInstanceName($configuration);
31 31
 
32
-        foreach ($beanDescriptors as $beanDescriptor) {
33
-            $daoName = $beanDescriptor->getDaoClassName();
32
+		foreach ($beanDescriptors as $beanDescriptor) {
33
+			$daoName = $beanDescriptor->getDaoClassName();
34 34
 
35
-            $instanceName = TDBMDaoGenerator::toVariableName($daoName);
36
-            if (!$moufManager->instanceExists($instanceName)) {
37
-                $moufManager->declareComponent($instanceName, $configuration->getDaoNamespace().'\\'.$daoName);
38
-            }
39
-            $moufManager->setParameterViaConstructor($instanceName, 0, $tdbmServiceInstanceName, 'object');
40
-            if ($daoFactoryInstanceName !== null) {
41
-                $moufManager->bindComponentViaSetter($daoFactoryInstanceName, 'set'.$daoName, $instanceName);
42
-            }
43
-        }
35
+			$instanceName = TDBMDaoGenerator::toVariableName($daoName);
36
+			if (!$moufManager->instanceExists($instanceName)) {
37
+				$moufManager->declareComponent($instanceName, $configuration->getDaoNamespace().'\\'.$daoName);
38
+			}
39
+			$moufManager->setParameterViaConstructor($instanceName, 0, $tdbmServiceInstanceName, 'object');
40
+			if ($daoFactoryInstanceName !== null) {
41
+				$moufManager->bindComponentViaSetter($daoFactoryInstanceName, 'set'.$daoName, $instanceName);
42
+			}
43
+		}
44 44
 
45
-        $moufManager->rewriteMouf();
46
-    }
45
+		$moufManager->rewriteMouf();
46
+	}
47 47
 
48
-    private function getTdbmInstanceName(ConfigurationInterface $configuration) : string
49
-    {
50
-        $moufManager = MoufManager::getMoufManager();
48
+	private function getTdbmInstanceName(ConfigurationInterface $configuration) : string
49
+	{
50
+		$moufManager = MoufManager::getMoufManager();
51 51
 
52
-        $configurationInstanceName = $moufManager->findInstanceName($configuration);
53
-        if (!$configurationInstanceName) {
54
-            throw new \TDBMException('Could not find TDBM instance for configuration object.');
55
-        }
52
+		$configurationInstanceName = $moufManager->findInstanceName($configuration);
53
+		if (!$configurationInstanceName) {
54
+			throw new \TDBMException('Could not find TDBM instance for configuration object.');
55
+		}
56 56
 
57
-        // Let's find the configuration
58
-        $tdbmServicesNames = $moufManager->findInstances(TDBMService::class);
57
+		// Let's find the configuration
58
+		$tdbmServicesNames = $moufManager->findInstances(TDBMService::class);
59 59
 
60
-        foreach ($tdbmServicesNames as $name) {
61
-            if ($moufManager->getInstanceDescriptor($name)->getConstructorArgumentProperty('configuration')->getValue()->getName() === $configurationInstanceName) {
62
-                return $name;
63
-            }
64
-        }
60
+		foreach ($tdbmServicesNames as $name) {
61
+			if ($moufManager->getInstanceDescriptor($name)->getConstructorArgumentProperty('configuration')->getValue()->getName() === $configurationInstanceName) {
62
+				return $name;
63
+			}
64
+		}
65 65
 
66
-        throw new \TDBMException('Could not find TDBMService instance.');
67
-    }
66
+		throw new \TDBMException('Could not find TDBMService instance.');
67
+	}
68 68
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Utils/DefaultNamingStrategy.php 1 patch
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -7,168 +7,168 @@
 block discarded – undo
7 7
 
8 8
 class DefaultNamingStrategy implements NamingStrategyInterface
9 9
 {
10
-    private $beanPrefix = '';
11
-    private $beanSuffix = '';
12
-    private $baseBeanPrefix = 'Abstract';
13
-    private $baseBeanSuffix = '';
14
-    private $daoPrefix = '';
15
-    private $daoSuffix = 'Dao';
16
-    private $baseDaoPrefix = 'Abstract';
17
-    private $baseDaoSuffix = 'Dao';
18
-
19
-    /**
20
-     * Sets the string prefix to any bean class name.
21
-     *
22
-     * @param string $beanPrefix
23
-     */
24
-    public function setBeanPrefix(string $beanPrefix)
25
-    {
26
-        $this->beanPrefix = $beanPrefix;
27
-    }
28
-
29
-    /**
30
-     * Sets the string suffix to any bean class name.
31
-     *
32
-     * @param string $beanSuffix
33
-     */
34
-    public function setBeanSuffix(string $beanSuffix)
35
-    {
36
-        $this->beanSuffix = $beanSuffix;
37
-    }
38
-
39
-    /**
40
-     * Sets the string prefix to any base bean class name.
41
-     *
42
-     * @param string $baseBeanPrefix
43
-     */
44
-    public function setBaseBeanPrefix(string $baseBeanPrefix)
45
-    {
46
-        $this->baseBeanPrefix = $baseBeanPrefix;
47
-    }
48
-
49
-    /**
50
-     * Sets the string suffix to any base bean class name.
51
-     *
52
-     * @param string $baseBeanSuffix
53
-     */
54
-    public function setBaseBeanSuffix(string $baseBeanSuffix)
55
-    {
56
-        $this->baseBeanSuffix = $baseBeanSuffix;
57
-    }
58
-
59
-    /**
60
-     * Sets the string prefix to any DAO class name.
61
-     *
62
-     * @param string $daoPrefix
63
-     */
64
-    public function setDaoPrefix(string $daoPrefix)
65
-    {
66
-        $this->daoPrefix = $daoPrefix;
67
-    }
68
-
69
-    /**
70
-     * Sets the string suffix to any DAO class name.
71
-     *
72
-     * @param string $daoSuffix
73
-     */
74
-    public function setDaoSuffix(string $daoSuffix)
75
-    {
76
-        $this->daoSuffix = $daoSuffix;
77
-    }
78
-
79
-    /**
80
-     * Sets the string prefix to any base DAO class name.
81
-     *
82
-     * @param string $baseDaoPrefix
83
-     */
84
-    public function setBaseDaoPrefix(string $baseDaoPrefix)
85
-    {
86
-        $this->baseDaoPrefix = $baseDaoPrefix;
87
-    }
88
-
89
-    /**
90
-     * Sets the string suffix to any base DAO class name.
91
-     *
92
-     * @param string $baseDaoSuffix
93
-     */
94
-    public function setBaseDaoSuffix(string $baseDaoSuffix)
95
-    {
96
-        $this->baseDaoSuffix = $baseDaoSuffix;
97
-    }
98
-
99
-
100
-    /**
101
-     * Returns the bean class name from the table name (excluding the namespace).
102
-     *
103
-     * @param string $tableName
104
-     * @return string
105
-     */
106
-    public function getBeanClassName(string $tableName): string
107
-    {
108
-        return $this->beanPrefix.self::toSingularCamelCase($tableName).$this->beanSuffix;
109
-    }
110
-
111
-    /**
112
-     * Returns the base bean class name from the table name (excluding the namespace).
113
-     *
114
-     * @param string $tableName
115
-     * @return string
116
-     */
117
-    public function getBaseBeanClassName(string $tableName): string
118
-    {
119
-        return $this->baseBeanPrefix.self::toSingularCamelCase($tableName).$this->baseBeanSuffix;
120
-    }
121
-
122
-    /**
123
-     * Returns the name of the DAO class from the table name (excluding the namespace).
124
-     *
125
-     * @param string $tableName
126
-     * @return string
127
-     */
128
-    public function getDaoClassName(string $tableName): string
129
-    {
130
-        return $this->daoPrefix.self::toSingularCamelCase($tableName).$this->daoSuffix;
131
-    }
132
-
133
-    /**
134
-     * Returns the name of the base DAO class from the table name (excluding the namespace).
135
-     *
136
-     * @param string $tableName
137
-     * @return string
138
-     */
139
-    public function getBaseDaoClassName(string $tableName): string
140
-    {
141
-        return $this->baseDaoPrefix.self::toSingularCamelCase($tableName).$this->baseDaoSuffix;
142
-    }
143
-
144
-    /**
145
-     * Tries to put string to the singular form (if it is plural) and camel case form.
146
-     * We assume the table names are in english.
147
-     *
148
-     * @param $str string
149
-     *
150
-     * @return string
151
-     */
152
-    private static function toSingularCamelCase(string $str): string
153
-    {
154
-        $tokens = preg_split("/[_ ]+/", $str);
155
-        $tokens = array_map([Inflector::class, 'singularize'], $tokens);
156
-
157
-        $str = '';
158
-        foreach ($tokens as $token) {
159
-            $str .= ucfirst(Inflector::singularize($token));
160
-        }
161
-
162
-        return $str;
163
-    }
164
-
165
-    /**
166
-     * Returns the class name for the DAO factory.
167
-     *
168
-     * @return string
169
-     */
170
-    public function getDaoFactoryClassName(): string
171
-    {
172
-        return 'DaoFactory';
173
-    }
10
+	private $beanPrefix = '';
11
+	private $beanSuffix = '';
12
+	private $baseBeanPrefix = 'Abstract';
13
+	private $baseBeanSuffix = '';
14
+	private $daoPrefix = '';
15
+	private $daoSuffix = 'Dao';
16
+	private $baseDaoPrefix = 'Abstract';
17
+	private $baseDaoSuffix = 'Dao';
18
+
19
+	/**
20
+	 * Sets the string prefix to any bean class name.
21
+	 *
22
+	 * @param string $beanPrefix
23
+	 */
24
+	public function setBeanPrefix(string $beanPrefix)
25
+	{
26
+		$this->beanPrefix = $beanPrefix;
27
+	}
28
+
29
+	/**
30
+	 * Sets the string suffix to any bean class name.
31
+	 *
32
+	 * @param string $beanSuffix
33
+	 */
34
+	public function setBeanSuffix(string $beanSuffix)
35
+	{
36
+		$this->beanSuffix = $beanSuffix;
37
+	}
38
+
39
+	/**
40
+	 * Sets the string prefix to any base bean class name.
41
+	 *
42
+	 * @param string $baseBeanPrefix
43
+	 */
44
+	public function setBaseBeanPrefix(string $baseBeanPrefix)
45
+	{
46
+		$this->baseBeanPrefix = $baseBeanPrefix;
47
+	}
48
+
49
+	/**
50
+	 * Sets the string suffix to any base bean class name.
51
+	 *
52
+	 * @param string $baseBeanSuffix
53
+	 */
54
+	public function setBaseBeanSuffix(string $baseBeanSuffix)
55
+	{
56
+		$this->baseBeanSuffix = $baseBeanSuffix;
57
+	}
58
+
59
+	/**
60
+	 * Sets the string prefix to any DAO class name.
61
+	 *
62
+	 * @param string $daoPrefix
63
+	 */
64
+	public function setDaoPrefix(string $daoPrefix)
65
+	{
66
+		$this->daoPrefix = $daoPrefix;
67
+	}
68
+
69
+	/**
70
+	 * Sets the string suffix to any DAO class name.
71
+	 *
72
+	 * @param string $daoSuffix
73
+	 */
74
+	public function setDaoSuffix(string $daoSuffix)
75
+	{
76
+		$this->daoSuffix = $daoSuffix;
77
+	}
78
+
79
+	/**
80
+	 * Sets the string prefix to any base DAO class name.
81
+	 *
82
+	 * @param string $baseDaoPrefix
83
+	 */
84
+	public function setBaseDaoPrefix(string $baseDaoPrefix)
85
+	{
86
+		$this->baseDaoPrefix = $baseDaoPrefix;
87
+	}
88
+
89
+	/**
90
+	 * Sets the string suffix to any base DAO class name.
91
+	 *
92
+	 * @param string $baseDaoSuffix
93
+	 */
94
+	public function setBaseDaoSuffix(string $baseDaoSuffix)
95
+	{
96
+		$this->baseDaoSuffix = $baseDaoSuffix;
97
+	}
98
+
99
+
100
+	/**
101
+	 * Returns the bean class name from the table name (excluding the namespace).
102
+	 *
103
+	 * @param string $tableName
104
+	 * @return string
105
+	 */
106
+	public function getBeanClassName(string $tableName): string
107
+	{
108
+		return $this->beanPrefix.self::toSingularCamelCase($tableName).$this->beanSuffix;
109
+	}
110
+
111
+	/**
112
+	 * Returns the base bean class name from the table name (excluding the namespace).
113
+	 *
114
+	 * @param string $tableName
115
+	 * @return string
116
+	 */
117
+	public function getBaseBeanClassName(string $tableName): string
118
+	{
119
+		return $this->baseBeanPrefix.self::toSingularCamelCase($tableName).$this->baseBeanSuffix;
120
+	}
121
+
122
+	/**
123
+	 * Returns the name of the DAO class from the table name (excluding the namespace).
124
+	 *
125
+	 * @param string $tableName
126
+	 * @return string
127
+	 */
128
+	public function getDaoClassName(string $tableName): string
129
+	{
130
+		return $this->daoPrefix.self::toSingularCamelCase($tableName).$this->daoSuffix;
131
+	}
132
+
133
+	/**
134
+	 * Returns the name of the base DAO class from the table name (excluding the namespace).
135
+	 *
136
+	 * @param string $tableName
137
+	 * @return string
138
+	 */
139
+	public function getBaseDaoClassName(string $tableName): string
140
+	{
141
+		return $this->baseDaoPrefix.self::toSingularCamelCase($tableName).$this->baseDaoSuffix;
142
+	}
143
+
144
+	/**
145
+	 * Tries to put string to the singular form (if it is plural) and camel case form.
146
+	 * We assume the table names are in english.
147
+	 *
148
+	 * @param $str string
149
+	 *
150
+	 * @return string
151
+	 */
152
+	private static function toSingularCamelCase(string $str): string
153
+	{
154
+		$tokens = preg_split("/[_ ]+/", $str);
155
+		$tokens = array_map([Inflector::class, 'singularize'], $tokens);
156
+
157
+		$str = '';
158
+		foreach ($tokens as $token) {
159
+			$str .= ucfirst(Inflector::singularize($token));
160
+		}
161
+
162
+		return $str;
163
+	}
164
+
165
+	/**
166
+	 * Returns the class name for the DAO factory.
167
+	 *
168
+	 * @return string
169
+	 */
170
+	public function getDaoFactoryClassName(): string
171
+	{
172
+		return 'DaoFactory';
173
+	}
174 174
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Commands/GenerateCommand.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -13,48 +13,48 @@
 block discarded – undo
13 13
 class GenerateCommand extends Command
14 14
 {
15 15
 
16
-    /**
17
-     * @var TDBMService
18
-     */
19
-    private $configuration;
16
+	/**
17
+	 * @var TDBMService
18
+	 */
19
+	private $configuration;
20 20
 
21
-    public function __construct(ConfigurationInterface $configuration)
22
-    {
23
-        parent::__construct();
24
-        $this->configuration = $configuration;
25
-    }
21
+	public function __construct(ConfigurationInterface $configuration)
22
+	{
23
+		parent::__construct();
24
+		$this->configuration = $configuration;
25
+	}
26 26
 
27
-    protected function configure()
28
-    {
29
-        $this->setName('tdbm:generate')
30
-            ->setDescription('Generates DAOs and beans.')
31
-            ->setHelp('Use this command to generate or regenerate the DAOs and beans for your project.')
32
-        ;
33
-    }
27
+	protected function configure()
28
+	{
29
+		$this->setName('tdbm:generate')
30
+			->setDescription('Generates DAOs and beans.')
31
+			->setHelp('Use this command to generate or regenerate the DAOs and beans for your project.')
32
+		;
33
+	}
34 34
 
35
-    protected function execute(InputInterface $input, OutputInterface $output)
36
-    {
37
-        // TODO: externalize composer.json file for autoloading (no more parameters for generateAllDaosAndBeans)
35
+	protected function execute(InputInterface $input, OutputInterface $output)
36
+	{
37
+		// TODO: externalize composer.json file for autoloading (no more parameters for generateAllDaosAndBeans)
38 38
 
39
-        $alteredConf = new AlteredConfiguration($this->configuration);
39
+		$alteredConf = new AlteredConfiguration($this->configuration);
40 40
 
41 41
 
42
-        $loggers = [ new ConsoleLogger($output) ];
42
+		$loggers = [ new ConsoleLogger($output) ];
43 43
 
44
-        $logger = $alteredConf->getLogger();
45
-        if ($logger) {
46
-            $loggers[] = $logger;
47
-        }
44
+		$logger = $alteredConf->getLogger();
45
+		if ($logger) {
46
+			$loggers[] = $logger;
47
+		}
48 48
 
49
-        $multiLogger = new MultiLogger($loggers);
49
+		$multiLogger = new MultiLogger($loggers);
50 50
 
51
-        $alteredConf->setLogger($multiLogger);
51
+		$alteredConf->setLogger($multiLogger);
52 52
 
53
-        $multiLogger->notice('Starting regenerating DAOs and beans');
53
+		$multiLogger->notice('Starting regenerating DAOs and beans');
54 54
 
55
-        $tdbmService = new TDBMService($this->configuration);
56
-        $tdbmService->generateAllDaosAndBeans();
55
+		$tdbmService = new TDBMService($this->configuration);
56
+		$tdbmService->generateAllDaosAndBeans();
57 57
 
58
-        $multiLogger->notice('Finished regenerating DAOs and beans');
59
-    }
58
+		$multiLogger->notice('Finished regenerating DAOs and beans');
59
+	}
60 60
 }
Please login to merge, or discard this patch.
src/Mouf/Database/TDBM/Controllers/TdbmInstallController.php 1 patch
Indentation   +215 added lines, -215 removed lines patch added patch discarded remove patch
@@ -21,219 +21,219 @@
 block discarded – undo
21 21
  */
22 22
 class TdbmInstallController extends Controller
23 23
 {
24
-    /**
25
-     * @var HtmlBlock
26
-     */
27
-    public $content;
28
-
29
-    public $selfedit;
30
-
31
-    /**
32
-     * The active MoufManager to be edited/viewed.
33
-     *
34
-     * @var MoufManager
35
-     */
36
-    public $moufManager;
37
-
38
-    /**
39
-     * The template used by the main page for mouf.
40
-     *
41
-     * @Property
42
-     * @Compulsory
43
-     *
44
-     * @var TemplateInterface
45
-     */
46
-    public $template;
47
-
48
-    /**
49
-     * Displays the first install screen.
50
-     *
51
-     * @Action
52
-     * @Logged
53
-     *
54
-     * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
55
-     */
56
-    public function defaultAction($selfedit = 'false')
57
-    {
58
-        $this->selfedit = $selfedit;
59
-
60
-        if ($selfedit == 'true') {
61
-            $this->moufManager = MoufManager::getMoufManager();
62
-        } else {
63
-            $this->moufManager = MoufManager::getMoufManagerHiddenInstance();
64
-        }
65
-
66
-        $this->content->addFile(dirname(__FILE__).'/../../../../views/installStep1.php', $this);
67
-        $this->template->toHtml();
68
-    }
69
-
70
-    /**
71
-     * Skips the install process.
72
-     *
73
-     * @Action
74
-     * @Logged
75
-     *
76
-     * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
77
-     */
78
-    public function skip($selfedit = 'false')
79
-    {
80
-        InstallUtils::continueInstall($selfedit == 'true');
81
-    }
82
-
83
-    protected $daoNamespace;
84
-    protected $beanNamespace;
85
-    protected $autoloadDetected;
86
-    //protected $storeInUtc;
87
-    protected $useCustomComposer = false;
88
-    protected $composerFile;
89
-
90
-    /**
91
-     * Displays the second install screen.
92
-     *
93
-     * @Action
94
-     * @Logged
95
-     *
96
-     * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
97
-     */
98
-    public function configure($selfedit = 'false')
99
-    {
100
-        $this->selfedit = $selfedit;
101
-
102
-        if ($selfedit == 'true') {
103
-            $this->moufManager = MoufManager::getMoufManager();
104
-        } else {
105
-            $this->moufManager = MoufManager::getMoufManagerHiddenInstance();
106
-        }
107
-
108
-        // Let's start by performing basic checks about the instances we assume to exist.
109
-        if (!$this->moufManager->instanceExists('dbalConnection')) {
110
-            $this->displayErrorMsg("The TDBM install process assumes your database connection instance is already created, and that the name of this instance is 'dbalConnection'. Could not find the 'dbalConnection' instance.");
111
-
112
-            return;
113
-        }
114
-
115
-        if ($this->moufManager->has('tdbmConfiguration')) {
116
-            $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration');
117
-
118
-            $this->beanNamespace = $tdbmConfiguration->getConstructorArgumentProperty('beanNamespace')->getValue();
119
-            $this->daoNamespace = $tdbmConfiguration->getConstructorArgumentProperty('daoNamespace')->getValue();
120
-        } else {
121
-            // Old TDBM 4.2 fallback
122
-            $this->daoNamespace = $this->moufManager->getVariable('tdbmDefaultDaoNamespace_tdbmService');
123
-            $this->beanNamespace = $this->moufManager->getVariable('tdbmDefaultBeanNamespace_tdbmService');
124
-        }
125
-
126
-        if ($this->daoNamespace == null && $this->beanNamespace == null) {
127
-            $classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json');
128
-
129
-            $autoloadNamespaces = $classNameMapper->getManagedNamespaces();
130
-            if ($autoloadNamespaces) {
131
-                $this->autoloadDetected = true;
132
-                $rootNamespace = $autoloadNamespaces[0];
133
-                $this->daoNamespace = $rootNamespace.'Dao';
134
-                $this->beanNamespace = $rootNamespace.'Model';
135
-            } else {
136
-                $this->autoloadDetected = false;
137
-                $this->daoNamespace = 'YourApplication\\Dao';
138
-                $this->beanNamespace = 'YourApplication\\Model';
139
-            }
140
-        } else {
141
-            $this->autoloadDetected = true;
142
-        }
143
-        $this->defaultPath = true;
144
-        $this->storePath = '';
145
-
146
-        $this->castDatesToDateTime = true;
147
-
148
-        $this->content->addFile(__DIR__.'/../../../../views/installStep2.php', $this);
149
-        $this->template->toHtml();
150
-    }
151
-
152
-    /**
153
-     * This action generates the TDBM instance, then the DAOs and Beans.
154
-     *
155
-     * @Action
156
-     *
157
-     * @param string $daonamespace
158
-     * @param string $beannamespace
159
-     * @param string $selfedit
160
-     *
161
-     * @throws \Mouf\MoufException
162
-     */
163
-    public function generate($daonamespace, $beannamespace, /*$storeInUtc = 0,*/ $selfedit = 'false', $defaultPath = false, $storePath = '')
164
-    {
165
-        $this->selfedit = $selfedit;
166
-
167
-        if ($selfedit == 'true') {
168
-            $this->moufManager = MoufManager::getMoufManager();
169
-        } else {
170
-            $this->moufManager = MoufManager::getMoufManagerHiddenInstance();
171
-        }
172
-
173
-        $doctrineCache = $this->moufManager->getInstanceDescriptor('defaultDoctrineCache');
174
-
175
-        $migratingFrom42 = false;
176
-        if ($this->moufManager->has('tdbmService') && !$this->moufManager->has('tdbmConfiguration')) {
177
-            $migratingFrom42 = true;
178
-        }
179
-
180
-        $namingStrategy = InstallUtils::getOrCreateInstance('namingStrategy', DefaultNamingStrategy::class, $this->moufManager);
181
-        if ($migratingFrom42) {
182
-            // Let's setup the naming strategy for compatibility
183
-            $namingStrategy->getSetterProperty('setBeanPrefix')->setValue('');
184
-            $namingStrategy->getSetterProperty('setBeanSuffix')->setValue('Bean');
185
-            $namingStrategy->getSetterProperty('setBaseBeanPrefix')->setValue('');
186
-            $namingStrategy->getSetterProperty('setBaseBeanSuffix')->setValue('BaseBean');
187
-            $namingStrategy->getSetterProperty('setDaoPrefix')->setValue('');
188
-            $namingStrategy->getSetterProperty('setDaoSuffix')->setValue('Dao');
189
-            $namingStrategy->getSetterProperty('setBaseDaoPrefix')->setValue('');
190
-            $namingStrategy->getSetterProperty('setBaseDaoSuffix')->setValue('BaseDao');
191
-        }
192
-
193
-        if (!$this->moufManager->instanceExists('tdbmConfiguration')) {
194
-            $moufListener = InstallUtils::getOrCreateInstance(MoufDiListener::class, MoufDiListener::class, $this->moufManager);
195
-
196
-            $tdbmConfiguration = $this->moufManager->createInstance(MoufConfiguration::class)->setName('tdbmConfiguration');
197
-            $tdbmConfiguration->getConstructorArgumentProperty('connection')->setValue($this->moufManager->getInstanceDescriptor('dbalConnection'));
198
-            $tdbmConfiguration->getConstructorArgumentProperty('cache')->setValue($doctrineCache);
199
-            $tdbmConfiguration->getConstructorArgumentProperty('namingStrategy')->setValue($namingStrategy);
200
-            $tdbmConfiguration->getProperty('daoFactoryInstanceName')->setValue('daoFactory');
201
-            $tdbmConfiguration->getConstructorArgumentProperty('generatorListeners')->setValue([$moufListener]);
202
-
203
-            // Let's also delete the tdbmService if migrating versions <= 4.2
204
-            if ($migratingFrom42) {
205
-                $this->moufManager->removeComponent('tdbmService');
206
-            }
207
-        } else {
208
-            $tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration');
209
-        }
210
-
211
-        if (!$this->moufManager->instanceExists('tdbmService')) {
212
-            $tdbmService = $this->moufManager->createInstance('Mouf\\Database\\TDBM\\TDBMService')->setName('tdbmService');
213
-            $tdbmService->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration);
214
-        }
215
-
216
-        // We declare our instance of the Symfony command as a Mouf instance
217
-        $generateCommand = InstallUtils::getOrCreateInstance('generateCommand', GenerateCommand::class, $this->moufManager);
218
-        $generateCommand->getConstructorArgumentProperty('tdbmConfiguration')->setValue($tdbmConfiguration);
219
-
220
-        // We register that instance descriptor using "ConsoleUtils"
221
-        $consoleUtils = new ConsoleUtils($this->moufManager);
222
-        $consoleUtils->registerCommand($generateCommand);
223
-
224
-        $this->moufManager->rewriteMouf();
225
-
226
-        TdbmController::generateDaos($this->moufManager, 'tdbmService', $daonamespace, $beannamespace, 'daoFactory', $selfedit, /*$storeInUtc,*/ $defaultPath, $storePath);
227
-
228
-        InstallUtils::continueInstall($selfedit == 'true');
229
-    }
230
-
231
-    protected $errorMsg;
232
-
233
-    private function displayErrorMsg($msg)
234
-    {
235
-        $this->errorMsg = $msg;
236
-        $this->content->addFile(__DIR__.'/../../../../views/installError.php', $this);
237
-        $this->template->toHtml();
238
-    }
24
+	/**
25
+	 * @var HtmlBlock
26
+	 */
27
+	public $content;
28
+
29
+	public $selfedit;
30
+
31
+	/**
32
+	 * The active MoufManager to be edited/viewed.
33
+	 *
34
+	 * @var MoufManager
35
+	 */
36
+	public $moufManager;
37
+
38
+	/**
39
+	 * The template used by the main page for mouf.
40
+	 *
41
+	 * @Property
42
+	 * @Compulsory
43
+	 *
44
+	 * @var TemplateInterface
45
+	 */
46
+	public $template;
47
+
48
+	/**
49
+	 * Displays the first install screen.
50
+	 *
51
+	 * @Action
52
+	 * @Logged
53
+	 *
54
+	 * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
55
+	 */
56
+	public function defaultAction($selfedit = 'false')
57
+	{
58
+		$this->selfedit = $selfedit;
59
+
60
+		if ($selfedit == 'true') {
61
+			$this->moufManager = MoufManager::getMoufManager();
62
+		} else {
63
+			$this->moufManager = MoufManager::getMoufManagerHiddenInstance();
64
+		}
65
+
66
+		$this->content->addFile(dirname(__FILE__).'/../../../../views/installStep1.php', $this);
67
+		$this->template->toHtml();
68
+	}
69
+
70
+	/**
71
+	 * Skips the install process.
72
+	 *
73
+	 * @Action
74
+	 * @Logged
75
+	 *
76
+	 * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
77
+	 */
78
+	public function skip($selfedit = 'false')
79
+	{
80
+		InstallUtils::continueInstall($selfedit == 'true');
81
+	}
82
+
83
+	protected $daoNamespace;
84
+	protected $beanNamespace;
85
+	protected $autoloadDetected;
86
+	//protected $storeInUtc;
87
+	protected $useCustomComposer = false;
88
+	protected $composerFile;
89
+
90
+	/**
91
+	 * Displays the second install screen.
92
+	 *
93
+	 * @Action
94
+	 * @Logged
95
+	 *
96
+	 * @param string $selfedit If true, the name of the component must be a component from the Mouf framework itself (internal use only)
97
+	 */
98
+	public function configure($selfedit = 'false')
99
+	{
100
+		$this->selfedit = $selfedit;
101
+
102
+		if ($selfedit == 'true') {
103
+			$this->moufManager = MoufManager::getMoufManager();
104
+		} else {
105
+			$this->moufManager = MoufManager::getMoufManagerHiddenInstance();
106
+		}
107
+
108
+		// Let's start by performing basic checks about the instances we assume to exist.
109
+		if (!$this->moufManager->instanceExists('dbalConnection')) {
110
+			$this->displayErrorMsg("The TDBM install process assumes your database connection instance is already created, and that the name of this instance is 'dbalConnection'. Could not find the 'dbalConnection' instance.");
111
+
112
+			return;
113
+		}
114
+
115
+		if ($this->moufManager->has('tdbmConfiguration')) {
116
+			$tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration');
117
+
118
+			$this->beanNamespace = $tdbmConfiguration->getConstructorArgumentProperty('beanNamespace')->getValue();
119
+			$this->daoNamespace = $tdbmConfiguration->getConstructorArgumentProperty('daoNamespace')->getValue();
120
+		} else {
121
+			// Old TDBM 4.2 fallback
122
+			$this->daoNamespace = $this->moufManager->getVariable('tdbmDefaultDaoNamespace_tdbmService');
123
+			$this->beanNamespace = $this->moufManager->getVariable('tdbmDefaultBeanNamespace_tdbmService');
124
+		}
125
+
126
+		if ($this->daoNamespace == null && $this->beanNamespace == null) {
127
+			$classNameMapper = ClassNameMapper::createFromComposerFile(__DIR__.'/../../../../../../../../composer.json');
128
+
129
+			$autoloadNamespaces = $classNameMapper->getManagedNamespaces();
130
+			if ($autoloadNamespaces) {
131
+				$this->autoloadDetected = true;
132
+				$rootNamespace = $autoloadNamespaces[0];
133
+				$this->daoNamespace = $rootNamespace.'Dao';
134
+				$this->beanNamespace = $rootNamespace.'Model';
135
+			} else {
136
+				$this->autoloadDetected = false;
137
+				$this->daoNamespace = 'YourApplication\\Dao';
138
+				$this->beanNamespace = 'YourApplication\\Model';
139
+			}
140
+		} else {
141
+			$this->autoloadDetected = true;
142
+		}
143
+		$this->defaultPath = true;
144
+		$this->storePath = '';
145
+
146
+		$this->castDatesToDateTime = true;
147
+
148
+		$this->content->addFile(__DIR__.'/../../../../views/installStep2.php', $this);
149
+		$this->template->toHtml();
150
+	}
151
+
152
+	/**
153
+	 * This action generates the TDBM instance, then the DAOs and Beans.
154
+	 *
155
+	 * @Action
156
+	 *
157
+	 * @param string $daonamespace
158
+	 * @param string $beannamespace
159
+	 * @param string $selfedit
160
+	 *
161
+	 * @throws \Mouf\MoufException
162
+	 */
163
+	public function generate($daonamespace, $beannamespace, /*$storeInUtc = 0,*/ $selfedit = 'false', $defaultPath = false, $storePath = '')
164
+	{
165
+		$this->selfedit = $selfedit;
166
+
167
+		if ($selfedit == 'true') {
168
+			$this->moufManager = MoufManager::getMoufManager();
169
+		} else {
170
+			$this->moufManager = MoufManager::getMoufManagerHiddenInstance();
171
+		}
172
+
173
+		$doctrineCache = $this->moufManager->getInstanceDescriptor('defaultDoctrineCache');
174
+
175
+		$migratingFrom42 = false;
176
+		if ($this->moufManager->has('tdbmService') && !$this->moufManager->has('tdbmConfiguration')) {
177
+			$migratingFrom42 = true;
178
+		}
179
+
180
+		$namingStrategy = InstallUtils::getOrCreateInstance('namingStrategy', DefaultNamingStrategy::class, $this->moufManager);
181
+		if ($migratingFrom42) {
182
+			// Let's setup the naming strategy for compatibility
183
+			$namingStrategy->getSetterProperty('setBeanPrefix')->setValue('');
184
+			$namingStrategy->getSetterProperty('setBeanSuffix')->setValue('Bean');
185
+			$namingStrategy->getSetterProperty('setBaseBeanPrefix')->setValue('');
186
+			$namingStrategy->getSetterProperty('setBaseBeanSuffix')->setValue('BaseBean');
187
+			$namingStrategy->getSetterProperty('setDaoPrefix')->setValue('');
188
+			$namingStrategy->getSetterProperty('setDaoSuffix')->setValue('Dao');
189
+			$namingStrategy->getSetterProperty('setBaseDaoPrefix')->setValue('');
190
+			$namingStrategy->getSetterProperty('setBaseDaoSuffix')->setValue('BaseDao');
191
+		}
192
+
193
+		if (!$this->moufManager->instanceExists('tdbmConfiguration')) {
194
+			$moufListener = InstallUtils::getOrCreateInstance(MoufDiListener::class, MoufDiListener::class, $this->moufManager);
195
+
196
+			$tdbmConfiguration = $this->moufManager->createInstance(MoufConfiguration::class)->setName('tdbmConfiguration');
197
+			$tdbmConfiguration->getConstructorArgumentProperty('connection')->setValue($this->moufManager->getInstanceDescriptor('dbalConnection'));
198
+			$tdbmConfiguration->getConstructorArgumentProperty('cache')->setValue($doctrineCache);
199
+			$tdbmConfiguration->getConstructorArgumentProperty('namingStrategy')->setValue($namingStrategy);
200
+			$tdbmConfiguration->getProperty('daoFactoryInstanceName')->setValue('daoFactory');
201
+			$tdbmConfiguration->getConstructorArgumentProperty('generatorListeners')->setValue([$moufListener]);
202
+
203
+			// Let's also delete the tdbmService if migrating versions <= 4.2
204
+			if ($migratingFrom42) {
205
+				$this->moufManager->removeComponent('tdbmService');
206
+			}
207
+		} else {
208
+			$tdbmConfiguration = $this->moufManager->getInstanceDescriptor('tdbmConfiguration');
209
+		}
210
+
211
+		if (!$this->moufManager->instanceExists('tdbmService')) {
212
+			$tdbmService = $this->moufManager->createInstance('Mouf\\Database\\TDBM\\TDBMService')->setName('tdbmService');
213
+			$tdbmService->getConstructorArgumentProperty('configuration')->setValue($tdbmConfiguration);
214
+		}
215
+
216
+		// We declare our instance of the Symfony command as a Mouf instance
217
+		$generateCommand = InstallUtils::getOrCreateInstance('generateCommand', GenerateCommand::class, $this->moufManager);
218
+		$generateCommand->getConstructorArgumentProperty('tdbmConfiguration')->setValue($tdbmConfiguration);
219
+
220
+		// We register that instance descriptor using "ConsoleUtils"
221
+		$consoleUtils = new ConsoleUtils($this->moufManager);
222
+		$consoleUtils->registerCommand($generateCommand);
223
+
224
+		$this->moufManager->rewriteMouf();
225
+
226
+		TdbmController::generateDaos($this->moufManager, 'tdbmService', $daonamespace, $beannamespace, 'daoFactory', $selfedit, /*$storeInUtc,*/ $defaultPath, $storePath);
227
+
228
+		InstallUtils::continueInstall($selfedit == 'true');
229
+	}
230
+
231
+	protected $errorMsg;
232
+
233
+	private function displayErrorMsg($msg)
234
+	{
235
+		$this->errorMsg = $msg;
236
+		$this->content->addFile(__DIR__.'/../../../../views/installError.php', $this);
237
+		$this->template->toHtml();
238
+	}
239 239
 }
Please login to merge, or discard this patch.