Skip to content

Commit

Permalink
[8.x] More Convenient Model Broadcasting (#37491)
Browse files Browse the repository at this point in the history
* Initial commit of model broadcasting conveniences

* allow null return from broadcaston

* add broadcast methods

* Allow HasBroadcastChannel instances in routes

This allows HasBroadcastChannel instances to be passed to broadcast routes.

* Rename method

* Do not broadcast if no channels for model event

* add trait

* use model basename

* allow manual override of channels

* add test

* add test

* allow specification of connection, queue, afterCommit

* wip
  • Loading branch information
taylorotwell authored Jun 1, 2021
1 parent db89ad5 commit 71a83fb
Show file tree
Hide file tree
Showing 9 changed files with 404 additions and 6 deletions.
8 changes: 7 additions & 1 deletion src/Illuminate/Broadcasting/BroadcastEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,14 @@ public function handle(Broadcaster $broadcaster)
$name = method_exists($this->event, 'broadcastAs')
? $this->event->broadcastAs() : get_class($this->event);

$channels = Arr::wrap($this->event->broadcastOn());

if (empty($channels)) {
return;
}

$broadcaster->broadcast(
Arr::wrap($this->event->broadcastOn()), $name,
$channels, $name,
$this->getPayloadFromEvent($this->event)
);
}
Expand Down
9 changes: 8 additions & 1 deletion src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use Illuminate\Container\Container;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
use Illuminate\Contracts\Routing\BindingRegistrar;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Support\Arr;
Expand Down Expand Up @@ -40,13 +41,19 @@ abstract class Broadcaster implements BroadcasterContract
/**
* Register a channel authenticator.
*
* @param string $channel
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $channel
* @param callable|string $callback
* @param array $options
* @return $this
*/
public function channel($channel, $callback, $options = [])
{
if ($channel instanceof HasBroadcastChannel) {
$channel = $channel->broadcastChannelRoute();
} elseif (is_string($channel) && class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) {
$channel = (new $channel)->broadcastChannelRoute();
}

$this->channels[$channel] = $callback;

$this->channelOptions[$channel] = $options;
Expand Down
6 changes: 4 additions & 2 deletions src/Illuminate/Broadcasting/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Illuminate\Broadcasting;

use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;

class Channel
{
/**
Expand All @@ -14,12 +16,12 @@ class Channel
/**
* Create a new channel instance.
*
* @param string $name
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
* @return void
*/
public function __construct($name)
{
$this->name = $name;
$this->name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/Illuminate/Broadcasting/PrivateChannel.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

namespace Illuminate\Broadcasting;

use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;

class PrivateChannel extends Channel
{
/**
* Create a new channel instance.
*
* @param string $name
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
* @return void
*/
public function __construct($name)
{
$name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;

parent::__construct('private-'.$name);
}
}
20 changes: 20 additions & 0 deletions src/Illuminate/Contracts/Broadcasting/HasBroadcastChannel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Illuminate\Contracts\Broadcasting;

interface HasBroadcastChannel
{
/**
* Get the broadcast channel route definition that is associated with the given entity.
*
* @return string
*/
public function broadcastChannelRoute();

/**
* Get the broadcast channel name that is associated with the given entity.
*
* @return string
*/
public function broadcastChannel();
}
100 changes: 100 additions & 0 deletions src/Illuminate/Database/Eloquent/BroadcastableModelEventOccurred.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace Illuminate\Database\Eloquent;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class BroadcastableModelEventOccurred implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;

/**
* The model instance corresponding to the event.
*
* @var \Illuminate\Database\Eloquent\Model
*/
public $model;

/**
* The event name (created, updated, etc.).
*
* @var string
*/
protected $event;

/**
* The channels that the event should be broadcast on.
*
* @var array
*/
protected $channels = [];

/**
* The queue connection that should be used to queue the broadcast job.
*
* @var string
*/
public $connection;

/**
* The queue that should be used to queue the broadcast job.
*
* @var string
*/
public $queue;

/**
* Create a new event instance.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $event
* @return void
*/
public function __construct($model, $event)
{
$this->model = $model;
$this->event = $event;
}

/**
* The channels the event should broadcast on.
*
* @return array
*/
public function broadcastOn()
{
$channels = empty($this->channels)
? ($this->model->broadcastOn($this->event) ?: [])
: $this->channels;

return collect($channels)->map(function ($channel) {
return $channel instanceof Model ? new PrivateChannel($channel) : $channel;
})->all();
}

/**
* The name the event should broadcast as.
*
* @return string
*/
public function broadcastAs()
{
return class_basename($this->model).ucfirst($this->event);
}

/**
* Manually specify the channels the event should broadcast on.
*
* @param array $channels
* @return $this
*/
public function onChannels(array $channels)
{
$this->channels = $channels;

return $this;
}
}
182 changes: 182 additions & 0 deletions src/Illuminate/Database/Eloquent/BroadcastsEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?php

namespace Illuminate\Database\Eloquent;

use Illuminate\Support\Arr;

trait BroadcastsEvents
{
/**
* Boot the event broadcasting trait.
*
* @return void
*/
public static function bootBroadcastsEvents()
{
static::created(function ($model) {
$model->broadcastCreated();
});

static::updated(function ($model) {
$model->broadcastUpdated();
});

if (method_exists(static::class, 'bootSoftDeletes')) {
static::trashed(function ($model) {
$model->broadcastTrashed();
});

static::restored(function ($model) {
$model->broadcastRestored();
});
}

static::deleted(function ($model) {
$model->broadcastDeleted();
});
}

/**
* Broadcast that the model was created.
*
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function broadcastCreated($channels = null)
{
return $this->broadcastIfBroadcastChannelsExistForEvent(
$this->newBroadcastableModelEvent('created'), 'created', $channels
);
}

/**
* Broadcast that the model was updated.
*
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function broadcastUpdated($channels = null)
{
return $this->broadcastIfBroadcastChannelsExistForEvent(
$this->newBroadcastableModelEvent('updated'), 'updated', $channels
);
}

/**
* Broadcast that the model was trashed.
*
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function broadcastTrashed($channels = null)
{
return $this->broadcastIfBroadcastChannelsExistForEvent(
$this->newBroadcastableModelEvent('trashed'), 'trashed', $channels
);
}

/**
* Broadcast that the model was restored.
*
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function broadcastRestored($channels = null)
{
return $this->broadcastIfBroadcastChannelsExistForEvent(
$this->newBroadcastableModelEvent('restored'), 'restored', $channels
);
}

/**
* Broadcast that the model was deleted.
*
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function broadcastDeleted($channels = null)
{
return $this->broadcastIfBroadcastChannelsExistForEvent(
$this->newBroadcastableModelEvent('deleted'), 'deleted', $channels
);
}

/**
* Broadcast the given event instance if channels are configured for the model event.
*
* @param mixed $instance
* @param string $event
* @param mixed $channels
* @return \Illuminate\Broadcasting\PendingBroadcast|null
*/
protected function broadcastIfBroadcastChannelsExistForEvent($instance, $event, $channels = null)
{
if (! empty($this->broadcastOn($event)) || ! empty($channels)) {
return broadcast($instance->onChannels(Arr::wrap($channels)));
}
}

/**
* Create a new broadcastable model event event.
*
* @param string $event
* @return mixed
*/
public function newBroadcastableModelEvent($event)
{
return tap(new BroadcastableModelEventOccurred($this, $event), function ($event) {
$event->connection = property_exists($this, 'broadcastConnection')
? $this->broadcastConnection
: $this->broadcastConnection();

$event->queue = property_exists($this, 'broadcastQueue')
? $this->broadcastQueue
: $this->broadcastQueue();

$event->afterCommit = property_exists($this, 'broadcastAfterCommit')
? $this->broadcastAfterCommit
: $this->broadcastAfterCommit();
});
}

/**
* Get the channels that model events should broadcast on.
*
* @param string $event
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn($event)
{
return [$this];
}

/**
* Get the queue connection that should be used to broadcast model events.
*
* @return string|null
*/
public function broadcastConnection()
{
//
}

/**
* Get the queue that should be used to broadcast model events.
*
* @return string|null
*/
public function broadcastQueue()
{
//
}

/**
* Determine if the model event broadcast queued job should be dispatched after all transactions are committed.
*
* @return bool
*/
public function broadcastAfterCommit()
{
return false;
}
}
Loading

0 comments on commit 71a83fb

Please sign in to comment.