Completed
Push — 4.2 ( f1faa2...7e8b07 )
by David
01:37
created

AbstractTDBMObject::checkTableName()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 7
Ratio 58.33 %

Importance

Changes 0
Metric Value
dl 7
loc 12
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 4
nop 1
1
<?php
2
3
namespace Mouf\Database\TDBM;
4
5
/*
6
 Copyright (C) 2006-2016 David Négrier - THE CODING MACHINE
7
8
 This program is free software; you can redistribute it and/or modify
9
 it under the terms of the GNU General Public License as published by
10
 the Free Software Foundation; either version 2 of the License, or
11
 (at your option) any later version.
12
13
 This program is distributed in the hope that it will be useful,
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 GNU General Public License for more details.
17
18
 You should have received a copy of the GNU General Public License
19
 along with this program; if not, write to the Free Software
20
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
23
use JsonSerializable;
24
25
/**
26
 * Instances of this class represent a "bean". Usually, a bean is mapped to a row of one table.
27
 * In some special cases (where inheritance is used), beans can be scattered on several tables.
28
 * Therefore, a TDBMObject is really a set of DbRow objects that represent one row in a table.
29
 *
30
 * @author David Negrier
31
 */
32
abstract class AbstractTDBMObject implements JsonSerializable
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 View Code Duplication
        if ($tableName === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if ($tableName === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if ($tableName === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
        return $this->dbRows[$tableName]->getRef($foreignKeyName);
288
    }
289
290
    /**
291
     * Adds a many to many relationship to this bean.
292
     *
293
     * @param string             $pivotTableName
294
     * @param AbstractTDBMObject $remoteBean
295
     */
296
    protected function addRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
297
    {
298
        $this->setRelationship($pivotTableName, $remoteBean, 'new');
299
    }
300
301
    /**
302
     * Returns true if there is a relationship to this bean.
303
     *
304
     * @param string             $pivotTableName
305
     * @param AbstractTDBMObject $remoteBean
306
     *
307
     * @return bool
308
     */
309
    protected function hasRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
310
    {
311
        $storage = $this->retrieveRelationshipsStorage($pivotTableName);
312
313
        if ($storage->contains($remoteBean)) {
314
            if ($storage[$remoteBean]['status'] !== 'delete') {
315
                return true;
316
            }
317
        }
318
319
        return false;
320
    }
321
322
    /**
323
     * Internal TDBM method. Removes a many to many relationship from this bean.
324
     *
325
     * @param string             $pivotTableName
326
     * @param AbstractTDBMObject $remoteBean
327
     */
328
    public function _removeRelationship($pivotTableName, AbstractTDBMObject $remoteBean)
329
    {
330
        if (isset($this->relationships[$pivotTableName][$remoteBean]) && $this->relationships[$pivotTableName][$remoteBean]['status'] === 'new') {
331
            unset($this->relationships[$pivotTableName][$remoteBean]);
332
            unset($remoteBean->relationships[$pivotTableName][$this]);
333
        } else {
334
            $this->setRelationship($pivotTableName, $remoteBean, 'delete');
335
        }
336
    }
337
338
    /**
339
     * Sets many to many relationships for this bean.
340
     * Adds new relationships and removes unused ones.
341
     *
342
     * @param $pivotTableName
343
     * @param array $remoteBeans
344
     */
345
    protected function setRelationships($pivotTableName, array $remoteBeans)
346
    {
347
        $storage = $this->retrieveRelationshipsStorage($pivotTableName);
348
349
        foreach ($storage as $oldRemoteBean) {
350
            if (!in_array($oldRemoteBean, $remoteBeans, true)) {
351
                // $oldRemoteBean must be removed
352
                $this->_removeRelationship($pivotTableName, $oldRemoteBean);
353
            }
354
        }
355
356
        foreach ($remoteBeans as $remoteBean) {
357
            if (!$storage->contains($remoteBean) || $storage[$remoteBean]['status'] === 'delete') {
358
                // $remoteBean must be added
359
                $this->addRelationship($pivotTableName, $remoteBean);
360
            }
361
        }
362
    }
363
364
    /**
365
     * Returns the list of objects linked to this bean via $pivotTableName.
366
     *
367
     * @param $pivotTableName
368
     *
369
     * @return \SplObjectStorage
370
     */
371
    private function retrieveRelationshipsStorage($pivotTableName)
372
    {
373
        $storage = $this->getRelationshipStorage($pivotTableName);
374
        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->loadedRelationships[$pivotTableName]) && $this->loadedRelationships[$pivotTableName])) {
375
            return $storage;
376
        }
377
378
        $beans = $this->tdbmService->_getRelatedBeans($pivotTableName, $this);
379
        $this->loadedRelationships[$pivotTableName] = true;
380
381
        foreach ($beans as $bean) {
382
            if (isset($storage[$bean])) {
383
                $oldStatus = $storage[$bean]['status'];
384
                if ($oldStatus === 'delete') {
385
                    // Keep deleted things deleted
386
                    continue;
387
                }
388
            }
389
            $this->setRelationship($pivotTableName, $bean, 'loaded');
390
        }
391
392
        return $storage;
393
    }
394
395
    /**
396
     * Internal TDBM method. Returns the list of objects linked to this bean via $pivotTableName.
397
     *
398
     * @param $pivotTableName
399
     *
400
     * @return AbstractTDBMObject[]
401
     */
402
    public function _getRelationships($pivotTableName)
403
    {
404
        return $this->relationshipStorageToArray($this->retrieveRelationshipsStorage($pivotTableName));
405
    }
406
407
    private function relationshipStorageToArray(\SplObjectStorage $storage)
408
    {
409
        $beans = [];
410
        foreach ($storage as $bean) {
411
            $statusArr = $storage[$bean];
412
            if ($statusArr['status'] !== 'delete') {
413
                $beans[] = $bean;
414
            }
415
        }
416
417
        return $beans;
418
    }
419
420
    /**
421
     * Declares a relationship between.
422
     *
423
     * @param string             $pivotTableName
424
     * @param AbstractTDBMObject $remoteBean
425
     * @param string             $status
426
     */
427
    private function setRelationship($pivotTableName, AbstractTDBMObject $remoteBean, $status)
428
    {
429
        $storage = $this->getRelationshipStorage($pivotTableName);
430
        $storage->attach($remoteBean, ['status' => $status, 'reverse' => false]);
431
        if ($this->status === TDBMObjectStateEnum::STATE_LOADED) {
432
            $this->_setStatus(TDBMObjectStateEnum::STATE_DIRTY);
433
        }
434
435
        $remoteStorage = $remoteBean->getRelationshipStorage($pivotTableName);
436
        $remoteStorage->attach($this, ['status' => $status, 'reverse' => true]);
437
    }
438
439
    /**
440
     * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
441
     *
442
     * @param string $pivotTableName
443
     *
444
     * @return \SplObjectStorage
445
     */
446
    private function getRelationshipStorage(string $pivotTableName) : \SplObjectStorage
447
    {
448
        return $this->relationships[$pivotTableName] ?? $this->relationships[$pivotTableName] = new \SplObjectStorage();
449
    }
450
451
    /**
452
     * Returns the SplObjectStorage associated to this relationship (creates it if it does not exists).
453
     *
454
     * @param string $tableName
455
     * @param string $foreignKeyName
456
     *
457
     * @return AlterableResultIterator
458
     */
459
    private function getManyToOneAlterableResultIterator(string $tableName, string $foreignKeyName) : AlterableResultIterator
460
    {
461
        $key = $tableName.'___'.$foreignKeyName;
462
463
        return $this->manyToOneRelationships[$key] ?? $this->manyToOneRelationships[$key] = new AlterableResultIterator();
464
    }
465
466
    /**
467
     * Declares a relationship between this bean and the bean pointing to it.
468
     *
469
     * @param string             $tableName
470
     * @param string             $foreignKeyName
471
     * @param AbstractTDBMObject $remoteBean
472
     */
473
    private function setManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
474
    {
475
        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
476
        $alterableResultIterator->add($remoteBean);
477
    }
478
479
    /**
480
     * Declares a relationship between this bean and the bean pointing to it.
481
     *
482
     * @param string             $tableName
483
     * @param string             $foreignKeyName
484
     * @param AbstractTDBMObject $remoteBean
485
     */
486
    private function removeManyToOneRelationship(string $tableName, string $foreignKeyName, AbstractTDBMObject $remoteBean)
487
    {
488
        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
489
        $alterableResultIterator->remove($remoteBean);
490
    }
491
492
    /**
493
     * Returns the list of objects linked to this bean via a given foreign key.
494
     *
495
     * @param string $tableName
496
     * @param string $foreignKeyName
497
     * @param string $searchTableName
498
     * @param array  $searchFilter
499
     * @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 !
500
     *
501
     * @return AlterableResultIterator
502
     */
503
    protected function retrieveManyToOneRelationshipsStorage(string $tableName, string $foreignKeyName, string $searchTableName, array $searchFilter, $orderString = null) : AlterableResultIterator
504
    {
505
        $key = $tableName.'___'.$foreignKeyName;
506
        $alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
507
        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->manyToOneRelationships[$key]) && $this->manyToOneRelationships[$key]->getUnderlyingResultIterator() !== null)) {
508
            return $alterableResultIterator;
509
        }
510
511
        $unalteredResultIterator = $this->tdbmService->findObjects($searchTableName, $searchFilter, [], $orderString);
512
513
        $alterableResultIterator->setResultIterator($unalteredResultIterator->getIterator());
514
515
        return $alterableResultIterator;
516
    }
517
518
    /**
519
     * Reverts any changes made to the object and resumes it to its DB state.
520
     * This can only be called on objects that come from database and that have not been deleted.
521
     * Otherwise, this will throw an exception.
522
     *
523
     * @throws TDBMException
524
     */
525
    public function discardChanges()
526
    {
527
        if ($this->status === TDBMObjectStateEnum::STATE_NEW || $this->status === TDBMObjectStateEnum::STATE_DETACHED) {
528
            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.");
529
        }
530
531
        if ($this->status === TDBMObjectStateEnum::STATE_DELETED) {
532
            throw new TDBMException('You cannot call discardChanges() on an object that has been deleted.');
533
        }
534
535
        $this->_setStatus(TDBMObjectStateEnum::STATE_NOT_LOADED);
536
    }
537
538
    /**
539
     * Method used internally by TDBM. You should not use it directly.
540
     * This method returns the status of the TDBMObject.
541
     * This is one of TDBMObjectStateEnum::STATE_NEW, TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DELETED.
542
     * $status = TDBMObjectStateEnum::STATE_NEW when a new object is created with DBMObject:getNewObject.
543
     * $status = TDBMObjectStateEnum::STATE_NOT_LOADED when the object has been retrieved with getObject but when no data has been accessed in it yet.
544
     * $status = TDBMObjectStateEnum::STATE_LOADED when the object is cached in memory.
545
     *
546
     * @return string
547
     */
548
    public function _getStatus()
549
    {
550
        return $this->status;
551
    }
552
553
    /**
554
     * Override the native php clone function for TDBMObjects.
555
     */
556
    public function __clone()
557
    {
558
        // Let's clone the many to many relationships
559
        if ($this->status === TDBMObjectStateEnum::STATE_DETACHED) {
560
            $pivotTableList = array_keys($this->relationships);
561
        } else {
562
            $pivotTableList = $this->tdbmService->_getPivotTablesLinkedToBean($this);
563
        }
564
565
        foreach ($pivotTableList as $pivotTable) {
566
            $storage = $this->retrieveRelationshipsStorage($pivotTable);
0 ignored issues
show
Unused Code introduced by
$storage is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
567
568
            // Let's duplicate the reverse side of the relationship // This is useless: already done by "retrieveRelationshipsStorage"!!!
569
            /*foreach ($storage as $remoteBean) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
570
                $metadata = $storage[$remoteBean];
571
572
                $remoteStorage = $remoteBean->getRelationshipStorage($pivotTable);
573
                $remoteStorage->attach($this, ['status' => $metadata['status'], 'reverse' => !$metadata['reverse']]);
574
            }*/
575
        }
576
577
        // Let's clone each row
578
        foreach ($this->dbRows as $key => &$dbRow) {
579
            $dbRow = clone $dbRow;
580
            $dbRow->setTDBMObject($this);
581
        }
582
583
        $this->manyToOneRelationships = [];
584
585
        // Let's set the status to new (to enter the save function)
586
        $this->status = TDBMObjectStateEnum::STATE_DETACHED;
587
    }
588
589
    /**
590
     * Returns raw database rows.
591
     *
592
     * @return DbRow[] Key: table name, Value: DbRow object
593
     */
594
    public function _getDbRows()
595
    {
596
        return $this->dbRows;
597
    }
598
599
    private function registerTable($tableName)
600
    {
601
        $dbRow = new DbRow($this, $tableName);
602
603
        if (in_array($this->status, [TDBMObjectStateEnum::STATE_NOT_LOADED, TDBMObjectStateEnum::STATE_LOADED, TDBMObjectStateEnum::STATE_DIRTY])) {
604
            // Let's get the primary key for the new table
605
            $anotherDbRow = array_values($this->dbRows)[0];
606
            /* @var $anotherDbRow DbRow */
607
            $indexedPrimaryKeys = array_values($anotherDbRow->_getPrimaryKeys());
608
            $primaryKeys = $this->tdbmService->_getPrimaryKeysFromIndexedPrimaryKeys($tableName, $indexedPrimaryKeys);
609
            $dbRow->_setPrimaryKeys($primaryKeys);
610
        }
611
612
        $dbRow->_setStatus($this->status);
613
614
        $this->dbRows[$tableName] = $dbRow;
615
        // TODO: look at status (if not new)=> get primary key from tdbmservice
616
    }
617
618
    /**
619
     * Internal function: return the list of relationships.
620
     *
621
     * @return \SplObjectStorage[]
622
     */
623
    public function _getCachedRelationships()
624
    {
625
        return $this->relationships;
626
    }
627
628
    /**
629
     * Returns an array of used tables by this bean (from parent to child relationship).
630
     *
631
     * @return string[]
632
     */
633
    abstract protected function getUsedTables();
634
635
    /**
636
     * Method called when the bean is removed from database.
637
     */
638
    protected function onDelete()
639
    {
640
    }
641
}
642