Skip to content

Commit

Permalink
[10.x] Introduce Observe attribute for models (#49843)
Browse files Browse the repository at this point in the history
* Introduce Observe attribute for models

* fix tests

* fix styling

* rename file

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
emargareten and taylorotwell authored Feb 9, 2024
1 parent 856d1bf commit 6c9cbc9
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 2 deletions.
19 changes: 19 additions & 0 deletions src/Illuminate/Database/Eloquent/Attributes/ObservedBy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Illuminate\Database\Eloquent\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
class ObservedBy
{
/**
* Create a new attribute instance.
*
* @param array|string $classes
* @return void
*/
public function __construct(array|string $classes)
{
}
}
27 changes: 27 additions & 0 deletions src/Illuminate/Database/Eloquent/Concerns/HasEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
namespace Illuminate\Database\Eloquent\Concerns;

use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Events\NullDispatcher;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use ReflectionClass;

trait HasEvents
{
Expand All @@ -27,6 +29,31 @@ trait HasEvents
*/
protected $observables = [];

/**
* Boot the has event trait for a model.
*
* @return void
*/
public static function bootHasEvents()
{
static::observe(static::resolveObserveAttributes());
}

/**
* Resolve the observe class names from the attributes.
*
* @return array
*/
public static function resolveObserveAttributes()
{
$reflectionClass = new ReflectionClass(static::class);

return collect($reflectionClass->getAttributes(ObservedBy::class))
->map(fn ($attribute) => $attribute->getArguments())
->flatten()
->all();
}

/**
* Register observers with the model.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User;
use Mockery as m;
use PHPUnit\Framework\TestCase;

class AuthListenersSendEmailVerificationNotificationHandleFunctionTest extends TestCase
Expand All @@ -29,8 +30,8 @@ public function testWillExecuted()
*/
public function testUserIsNotInstanceOfMustVerifyEmail()
{
$user = $this->getMockBuilder(User::class)->getMock();
$user->expects($this->never())->method('sendEmailVerificationNotification');
$user = m::mock(User::class);
$user->shouldNotReceive('sendEmailVerificationNotification');

$listener = new SendEmailVerificationNotification;

Expand Down
33 changes: 33 additions & 0 deletions tests/Database/DatabaseEloquentModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Illuminate\Database\Connection;
use Illuminate\Database\ConnectionResolverInterface;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\ArrayObject;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
Expand Down Expand Up @@ -1893,6 +1894,26 @@ public function testModelObserversCanBeAttachedToModelsThroughAnArray()
EloquentModelStub::flushEventListeners();
}

public function testModelObserversCanBeAttachedToModelsWithStringUsingAttribute()
{
EloquentModelWithObserveAttributeStub::setEventDispatcher($events = m::mock(Dispatcher::class));
$events->shouldReceive('dispatch');
$events->shouldReceive('listen')->once()->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeStub', EloquentTestObserverStub::class.'@creating');
$events->shouldReceive('listen')->once()->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeStub', EloquentTestObserverStub::class.'@saved');
$events->shouldReceive('forget');
EloquentModelWithObserveAttributeStub::flushEventListeners();
}

public function testModelObserversCanBeAttachedToModelsThroughAnArrayUsingAttribute()
{
EloquentModelWithObserveAttributeUsingArrayStub::setEventDispatcher($events = m::mock(Dispatcher::class));
$events->shouldReceive('dispatch');
$events->shouldReceive('listen')->once()->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeUsingArrayStub', EloquentTestObserverStub::class.'@creating');
$events->shouldReceive('listen')->once()->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeUsingArrayStub', EloquentTestObserverStub::class.'@saved');
$events->shouldReceive('forget');
EloquentModelWithObserveAttributeUsingArrayStub::flushEventListeners();
}

public function testThrowExceptionOnAttachingNotExistsModelObserverWithString()
{
$this->expectException(InvalidArgumentException::class);
Expand Down Expand Up @@ -3334,6 +3355,18 @@ public function uniqueIds()
}
}

#[ObservedBy(EloquentTestObserverStub::class)]
class EloquentModelWithObserveAttributeStub extends EloquentModelStub
{
//
}

#[ObservedBy([EloquentTestObserverStub::class])]
class EloquentModelWithObserveAttributeUsingArrayStub extends EloquentModelStub
{
//
}

class EloquentModelSavingEventStub
{
//
Expand Down

0 comments on commit 6c9cbc9

Please sign in to comment.