Completed
Push — master ( 39572a...fb3ec7 )
by Marco
14:02
created

testLifecycleCallbacksGetInherited()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM\Functional;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\ORM\Event\LifecycleEventArgs;
7
use Doctrine\ORM\Event\PreFlushEventArgs;
8
use Doctrine\ORM\Event\PreUpdateEventArgs;
9
use Doctrine\ORM\Query;
10
use Doctrine\Tests\OrmFunctionalTestCase;
11
12
class LifecycleCallbackTest extends OrmFunctionalTestCase
13
{
14 View Code Duplication
    protected function setUp()
15
    {
16
        parent::setUp();
17
        try {
18
            $this->_schemaTool->createSchema(
19
                [
20
                $this->_em->getClassMetadata(LifecycleCallbackEventArgEntity::class),
21
                $this->_em->getClassMetadata(LifecycleCallbackTestEntity::class),
22
                $this->_em->getClassMetadata(LifecycleCallbackTestUser::class),
23
                $this->_em->getClassMetadata(LifecycleCallbackCascader::class),
24
                ]
25
            );
26
        } catch (\Exception $e) {
27
            // Swallow all exceptions. We do not test the schema tool here.
28
        }
29
    }
30
31
    public function testPreSavePostSaveCallbacksAreInvoked()
32
    {
33
        $entity = new LifecycleCallbackTestEntity;
34
        $entity->value = 'hello';
35
        $this->_em->persist($entity);
36
        $this->_em->flush();
37
38
        $this->assertTrue($entity->prePersistCallbackInvoked);
39
        $this->assertTrue($entity->postPersistCallbackInvoked);
40
41
        $this->_em->clear();
42
43
        $query = $this->_em->createQuery("select e from Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity e");
44
        $result = $query->getResult();
45
        $this->assertTrue($result[0]->postLoadCallbackInvoked);
46
47
        $result[0]->value = 'hello again';
48
49
        $this->_em->flush();
50
51
        $this->assertEquals('changed from preUpdate callback!', $result[0]->value);
52
    }
53
54
    public function testPreFlushCallbacksAreInvoked()
55
    {
56
        $entity = new LifecycleCallbackTestEntity;
57
        $entity->value = 'hello';
58
        $this->_em->persist($entity);
59
60
        $this->_em->flush();
61
62
        $this->assertTrue($entity->prePersistCallbackInvoked);
63
        $this->assertTrue($entity->preFlushCallbackInvoked);
64
65
        $entity->preFlushCallbackInvoked = false;
66
        $this->_em->flush();
67
68
        $this->assertTrue($entity->preFlushCallbackInvoked);
69
70
        $entity->value = 'bye';
71
        $entity->preFlushCallbackInvoked = false;
72
        $this->_em->flush();
73
74
        $this->assertTrue($entity->preFlushCallbackInvoked);
75
    }
76
77
    public function testChangesDontGetLost()
78
    {
79
        $user = new LifecycleCallbackTestUser;
80
        $user->setName('Bob');
81
        $user->setValue('value');
82
        $this->_em->persist($user);
83
        $this->_em->flush();
84
85
        $user->setName('Alice');
86
        $this->_em->flush(); // Triggers preUpdate
87
88
        $this->_em->clear();
89
90
        $user2 = $this->_em->find(get_class($user), $user->getId());
91
92
        $this->assertEquals('Alice', $user2->getName());
93
        $this->assertEquals('Hello World', $user2->getValue());
94
    }
95
96
    /**
97
     * @group DDC-194
98
     */
99
    public function testGetReferenceWithPostLoadEventIsDelayedUntilProxyTrigger()
100
    {
101
        $entity = new LifecycleCallbackTestEntity;
102
        $entity->value = 'hello';
103
        $this->_em->persist($entity);
104
        $this->_em->flush();
105
        $id = $entity->getId();
106
107
        $this->_em->clear();
108
109
        $reference = $this->_em->getReference(LifecycleCallbackTestEntity::class, $id);
110
        $this->assertFalse($reference->postLoadCallbackInvoked);
111
112
        $reference->getValue(); // trigger proxy load
113
        $this->assertTrue($reference->postLoadCallbackInvoked);
114
    }
115
116
    /**
117
     * @group DDC-958
118
     */
119
    public function testPostLoadTriggeredOnRefresh()
120
    {
121
        $entity = new LifecycleCallbackTestEntity;
122
        $entity->value = 'hello';
123
        $this->_em->persist($entity);
124
        $this->_em->flush();
125
        $id = $entity->getId();
126
127
        $this->_em->clear();
128
129
        $reference = $this->_em->find(LifecycleCallbackTestEntity::class, $id);
130
        $this->assertTrue($reference->postLoadCallbackInvoked);
131
        $reference->postLoadCallbackInvoked = false;
132
133
        $this->_em->refresh($reference);
134
        $this->assertTrue($reference->postLoadCallbackInvoked, "postLoad should be invoked when refresh() is called.");
135
    }
136
137
    /**
138
     * @group DDC-113
139
     */
140
    public function testCascadedEntitiesCallsPrePersist()
141
    {
142
        //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% 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...
143
144
        $e1 = new LifecycleCallbackTestEntity;
145
        $e2 = new LifecycleCallbackTestEntity;
146
147
        $c = new LifecycleCallbackCascader();
148
        $this->_em->persist($c);
149
150
        $c->entities[] = $e1;
151
        $c->entities[] = $e2;
152
        $e1->cascader = $c;
153
        $e2->cascader = $c;
154
155
        //$this->_em->persist($c);
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% 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...
156
        $this->_em->flush();
157
158
        $this->assertTrue($e1->prePersistCallbackInvoked);
159
        $this->assertTrue($e2->prePersistCallbackInvoked);
160
    }
161
162
    /**
163
     * @group DDC-54
164
     * @group DDC-3005
165
     */
166
    public function testCascadedEntitiesLoadedInPostLoad()
167
    {
168
        $e1 = new LifecycleCallbackTestEntity();
169
        $e2 = new LifecycleCallbackTestEntity();
170
171
        $c = new LifecycleCallbackCascader();
172
        $this->_em->persist($c);
173
174
        $c->entities[] = $e1;
175
        $c->entities[] = $e2;
176
        $e1->cascader = $c;
177
        $e2->cascader = $c;
178
179
        $this->_em->flush();
180
        $this->_em->clear();
181
182
        $dql = <<<'DQL'
183
SELECT
184
    e, c
185
FROM
186
    Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e
187
LEFT JOIN
188
    e.cascader AS c
189
WHERE
190
    e.id IN (%s, %s)
191
DQL;
192
193
        $entities = $this
194
            ->_em
195
            ->createQuery(sprintf($dql, $e1->getId(), $e2->getId()))
196
            ->getResult();
197
198
        $this->assertTrue(current($entities)->postLoadCallbackInvoked);
199
        $this->assertTrue(current($entities)->postLoadCascaderNotNull);
200
        $this->assertTrue(current($entities)->cascader->postLoadCallbackInvoked);
201
        $this->assertEquals(current($entities)->cascader->postLoadEntitiesCount, 2);
202
    }
203
204
    /**
205
     * @group DDC-54
206
     * @group DDC-3005
207
     */
208
    public function testCascadedEntitiesNotLoadedInPostLoadDuringIteration()
209
    {
210
        $e1 = new LifecycleCallbackTestEntity();
211
        $e2 = new LifecycleCallbackTestEntity();
212
213
        $c = new LifecycleCallbackCascader();
214
        $this->_em->persist($c);
215
216
        $c->entities[] = $e1;
217
        $c->entities[] = $e2;
218
        $e1->cascader = $c;
219
        $e2->cascader = $c;
220
221
        $this->_em->flush();
222
        $this->_em->clear();
223
224
        $dql = <<<'DQL'
225
SELECT
226
    e, c
227
FROM
228
    Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e
229
LEFT JOIN
230
    e.cascader AS c
231
WHERE
232
    e.id IN (%s, %s)
233
DQL;
234
235
        $result = $this
236
            ->_em
237
            ->createQuery(sprintf($dql, $e1->getId(), $e2->getId()))
238
            ->iterate();
239
240 View Code Duplication
        foreach ($result as $entity) {
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...
241
            $this->assertTrue($entity[0]->postLoadCallbackInvoked);
242
            $this->assertFalse($entity[0]->postLoadCascaderNotNull);
243
244
            break;
245
        }
246
    }
247
    /**
248
     * @group DDC-54
249
     * @group DDC-3005
250
     */
251
    public function testCascadedEntitiesNotLoadedInPostLoadDuringIterationWithSimpleObjectHydrator()
252
    {
253
        $this->_em->persist(new LifecycleCallbackTestEntity());
254
        $this->_em->persist(new LifecycleCallbackTestEntity());
255
256
        $this->_em->flush();
257
        $this->_em->clear();
258
259
        $result = $this
260
            ->_em
261
            ->createQuery('SELECT e FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity AS e')
262
            ->iterate(null, Query::HYDRATE_SIMPLEOBJECT);
263
264 View Code Duplication
        foreach ($result as $entity) {
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...
265
            $this->assertTrue($entity[0]->postLoadCallbackInvoked);
266
            $this->assertFalse($entity[0]->postLoadCascaderNotNull);
267
268
            break;
269
        }
270
    }
271
    
272
    /**
273
     * https://github.com/doctrine/doctrine2/issues/6568
274
     */
275
    public function testPostLoadIsInvokedOnFetchJoinedEntities()
276
    {
277
        $entA = new LifecycleCallbackCascader();
278
        $this->_em->persist($entA);
279
        
280
        $entB_1 = new LifecycleCallbackTestEntity();
281
        $entB_2 = new LifecycleCallbackTestEntity();
282
283
        $entA->entities[] = $entB_1;
284
        $entA->entities[] = $entB_2;
285
        $entB_1->cascader = $entA;
286
        $entB_2->cascader = $entA;
287
288
        $this->_em->flush();
289
        $this->_em->clear();
290
291
        $dql = <<<'DQL'
292
SELECT
293
    entA, entB
294
FROM
295
    Doctrine\Tests\ORM\Functional\LifecycleCallbackCascader AS entA
296
LEFT JOIN
297
    entA.entities AS entB
298
WHERE
299
    entA.id = :entA_id
300
DQL;
301
302
        $fetchedA = $this
303
            ->_em
304
            ->createQuery($dql)->setParameter('entA_id', $entA->getId())
305
            ->getOneOrNullResult();
306
307
        $this->assertTrue($fetchedA->postLoadCallbackInvoked);
308
        foreach ($fetchedA->entities as $fetchJoinedEntB) {
309
            $this->assertTrue($fetchJoinedEntB->postLoadCallbackInvoked);
310
        }
311
    }
312
313
    public function testLifecycleCallbacksGetInherited()
314
    {
315
        $childMeta = $this->_em->getClassMetadata(LifecycleCallbackChildEntity::class);
316
        $this->assertEquals(['prePersist' => [0 => 'doStuff']], $childMeta->lifecycleCallbacks);
317
    }
318
319
    public function testLifecycleListener_ChangeUpdateChangeSet()
320
    {
321
        $listener = new LifecycleListenerPreUpdate;
322
        $this->_em->getEventManager()->addEventListener(['preUpdate'], $listener);
323
324
        $user = new LifecycleCallbackTestUser;
325
        $user->setName('Bob');
326
        $user->setValue('value');
327
        $this->_em->persist($user);
328
        $this->_em->flush();
329
        $this->_em->clear();
330
331
        $dql = "SELECT u FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestUser u WHERE u.name = 'Bob'";
332
        $bob = $this->_em->createQuery($dql)->getSingleResult();
333
        $bob->setName('Alice');
334
335
        $this->_em->flush(); // preUpdate reverts Alice to Bob
336
        $this->_em->clear();
337
338
        $this->_em->getEventManager()->removeEventListener(['preUpdate'], $listener);
339
340
        $bob = $this->_em->createQuery($dql)->getSingleResult();
341
342
        $this->assertEquals('Bob', $bob->getName());
343
    }
344
345
    /**
346
    * @group DDC-1955
347
    */
348
    public function testLifecycleCallbackEventArgs()
349
    {
350
        $e = new LifecycleCallbackEventArgEntity;
351
352
        $e->value = 'foo';
353
        $this->_em->persist($e);
354
        $this->_em->flush();
355
356
        $e->value = 'var';
357
        $this->_em->persist($e);
358
        $this->_em->flush();
359
360
        $this->_em->refresh($e);
361
362
        $this->_em->remove($e);
363
        $this->_em->flush();
364
365
366
        $this->assertArrayHasKey('preFlushHandler', $e->calls);
367
        $this->assertArrayHasKey('postLoadHandler', $e->calls);
368
        $this->assertArrayHasKey('prePersistHandler', $e->calls);
369
        $this->assertArrayHasKey('postPersistHandler', $e->calls);
370
        $this->assertArrayHasKey('preUpdateHandler', $e->calls);
371
        $this->assertArrayHasKey('postUpdateHandler', $e->calls);
372
        $this->assertArrayHasKey('preRemoveHandler', $e->calls);
373
        $this->assertArrayHasKey('postRemoveHandler', $e->calls);
374
375
        $this->assertInstanceOf(PreFlushEventArgs::class, $e->calls['preFlushHandler']);
376
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['postLoadHandler']);
377
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['prePersistHandler']);
378
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['postPersistHandler']);
379
        $this->assertInstanceOf(PreUpdateEventArgs::class, $e->calls['preUpdateHandler']);
380
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['postUpdateHandler']);
381
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['preRemoveHandler']);
382
        $this->assertInstanceOf(LifecycleEventArgs::class, $e->calls['postRemoveHandler']);
383
    }
384
}
385
386
/** @Entity @HasLifecycleCallbacks */
387
class LifecycleCallbackTestUser {
388
    /** @Id @Column(type="integer") @GeneratedValue */
389
    private $id;
390
    /** @Column(type="string") */
391
    private $value;
392
    /** @Column(type="string") */
393
    private $name;
394
    public function getId() {return $this->id;}
395
    public function getValue() {return $this->value;}
396
    public function setValue($value) {$this->value = $value;}
397
    public function getName() {return $this->name;}
398
    public function setName($name) {$this->name = $name;}
399
    /** @PreUpdate */
400
    public function testCallback() {$this->value = 'Hello World';}
401
}
402
403
/**
404
 * @Entity
405
 * @HasLifecycleCallbacks
406
 * @Table(name="lc_cb_test_entity")
407
 */
408
class LifecycleCallbackTestEntity
409
{
410
    /* test stuff */
411
    public $prePersistCallbackInvoked = false;
412
    public $postPersistCallbackInvoked = false;
413
    public $postLoadCallbackInvoked = false;
414
    public $postLoadCascaderNotNull = false;
415
    public $preFlushCallbackInvoked = false;
416
417
    /**
418
     * @Id @Column(type="integer")
419
     * @GeneratedValue(strategy="AUTO")
420
     */
421
    private $id;
422
    /**
423
     * @Column(type="string", nullable=true)
424
     */
425
    public $value;
426
427
    /**
428
     * @ManyToOne(targetEntity="LifecycleCallbackCascader")
429
     * @JoinColumn(name="cascader_id", referencedColumnName="id")
430
     */
431
    public $cascader;
432
433
    public function getId() {
434
        return $this->id;
435
    }
436
437
    public function getValue() {
438
        return $this->value;
439
    }
440
441
    /** @PrePersist */
442
    public function doStuffOnPrePersist() {
443
        $this->prePersistCallbackInvoked = true;
444
    }
445
446
    /** @PostPersist */
447
    public function doStuffOnPostPersist() {
448
        $this->postPersistCallbackInvoked = true;
449
    }
450
451
    /** @PostLoad */
452
    public function doStuffOnPostLoad() {
453
        $this->postLoadCallbackInvoked = true;
454
        $this->postLoadCascaderNotNull = isset($this->cascader);
455
    }
456
457
    /** @PreUpdate */
458
    public function doStuffOnPreUpdate() {
459
        $this->value = 'changed from preUpdate callback!';
460
    }
461
462
    /** @PreFlush */
463
    public function doStuffOnPreFlush() {
464
        $this->preFlushCallbackInvoked = true;
465
    }
466
}
467
468
/**
469
 * @Entity @HasLifecycleCallbacks
470
 * @Table(name="lc_cb_test_cascade")
471
 */
472
class LifecycleCallbackCascader
473
{
474
    /* test stuff */
475
    public $postLoadCallbackInvoked = false;
476
    public $postLoadEntitiesCount = 0;
477
478
    /**
479
     * @Id @Column(type="integer")
480
     * @GeneratedValue(strategy="AUTO")
481
     */
482
    private $id;
483
484
    /**
485
     * @OneToMany(targetEntity="LifecycleCallbackTestEntity", mappedBy="cascader", cascade={"persist"})
486
     */
487
    public $entities;
488
489
    public function __construct()
490
    {
491
        $this->entities = new ArrayCollection();
492
    }
493
494
    /** @PostLoad */
495
    public function doStuffOnPostLoad() {
496
        $this->postLoadCallbackInvoked = true;
497
        $this->postLoadEntitiesCount = count($this->entities);
498
    }
499
    
500
    public function getId() {
501
        return $this->id;
502
    }
503
}
504
505
/** @MappedSuperclass @HasLifecycleCallbacks */
506
class LifecycleCallbackParentEntity {
507
    /** @PrePersist */
508
    function doStuff() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
509
510
    }
511
}
512
513
/** @Entity @Table(name="lc_cb_childentity") */
514
class LifecycleCallbackChildEntity extends LifecycleCallbackParentEntity {
515
    /** @Id @Column(type="integer") @GeneratedValue */
516
    private $id;
0 ignored issues
show
Unused Code introduced by
The property $id is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
517
}
518
519
class LifecycleListenerPreUpdate
520
{
521
    public function preUpdate(PreUpdateEventArgs $eventArgs)
522
    {
523
        $eventArgs->setNewValue('name', 'Bob');
524
    }
525
}
526
527
/** @Entity @HasLifecycleCallbacks */
528
class LifecycleCallbackEventArgEntity
529
{
530
    /** @Id @Column(type="integer") @GeneratedValue */
531
    public $id;
532
533
    /** @Column() */
534
    public $value;
535
536
    public $calls = [];
537
538
    /**
539
     * @PostPersist
540
     */
541
    public function postPersistHandler(LifecycleEventArgs $event)
542
    {
543
        $this->calls[__FUNCTION__] = $event;
544
    }
545
546
    /**
547
     * @PrePersist
548
     */
549
    public function prePersistHandler(LifecycleEventArgs $event)
550
    {
551
        $this->calls[__FUNCTION__] = $event;
552
    }
553
554
    /**
555
     * @PostUpdate
556
     */
557
    public function postUpdateHandler(LifecycleEventArgs $event)
558
    {
559
        $this->calls[__FUNCTION__] = $event;
560
    }
561
562
    /**
563
     * @PreUpdate
564
     */
565
    public function preUpdateHandler(PreUpdateEventArgs $event)
566
    {
567
        $this->calls[__FUNCTION__] = $event;
568
    }
569
570
    /**
571
     * @PostRemove
572
     */
573
    public function postRemoveHandler(LifecycleEventArgs $event)
574
    {
575
        $this->calls[__FUNCTION__] = $event;
576
    }
577
578
    /**
579
     * @PreRemove
580
     */
581
    public function preRemoveHandler(LifecycleEventArgs $event)
582
    {
583
        $this->calls[__FUNCTION__] = $event;
584
    }
585
586
    /**
587
     * @PreFlush
588
     */
589
    public function preFlushHandler(PreFlushEventArgs $event)
590
    {
591
        $this->calls[__FUNCTION__] = $event;
592
    }
593
594
    /**
595
     * @PostLoad
596
     */
597
    public function postLoadHandler(LifecycleEventArgs $event)
598
    {
599
        $this->calls[__FUNCTION__] = $event;
600
    }
601
}
602