Skip to content

Commit

Permalink
Merge pull request #103 from siamak2/2.x
Browse files Browse the repository at this point in the history
[2.2.0] Added createdWithMetas, updatedWithMetas, savedWithMetas events.
  • Loading branch information
kodeine authored May 15, 2023
2 parents 2b90159 + a045c0d commit a07765c
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 1 deletion.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Release Notes

## v2.2.0

### Added

* Added `createdWithMetas`, `updatedWithMetas`, `savedWithMetas` events.

## v2.1.1

### Added

* Added Laravel 10.x Compatibility.

## v2.1.0

### Added
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ After that, you can listen for events the [same way](https://laravel.com/docs/ma

> If you `return false;` in listener of any event ending with `ing`, that operation will be aborted.
### Additional events

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.
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.
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.

There are 3 ways to listen for events:

#### 1. By Defining `$dispatchesEvents` Property
Expand Down
48 changes: 48 additions & 0 deletions src/Kodeine/Metable/Metable.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ trait Metable
{

protected $__metaData = null;
protected $__wasCreatedEventFired = false;
protected $__wasUpdatedEventFired = false;
protected $__wasSavedEventFired = false;

/**
* whereMeta scope for easier join
Expand Down Expand Up @@ -339,6 +342,21 @@ public function saveMeta() {
}
}
}

if ( $this->__wasCreatedEventFired ) {
$this->__wasCreatedEventFired = false;
$this->fireModelEvent( 'createdWithMetas', false );
}

if ( $this->__wasUpdatedEventFired ) {
$this->__wasUpdatedEventFired = false;
$this->fireModelEvent( 'updatedWithMetas', false );
}

if ( $this->__wasSavedEventFired ) {
$this->__wasSavedEventFired = false;
$this->fireModelEvent( 'savedWithMetas', false );
}
}

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

public static function bootMetable() {
static::saved( function ($model) {
$model->__wasSavedEventFired = true;
$model->saveMeta();
} );

static::created( function ($model) {
$model->__wasCreatedEventFired = true;
} );

static::updated( function ($model) {
$model->__wasUpdatedEventFired = true;
} );
}

protected function initializeMetable() {
$this->observables = array_merge( $this->observables, [
'createdWithMetas',
'updatedWithMetas',
'savedWithMetas',
] );
$this->observables = array_unique( $this->observables );
}

public static function createdWithMetas($callback) {
static::registerModelEvent( 'createdWithMetas', $callback );
}

public static function updatedWithMetas($callback) {
static::registerModelEvent( 'updatedWithMetas', $callback );
}

public static function savedWithMetas($callback) {
static::registerModelEvent( 'savedWithMetas', $callback );
}

public function __unset($key) {
Expand Down
2 changes: 1 addition & 1 deletion tests/Events/BaseEventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class BaseEventTest
public $model;
public $meta;

public function __construct(EventTest $model, $meta) {
public function __construct(EventTest $model, $meta = null) {
$this->model = $model;
$this->meta = $meta;
}
Expand Down
9 changes: 9 additions & 0 deletions tests/Events/CreatedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Kodeine\Metable\Tests\Events;


class CreatedWithMetasTestEvent extends BaseEventTest
{

}
8 changes: 8 additions & 0 deletions tests/Events/SavedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Kodeine\Metable\Tests\Events;

class SavedWithMetasTestEvent extends BaseEventTest
{

}
8 changes: 8 additions & 0 deletions tests/Events/UpdatedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Kodeine\Metable\Tests\Events;

class UpdatedWithMetasTestEvent extends BaseEventTest
{

}
135 changes: 135 additions & 0 deletions tests/HasMetaEventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
use Kodeine\Metable\Tests\Observers\EventObserver;
use Kodeine\Metable\Tests\Events\MetaDeletingTestEvent;
use Kodeine\Metable\Tests\Events\MetaCreatingTestEvent;
use Kodeine\Metable\Tests\Events\SavedWithMetasTestEvent;
use Kodeine\Metable\Tests\Events\CreatedWithMetasTestEvent;
use Kodeine\Metable\Tests\Events\UpdatedWithMetasTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleMetaSavedTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleMetaSavingTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleMetaCreatedTestEvent;
Expand All @@ -24,6 +27,9 @@
use Kodeine\Metable\Tests\Listeners\HandleMetaUpdatingTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleMetaDeletingTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleMetaCreatingTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleSavedWithMetasTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleCreatedWithMetasTestEvent;
use Kodeine\Metable\Tests\Listeners\HandleUpdatedWithMetasTestEvent;

class HasMetaEventsTest extends TestCase
{
Expand Down Expand Up @@ -86,6 +92,15 @@ public static function setUpBeforeClass(): void {
MetaDeletedTestEvent::class => [
HandleMetaDeletedTestEvent::class,
],
CreatedWithMetasTestEvent::class => [
HandleCreatedWithMetasTestEvent::class,
],
UpdatedWithMetasTestEvent::class => [
HandleUpdatedWithMetasTestEvent::class,
],
SavedWithMetasTestEvent::class => [
HandleSavedWithMetasTestEvent::class,
],
];
foreach ($listen as $event => $listeners) {
foreach ($listeners as $listener) {
Expand Down Expand Up @@ -467,4 +482,124 @@ public function testMetaDeletedEvent() {
$event->listenerShouldReturnFalse['metaDeleting'] = false;
$event->delete();
}

public function testCreatedWithMetasEvent() {
$eventName = 'createdWithMetas';
$event = new EventTest;

$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );

EventTest::creating( function () {
static $fired = false;//make sure event listener fired only once
if ( ! $fired ) {
$fired = true;
return false;
}
return true;
} );

$event->foo = 'bar';
$event->save();//the creating event in above should prevent model saving process

$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertFalse( $event->exists, "model should not be saved" );

$event->save();//the creating event in above should have no affect

$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );

$event->bar = 'bar';

$event->save();//model already created. so everything should be the same

$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );

$event->delete();
}

public function testUpdatedWithMetasEvent() {
$eventName = 'updatedWithMetas';
$event = new EventTest;

$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );

$event->foo = 'bar';
$event->save();

$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );

$event->name = 'foo';
$event->save();

$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );

EventTest::updating( function () {
static $fired = false;//make sure event listener fired only once
if ( ! $fired ) {
$fired = true;
return false;
}
return true;
} );

$event->name = 'bar';

$event->save();//the updating event in above should prevent model updating process

$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );

$event->delete();
}

public function testSavedWithMetasEvent() {
$eventName = 'savedWithMetas';
$event = new EventTest;

$this->assertContains( $eventName, $event->getObservableEvents(), "$eventName event should be observable" );

EventTest::saving( function () {
static $fired = false;//make sure event listener fired only once
if ( ! $fired ) {
$fired = true;
return false;
}
return true;
} );

$event->foo = 'bar';
$event->save();//the saving event in above should prevent model saving process

$this->assertEmpty( $event->listenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->observersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertEmpty( $event->classListenersChanges[$eventName] ?? [], "$eventName event should not be fired" );
$this->assertFalse( $event->exists, "model should not be saved" );

$event->save();//the saving event in above should have no affect

$this->assertCount( 1, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired only once" );
$this->assertCount( 1, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer only once" );
$this->assertCount( 1, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener only once" );

$event->name = 'foo';

$event->save();

$this->assertCount( 2, $event->listenersChanges[$eventName] ?? [], "$eventName event should be fired twice" );
$this->assertCount( 2, $event->observersChanges[$eventName] ?? [], "$eventName event should be fired by observer twice" );
$this->assertCount( 2, $event->classListenersChanges[$eventName] ?? [], "$eventName event should be fired by class listener twice" );

$event->delete();
}
}
8 changes: 8 additions & 0 deletions tests/Listeners/HandleCreatedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Kodeine\Metable\Tests\Listeners;

class HandleCreatedWithMetasTestEvent extends BaseListenerTest
{

}
8 changes: 8 additions & 0 deletions tests/Listeners/HandleSavedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Kodeine\Metable\Tests\Listeners;

class HandleSavedWithMetasTestEvent extends BaseListenerTest
{

}
8 changes: 8 additions & 0 deletions tests/Listeners/HandleUpdatedWithMetasTestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Kodeine\Metable\Tests\Listeners;

class HandleUpdatedWithMetasTestEvent extends BaseListenerTest
{

}
18 changes: 18 additions & 0 deletions tests/Models/EventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
use Kodeine\Metable\Tests\Events\MetaSavedTestEvent;
use Kodeine\Metable\Tests\Events\MetaSavingTestEvent;
use Kodeine\Metable\Tests\Events\MetaCreatedTestEvent;
use Kodeine\Metable\Tests\Events\SavedWithMetasTestEvent;
use Kodeine\Metable\Tests\Events\CreatedWithMetasTestEvent;
use Kodeine\Metable\Tests\Events\MetaUpdatedTestEvent;
use Kodeine\Metable\Tests\Events\MetaDeletedTestEvent;
use Kodeine\Metable\Tests\Events\MetaUpdatingTestEvent;
use Kodeine\Metable\Tests\Events\MetaDeletingTestEvent;
use Kodeine\Metable\Tests\Events\MetaCreatingTestEvent;
use Kodeine\Metable\Tests\Events\UpdatedWithMetasTestEvent;

class EventTest extends Model
{
Expand All @@ -35,6 +38,9 @@ class EventTest extends Model
'metaUpdated' => MetaUpdatedTestEvent::class,
'metaDeleting' => MetaDeletingTestEvent::class,
'metaDeleted' => MetaDeletedTestEvent::class,
'createdWithMetas' => CreatedWithMetasTestEvent::class,
'updatedWithMetas' => UpdatedWithMetasTestEvent::class,
'savedWithMetas' => SavedWithMetasTestEvent::class,
];

public static function boot() {
Expand Down Expand Up @@ -82,5 +88,17 @@ public static function boot() {
static::metaDeleted( function (EventTest $model, $meta) use ($listener) {
return $listener( $model, $meta, 'metaDeleted' );
} );

static::createdWithMetas( function (EventTest $model) use ($listener) {
return $listener( $model, null, 'createdWithMetas' );
} );

static::updatedWithMetas( function (EventTest $model) use ($listener) {
return $listener( $model, null, 'updatedWithMetas' );
} );

static::savedWithMetas( function (EventTest $model) use ($listener) {
return $listener( $model, null, 'savedWithMetas' );
} );
}
}
12 changes: 12 additions & 0 deletions tests/Observers/EventObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ public function metaDeleted(EventTest $model, $meta) {
return $this->genericObserver( $model, $meta, __FUNCTION__ );
}

public function createdWithMetas(EventTest $model) {
return $this->genericObserver( $model, null, __FUNCTION__ );
}

public function updatedWithMetas(EventTest $model) {
return $this->genericObserver( $model, null, __FUNCTION__ );
}

public function savedWithMetas(EventTest $model) {
return $this->genericObserver( $model, null, __FUNCTION__ );
}

protected function genericObserver(EventTest $model, $meta, $eventName) {
if ( ! isset( $model->observersChanges[$eventName] ) ) {
$model->observersChanges[$eventName] = [];
Expand Down

0 comments on commit a07765c

Please sign in to comment.