Skip to content

Commit a07765c

Browse files
authored
Merge pull request #103 from siamak2/2.x
[2.2.0] Added createdWithMetas, updatedWithMetas, savedWithMetas events.
2 parents 2b90159 + a045c0d commit a07765c

13 files changed

+281
-1
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Release Notes
22

3+
## v2.2.0
4+
5+
### Added
6+
7+
* Added `createdWithMetas`, `updatedWithMetas`, `savedWithMetas` events.
8+
9+
## v2.1.1
10+
11+
### Added
12+
13+
* Added Laravel 10.x Compatibility.
14+
315
## v2.1.0
416

517
### Added

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ After that, you can listen for events the [same way](https://laravel.com/docs/ma
430430

431431
> If you `return false;` in listener of any event ending with `ing`, that operation will be aborted.
432432
433+
### Additional events
434+
435+
There are some additional events that extend existing laravel events. These events don't need `HasMetaEvents` trait and like default laravel events, the event listeners should expect one parameter.
436+
Event names: `createdWithMetas`, `updatedWithMetas`, `savedWithMetas`. These events fire exactly like default laravel events except that they are only fired after all metas saved to database.
437+
For example, you may need to access metas inside a queue job after model created. But because metas have not been saved to database yet (metas will be saved to database in `saved` event and this event has not been fired yet), job can't access them. by using `createdWithMetas` event instead of `created` event, the problem will be solved.
438+
433439
There are 3 ways to listen for events:
434440

435441
#### 1. By Defining `$dispatchesEvents` Property

src/Kodeine/Metable/Metable.php

+48
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ trait Metable
1414
{
1515

1616
protected $__metaData = null;
17+
protected $__wasCreatedEventFired = false;
18+
protected $__wasUpdatedEventFired = false;
19+
protected $__wasSavedEventFired = false;
1720

1821
/**
1922
* whereMeta scope for easier join
@@ -339,6 +342,21 @@ public function saveMeta() {
339342
}
340343
}
341344
}
345+
346+
if ( $this->__wasCreatedEventFired ) {
347+
$this->__wasCreatedEventFired = false;
348+
$this->fireModelEvent( 'createdWithMetas', false );
349+
}
350+
351+
if ( $this->__wasUpdatedEventFired ) {
352+
$this->__wasUpdatedEventFired = false;
353+
$this->fireModelEvent( 'updatedWithMetas', false );
354+
}
355+
356+
if ( $this->__wasSavedEventFired ) {
357+
$this->__wasSavedEventFired = false;
358+
$this->fireModelEvent( 'savedWithMetas', false );
359+
}
342360
}
343361

344362
protected function fireMetaEvent($event, $metaName, bool $halt = true) {
@@ -527,8 +545,38 @@ public function hasColumn($column): bool {
527545

528546
public static function bootMetable() {
529547
static::saved( function ($model) {
548+
$model->__wasSavedEventFired = true;
530549
$model->saveMeta();
531550
} );
551+
552+
static::created( function ($model) {
553+
$model->__wasCreatedEventFired = true;
554+
} );
555+
556+
static::updated( function ($model) {
557+
$model->__wasUpdatedEventFired = true;
558+
} );
559+
}
560+
561+
protected function initializeMetable() {
562+
$this->observables = array_merge( $this->observables, [
563+
'createdWithMetas',
564+
'updatedWithMetas',
565+
'savedWithMetas',
566+
] );
567+
$this->observables = array_unique( $this->observables );
568+
}
569+
570+
public static function createdWithMetas($callback) {
571+
static::registerModelEvent( 'createdWithMetas', $callback );
572+
}
573+
574+
public static function updatedWithMetas($callback) {
575+
static::registerModelEvent( 'updatedWithMetas', $callback );
576+
}
577+
578+
public static function savedWithMetas($callback) {
579+
static::registerModelEvent( 'savedWithMetas', $callback );
532580
}
533581

534582
public function __unset($key) {

tests/Events/BaseEventTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class BaseEventTest
1212
public $model;
1313
public $meta;
1414

15-
public function __construct(EventTest $model, $meta) {
15+
public function __construct(EventTest $model, $meta = null) {
1616
$this->model = $model;
1717
$this->meta = $meta;
1818
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Events;
4+
5+
6+
class CreatedWithMetasTestEvent extends BaseEventTest
7+
{
8+
9+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Events;
4+
5+
class SavedWithMetasTestEvent extends BaseEventTest
6+
{
7+
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Events;
4+
5+
class UpdatedWithMetasTestEvent extends BaseEventTest
6+
{
7+
8+
}

tests/HasMetaEventsTest.php

+135
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
use Kodeine\Metable\Tests\Observers\EventObserver;
1717
use Kodeine\Metable\Tests\Events\MetaDeletingTestEvent;
1818
use Kodeine\Metable\Tests\Events\MetaCreatingTestEvent;
19+
use Kodeine\Metable\Tests\Events\SavedWithMetasTestEvent;
20+
use Kodeine\Metable\Tests\Events\CreatedWithMetasTestEvent;
21+
use Kodeine\Metable\Tests\Events\UpdatedWithMetasTestEvent;
1922
use Kodeine\Metable\Tests\Listeners\HandleMetaSavedTestEvent;
2023
use Kodeine\Metable\Tests\Listeners\HandleMetaSavingTestEvent;
2124
use Kodeine\Metable\Tests\Listeners\HandleMetaCreatedTestEvent;
@@ -24,6 +27,9 @@
2427
use Kodeine\Metable\Tests\Listeners\HandleMetaUpdatingTestEvent;
2528
use Kodeine\Metable\Tests\Listeners\HandleMetaDeletingTestEvent;
2629
use Kodeine\Metable\Tests\Listeners\HandleMetaCreatingTestEvent;
30+
use Kodeine\Metable\Tests\Listeners\HandleSavedWithMetasTestEvent;
31+
use Kodeine\Metable\Tests\Listeners\HandleCreatedWithMetasTestEvent;
32+
use Kodeine\Metable\Tests\Listeners\HandleUpdatedWithMetasTestEvent;
2733

2834
class HasMetaEventsTest extends TestCase
2935
{
@@ -86,6 +92,15 @@ public static function setUpBeforeClass(): void {
8692
MetaDeletedTestEvent::class => [
8793
HandleMetaDeletedTestEvent::class,
8894
],
95+
CreatedWithMetasTestEvent::class => [
96+
HandleCreatedWithMetasTestEvent::class,
97+
],
98+
UpdatedWithMetasTestEvent::class => [
99+
HandleUpdatedWithMetasTestEvent::class,
100+
],
101+
SavedWithMetasTestEvent::class => [
102+
HandleSavedWithMetasTestEvent::class,
103+
],
89104
];
90105
foreach ($listen as $event => $listeners) {
91106
foreach ($listeners as $listener) {
@@ -467,4 +482,124 @@ public function testMetaDeletedEvent() {
467482
$event->listenerShouldReturnFalse['metaDeleting'] = false;
468483
$event->delete();
469484
}
485+
486+
public function testCreatedWithMetasEvent() {
487+
$eventName = 'createdWithMetas';
488+
$event = new EventTest;
489+
490+
$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );
491+
492+
EventTest::creating( function () {
493+
static $fired = false;//make sure event listener fired only once
494+
if ( ! $fired ) {
495+
$fired = true;
496+
return false;
497+
}
498+
return true;
499+
} );
500+
501+
$event->foo = 'bar';
502+
$event->save();//the creating event in above should prevent model saving process
503+
504+
$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
505+
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
506+
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
507+
$this->assertFalse( $event->exists, "model should not be saved" );
508+
509+
$event->save();//the creating event in above should have no affect
510+
511+
$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
512+
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
513+
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );
514+
515+
$event->bar = 'bar';
516+
517+
$event->save();//model already created. so everything should be the same
518+
519+
$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
520+
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
521+
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );
522+
523+
$event->delete();
524+
}
525+
526+
public function testUpdatedWithMetasEvent() {
527+
$eventName = 'updatedWithMetas';
528+
$event = new EventTest;
529+
530+
$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );
531+
532+
$event->foo = 'bar';
533+
$event->save();
534+
535+
$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
536+
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
537+
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
538+
539+
$event->name = 'foo';
540+
$event->save();
541+
542+
$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
543+
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
544+
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );
545+
546+
EventTest::updating( function () {
547+
static $fired = false;//make sure event listener fired only once
548+
if ( ! $fired ) {
549+
$fired = true;
550+
return false;
551+
}
552+
return true;
553+
} );
554+
555+
$event->name = 'bar';
556+
557+
$event->save();//the updating event in above should prevent model updating process
558+
559+
$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
560+
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
561+
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );
562+
563+
$event->delete();
564+
}
565+
566+
public function testSavedWithMetasEvent() {
567+
$eventName = 'savedWithMetas';
568+
$event = new EventTest;
569+
570+
$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );
571+
572+
EventTest::saving( function () {
573+
static $fired = false;//make sure event listener fired only once
574+
if ( ! $fired ) {
575+
$fired = true;
576+
return false;
577+
}
578+
return true;
579+
} );
580+
581+
$event->foo = 'bar';
582+
$event->save();//the saving event in above should prevent model saving process
583+
584+
$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
585+
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
586+
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
587+
$this->assertFalse( $event->exists, "model should not be saved" );
588+
589+
$event->save();//the saving event in above should have no affect
590+
591+
$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
592+
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
593+
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );
594+
595+
$event->name = 'foo';
596+
597+
$event->save();
598+
599+
$this->assertCount( 2, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired twice" );
600+
$this->assertCount( 2, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer twice" );
601+
$this->assertCount( 2, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener twice" );
602+
603+
$event->delete();
604+
}
470605
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Listeners;
4+
5+
class HandleCreatedWithMetasTestEvent extends BaseListenerTest
6+
{
7+
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Listeners;
4+
5+
class HandleSavedWithMetasTestEvent extends BaseListenerTest
6+
{
7+
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Kodeine\Metable\Tests\Listeners;
4+
5+
class HandleUpdatedWithMetasTestEvent extends BaseListenerTest
6+
{
7+
8+
}

tests/Models/EventTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
use Kodeine\Metable\Tests\Events\MetaSavedTestEvent;
1010
use Kodeine\Metable\Tests\Events\MetaSavingTestEvent;
1111
use Kodeine\Metable\Tests\Events\MetaCreatedTestEvent;
12+
use Kodeine\Metable\Tests\Events\SavedWithMetasTestEvent;
13+
use Kodeine\Metable\Tests\Events\CreatedWithMetasTestEvent;
1214
use Kodeine\Metable\Tests\Events\MetaUpdatedTestEvent;
1315
use Kodeine\Metable\Tests\Events\MetaDeletedTestEvent;
1416
use Kodeine\Metable\Tests\Events\MetaUpdatingTestEvent;
1517
use Kodeine\Metable\Tests\Events\MetaDeletingTestEvent;
1618
use Kodeine\Metable\Tests\Events\MetaCreatingTestEvent;
19+
use Kodeine\Metable\Tests\Events\UpdatedWithMetasTestEvent;
1720

1821
class EventTest extends Model
1922
{
@@ -35,6 +38,9 @@ class EventTest extends Model
3538
'metaUpdated' => MetaUpdatedTestEvent::class,
3639
'metaDeleting' => MetaDeletingTestEvent::class,
3740
'metaDeleted' => MetaDeletedTestEvent::class,
41+
'createdWithMetas' => CreatedWithMetasTestEvent::class,
42+
'updatedWithMetas' => UpdatedWithMetasTestEvent::class,
43+
'savedWithMetas' => SavedWithMetasTestEvent::class,
3844
];
3945

4046
public static function boot() {
@@ -82,5 +88,17 @@ public static function boot() {
8288
static::metaDeleted( function (EventTest $model, $meta) use ($listener) {
8389
return $listener( $model, $meta, 'metaDeleted' );
8490
} );
91+
92+
static::createdWithMetas( function (EventTest $model) use ($listener) {
93+
return $listener( $model, null, 'createdWithMetas' );
94+
} );
95+
96+
static::updatedWithMetas( function (EventTest $model) use ($listener) {
97+
return $listener( $model, null, 'updatedWithMetas' );
98+
} );
99+
100+
static::savedWithMetas( function (EventTest $model) use ($listener) {
101+
return $listener( $model, null, 'savedWithMetas' );
102+
} );
85103
}
86104
}

tests/Observers/EventObserver.php

+12
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ public function metaDeleted(EventTest $model, $meta) {
3838
return $this->genericObserver( $model, $meta, __FUNCTION__ );
3939
}
4040

41+
public function createdWithMetas(EventTest $model) {
42+
return $this->genericObserver( $model, null, __FUNCTION__ );
43+
}
44+
45+
public function updatedWithMetas(EventTest $model) {
46+
return $this->genericObserver( $model, null, __FUNCTION__ );
47+
}
48+
49+
public function savedWithMetas(EventTest $model) {
50+
return $this->genericObserver( $model, null, __FUNCTION__ );
51+
}
52+
4153
protected function genericObserver(EventTest $model, $meta, $eventName) {
4254
if ( ! isset( $model->observersChanges[$eventName] ) ) {
4355
$model->observersChanges[$eventName] = [];

0 commit comments

Comments
 (0)