Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added listener for model. #420

Merged
merged 24 commits into from
Aug 23, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
97 changes: 90 additions & 7 deletions src/database/src/Model/Concerns/HasEvents.php
Expand Up @@ -12,22 +12,23 @@

namespace Hyperf\Database\Model\Concerns;

use Hyperf\Utils\Arr;
use Hyperf\Database\Model\Events\Saved;
use Hyperf\Database\Model\Events\Booted;
use Hyperf\Database\Model\Events\Saving;
use Hyperf\Database\Model\Events\Booting;
use Hyperf\Database\Model\Events\Created;
use Hyperf\Database\Model\Events\Creating;
use Hyperf\Database\Model\Events\Deleted;
use Hyperf\Database\Model\Events\Updated;
use Hyperf\Database\Model\Events\Creating;
use Hyperf\Database\Model\Events\Deleting;
use Hyperf\Database\Model\Events\ForceDeleted;
use Hyperf\Database\Model\Events\Restored;
use Hyperf\Database\Model\Events\Updating;
use Hyperf\Database\Model\Events\Restoring;
use Hyperf\Database\Model\Events\Retrieved;
use Hyperf\Database\Model\Events\Saved;
use Hyperf\Database\Model\Events\Saving;
use Hyperf\Database\Model\Events\Updated;
use Hyperf\Database\Model\Events\Updating;
use Psr\EventDispatcher\EventDispatcherInterface;
use Hyperf\Database\Model\Events\ForceDeleted;
use Psr\EventDispatcher\StoppableEventInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
* @method retrieved(Retrieved $event)
Expand All @@ -52,8 +53,61 @@ trait HasEvents
*/
protected $events = [];

/**
* User exposed observable events.
*
* These are extra user-defined events observers may subscribe to.
*
* @var array
*/
protected static $observables = [];

/**
* Register observers with the model.
*
* @param object|array|string $classes
* @return void
*/
public static function observe($classes): void
{
$instance = new static;

foreach (Arr::wrap($classes) as $class) {
$instance->registerObserver($class);
}
}

/**
* Clear all registered observers with the model.
*
* @return void
*/
public static function clearObservables(): void
{
static::$observables = [];
}

/**
* Register a single observer with the model.
*
* @param object|string $class
* @return void
*/
protected function registerObserver($class): void
huangzhhui marked this conversation as resolved.
Show resolved Hide resolved
{
$className = is_string($class) ? $class : get_class($class);

foreach ($this->getDefaultEvents() as $alias => $eventClass) {
if (method_exists($class, $alias)) {
static::$observables[static::class][$alias] = $class;
}
}
}

/**
* Set the user-defined event names.
*
* @return self
*/
public function setEvents(array $events): self
{
Expand All @@ -73,6 +127,7 @@ public function setEvents(array $events): self
* Add some observable event.
*
* @param array|string $events
* @return void
*/
public function addEvents($events): void
{
Expand All @@ -81,6 +136,9 @@ public function addEvents($events): void

/**
* Remove some registed event.
*
* @param array $events
huangzhhui marked this conversation as resolved.
Show resolved Hide resolved
* @return void
*/
public function removeEvents(array $events): void
{
Expand All @@ -105,8 +163,33 @@ public function getAvailableEvents(): array
return array_replace($this->getDefaultEvents(), $this->events);
}

/**
* Set observable mappings.
*
* @param array $observables
* @return self
*/
public function setObservables(array $observables): self
{
static::$observables[static::class] = $observables;

return $this;
}

/**
* Get observable mappings.
*
* @return array
*/
public function getObservables(): array
{
return static::$observables[static::class] ?? [];
}

/**
* Get the default events of Hyperf Database Model.
*
* @return array
*/
protected function getDefaultEvents(): array
{
Expand Down
17 changes: 13 additions & 4 deletions src/database/src/Model/Events/Event.php
Expand Up @@ -12,12 +12,12 @@

namespace Hyperf\Database\Model\Events;

use Hyperf\Database\Model\Model;
use Hyperf\Event\Stoppable;
use Psr\EventDispatcher\StoppableEventInterface;
use function class_basename;
use function lcfirst;
use Hyperf\Event\Stoppable;
use function method_exists;
use function class_basename;
use Hyperf\Database\Model\Model;
use Psr\EventDispatcher\StoppableEventInterface;

abstract class Event implements StoppableEventInterface
{
Expand Down Expand Up @@ -45,6 +45,10 @@ public function handle()
return $this->getModel()->{$this->getMethod()}($this);
}

if ($observerClass = $this->getObserverClass()) {
return make($observerClass)->{$this->getMethod()}($this->getModel());
huangzhhui marked this conversation as resolved.
Show resolved Hide resolved
}

return $this;
}

Expand All @@ -57,4 +61,9 @@ public function getModel(): Model
{
return $this->model;
}

public function getObserverClass(): ?string
{
return $this->getModel()->getObservables()[$this->getMethod()] ?? null;
}
}
148 changes: 112 additions & 36 deletions src/database/tests/ModelTest.php
Expand Up @@ -12,60 +12,65 @@

namespace HyperfTest\Database;

use Carbon\Carbon;
use Mockery;
use DateTime;
use stdClass;
use Exception;
use Carbon\Carbon;
use ReflectionClass;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;
use Hyperf\Database\ConnectionInterface;
use Hyperf\Database\ConnectionInterface as Connection;
use Hyperf\Database\ConnectionResolver;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Connectors\ConnectionFactory;
use Hyperf\Database\Connectors\MySqlConnector;
use Hyperf\Utils\Context;
use PHPUnit\Framework\TestCase;
use Hyperf\Database\Model\Model;
use Hyperf\Database\Model\Booted;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Collection;
use Hyperf\Database\Model\Events;
use Hyperf\Database\Model\Model;
use Hyperf\Event\EventDispatcher;
use Hyperf\Database\Model\Builder;
use Hyperf\Event\ListenerProvider;
use HyperfTest\Database\Stubs\User;
use Hyperf\Database\Model\Register;
use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Database\Model\Relations\Relation;
use Hyperf\Database\Query\Grammars\Grammar;
use Hyperf\Database\Query\Processors\Processor;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Collection as BaseCollection;
use Hyperf\Utils\Context;
use Hyperf\Utils\InteractsWithTime;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Database\Model\Collection;
use Psr\Container\ContainerInterface;
use Hyperf\Database\ConnectionResolver;
use Hyperf\Database\Model\Events\Event;
use HyperfTest\Database\Stubs\ModelStub;
use Hyperf\Database\ConnectionInterface;
use Hyperf\Database\Query\Grammars\Grammar;
use HyperfTest\Database\Stubs\DateModelStub;
use HyperfTest\Database\Stubs\DifferentConnectionModelStub;
use HyperfTest\Database\Stubs\ModelSaveStub;
use HyperfTest\Database\Stubs\ModelWithStub;
use HyperfTest\Database\Stubs\ModelCamelStub;
use Hyperf\Database\Model\Relations\Relation;
use Hyperf\Database\Connectors\MySqlConnector;
use Hyperf\Database\Model\Relations\BelongsTo;
use Hyperf\Utils\Collection as BaseCollection;
use HyperfTest\Database\Stubs\KeyTypeModelStub;
use HyperfTest\Database\Stubs\ModelAppendsStub;
use HyperfTest\Database\Stubs\ModelBootingTestStub;
use HyperfTest\Database\Stubs\ModelCamelStub;
use HyperfTest\Database\Stubs\ModelCastingStub;
use HyperfTest\Database\Stubs\ModelDestroyStub;
use HyperfTest\Database\Stubs\ModelDynamicHiddenStub;
use HyperfTest\Database\Stubs\ModelDynamicVisibleStub;
use Hyperf\Database\Query\Processors\Processor;
use HyperfTest\Database\Stubs\ModelObserverStub;
use Hyperf\Database\ConnectionResolverInterface;
use HyperfTest\Database\Stubs\ModelStubWithTrait;
use Hyperf\Database\Connectors\ConnectionFactory;
use HyperfTest\Database\Stubs\ModelBootingTestStub;
use HyperfTest\Database\Stubs\ModelEventObjectStub;
use HyperfTest\Database\Stubs\ModelFindWithWritePdoStub;
use HyperfTest\Database\Stubs\ModelGetMutatorsStub;
use HyperfTest\Database\Stubs\ModelNonIncrementingStub;
use HyperfTest\Database\Stubs\ModelSaveStub;
use HyperfTest\Database\Stubs\ModelSavingEventStub;
use HyperfTest\Database\Stubs\ModelStub;
use HyperfTest\Database\Stubs\ModelStubWithTrait;
use HyperfTest\Database\Stubs\ModelWithoutRelationStub;
use HyperfTest\Database\Stubs\ModelWithoutTableStub;
use HyperfTest\Database\Stubs\ModelWithStub;
use HyperfTest\Database\Stubs\NoConnectionModelStub;
use HyperfTest\Database\Stubs\User;
use Mockery;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use HyperfTest\Database\Stubs\ModelDynamicHiddenStub;
use HyperfTest\Database\Stubs\ModelEventListenerStub;
use HyperfTest\Database\Stubs\ModelDynamicVisibleStub;
use Hyperf\Database\ConnectionInterface as Connection;
use HyperfTest\Database\Stubs\ModelNonIncrementingStub;
use HyperfTest\Database\Stubs\ModelWithoutRelationStub;
use HyperfTest\Database\Stubs\ModelFindWithWritePdoStub;
use HyperfTest\Database\Stubs\DifferentConnectionModelStub;
use Psr\EventDispatcher\EventDispatcherInterface as Dispatcher;
use ReflectionClass;
use stdClass;

/**
* @internal
Expand Down Expand Up @@ -93,6 +98,8 @@ public function tearDown()
Carbon::resetToStringFormat();

Booted::$container = [];

Model::clearObservables();
}

public function testAttributeManipulation()
Expand Down Expand Up @@ -1836,6 +1843,75 @@ public function testWithoutTouchingOnCallback()
$this->assertTrue($called);
}

public function testSetObservables()
{
$model = new ModelStub(['id' => 1]);
$model->setObservables($observables = [
'created' => 'ObserverClass'
]);

$this->assertSame($observables, $model->getObservables());
}

public function testObserve()
{
ModelStub::observe(ModelObserverStub::class);

$model = new ModelStub(['id' => 1]);

$this->assertSame(['updating' => ModelObserverStub::class], $model->getObservables());
}

public function testClearObservables()
{
$model = new ModelStub(['id' => 1]);
$model->setObservables($observables = [
'created' => 'ObserverClass'
]);

ModelStub::clearObservables();

$this->assertSame([], $model->getObservables());
}

public function testGetObserverClassFromModelEvent()
{
$model = new ModelStub(['id' => 1]);
$model->setObservables($observables = [
'updating' => 'ObserverClass'
]);

$saving = new Events\Saving($model, 'saving');
$updating = new Events\Updating($model, 'updating');

$this->assertNull($saving->getObserverClass());
$this->assertSame('ObserverClass', $updating->getObserverClass());
}

public function testHandleModelObserver()
{
$listenerProvider = new ListenerProvider;
$listenerProvider->on(Event::class, [new ModelEventListenerStub, 'process']);

Register::setEventDispatcher(new EventDispatcher($listenerProvider));

$model = $this->getMockBuilder(ModelStub::class)->setMethods(['newModelQuery', 'updateTimestamps'])->getMock();
$query = Mockery::mock(Builder::class);
$query->shouldReceive('where')->once()->with('id', '=', 1);
$query->shouldReceive('update')->once()->with(['foo' => 'bar'])->andReturn(1);
$model->expects($this->once())->method('newModelQuery')->will($this->returnValue($query));
$model->expects($this->once())->method('updateTimestamps');

$model::observe(ModelObserverStub::class);

$model->id = 1;
$model->syncOriginal();
$model->foo = 'foo';
$model->exists = true;

$this->assertTrue($model->save());
}

public function testModelGenerate()
{
$this->getContainer();
Expand Down