Skip to content

Commit 71a83fb

Browse files
authored
[8.x] More Convenient Model Broadcasting (#37491)
* 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
1 parent db89ad5 commit 71a83fb

File tree

9 files changed

+404
-6
lines changed

9 files changed

+404
-6
lines changed

src/Illuminate/Broadcasting/BroadcastEvent.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,14 @@ public function handle(Broadcaster $broadcaster)
6060
$name = method_exists($this->event, 'broadcastAs')
6161
? $this->event->broadcastAs() : get_class($this->event);
6262

63+
$channels = Arr::wrap($this->event->broadcastOn());
64+
65+
if (empty($channels)) {
66+
return;
67+
}
68+
6369
$broadcaster->broadcast(
64-
Arr::wrap($this->event->broadcastOn()), $name,
70+
$channels, $name,
6571
$this->getPayloadFromEvent($this->event)
6672
);
6773
}

src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use Illuminate\Container\Container;
77
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
8+
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
89
use Illuminate\Contracts\Routing\BindingRegistrar;
910
use Illuminate\Contracts\Routing\UrlRoutable;
1011
use Illuminate\Support\Arr;
@@ -40,13 +41,19 @@ abstract class Broadcaster implements BroadcasterContract
4041
/**
4142
* Register a channel authenticator.
4243
*
43-
* @param string $channel
44+
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $channel
4445
* @param callable|string $callback
4546
* @param array $options
4647
* @return $this
4748
*/
4849
public function channel($channel, $callback, $options = [])
4950
{
51+
if ($channel instanceof HasBroadcastChannel) {
52+
$channel = $channel->broadcastChannelRoute();
53+
} elseif (is_string($channel) && class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) {
54+
$channel = (new $channel)->broadcastChannelRoute();
55+
}
56+
5057
$this->channels[$channel] = $callback;
5158

5259
$this->channelOptions[$channel] = $options;

src/Illuminate/Broadcasting/Channel.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Illuminate\Broadcasting;
44

5+
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
6+
57
class Channel
68
{
79
/**
@@ -14,12 +16,12 @@ class Channel
1416
/**
1517
* Create a new channel instance.
1618
*
17-
* @param string $name
19+
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
1820
* @return void
1921
*/
2022
public function __construct($name)
2123
{
22-
$this->name = $name;
24+
$this->name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
2325
}
2426

2527
/**

src/Illuminate/Broadcasting/PrivateChannel.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
namespace Illuminate\Broadcasting;
44

5+
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
6+
57
class PrivateChannel extends Channel
68
{
79
/**
810
* Create a new channel instance.
911
*
10-
* @param string $name
12+
* @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $name
1113
* @return void
1214
*/
1315
public function __construct($name)
1416
{
17+
$name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
18+
1519
parent::__construct('private-'.$name);
1620
}
1721
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Illuminate\Contracts\Broadcasting;
4+
5+
interface HasBroadcastChannel
6+
{
7+
/**
8+
* Get the broadcast channel route definition that is associated with the given entity.
9+
*
10+
* @return string
11+
*/
12+
public function broadcastChannelRoute();
13+
14+
/**
15+
* Get the broadcast channel name that is associated with the given entity.
16+
*
17+
* @return string
18+
*/
19+
public function broadcastChannel();
20+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
use Illuminate\Broadcasting\InteractsWithSockets;
6+
use Illuminate\Broadcasting\PrivateChannel;
7+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
8+
use Illuminate\Queue\SerializesModels;
9+
10+
class BroadcastableModelEventOccurred implements ShouldBroadcast
11+
{
12+
use InteractsWithSockets, SerializesModels;
13+
14+
/**
15+
* The model instance corresponding to the event.
16+
*
17+
* @var \Illuminate\Database\Eloquent\Model
18+
*/
19+
public $model;
20+
21+
/**
22+
* The event name (created, updated, etc.).
23+
*
24+
* @var string
25+
*/
26+
protected $event;
27+
28+
/**
29+
* The channels that the event should be broadcast on.
30+
*
31+
* @var array
32+
*/
33+
protected $channels = [];
34+
35+
/**
36+
* The queue connection that should be used to queue the broadcast job.
37+
*
38+
* @var string
39+
*/
40+
public $connection;
41+
42+
/**
43+
* The queue that should be used to queue the broadcast job.
44+
*
45+
* @var string
46+
*/
47+
public $queue;
48+
49+
/**
50+
* Create a new event instance.
51+
*
52+
* @param \Illuminate\Database\Eloquent\Model $model
53+
* @param string $event
54+
* @return void
55+
*/
56+
public function __construct($model, $event)
57+
{
58+
$this->model = $model;
59+
$this->event = $event;
60+
}
61+
62+
/**
63+
* The channels the event should broadcast on.
64+
*
65+
* @return array
66+
*/
67+
public function broadcastOn()
68+
{
69+
$channels = empty($this->channels)
70+
? ($this->model->broadcastOn($this->event) ?: [])
71+
: $this->channels;
72+
73+
return collect($channels)->map(function ($channel) {
74+
return $channel instanceof Model ? new PrivateChannel($channel) : $channel;
75+
})->all();
76+
}
77+
78+
/**
79+
* The name the event should broadcast as.
80+
*
81+
* @return string
82+
*/
83+
public function broadcastAs()
84+
{
85+
return class_basename($this->model).ucfirst($this->event);
86+
}
87+
88+
/**
89+
* Manually specify the channels the event should broadcast on.
90+
*
91+
* @param array $channels
92+
* @return $this
93+
*/
94+
public function onChannels(array $channels)
95+
{
96+
$this->channels = $channels;
97+
98+
return $this;
99+
}
100+
}
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Eloquent;
4+
5+
use Illuminate\Support\Arr;
6+
7+
trait BroadcastsEvents
8+
{
9+
/**
10+
* Boot the event broadcasting trait.
11+
*
12+
* @return void
13+
*/
14+
public static function bootBroadcastsEvents()
15+
{
16+
static::created(function ($model) {
17+
$model->broadcastCreated();
18+
});
19+
20+
static::updated(function ($model) {
21+
$model->broadcastUpdated();
22+
});
23+
24+
if (method_exists(static::class, 'bootSoftDeletes')) {
25+
static::trashed(function ($model) {
26+
$model->broadcastTrashed();
27+
});
28+
29+
static::restored(function ($model) {
30+
$model->broadcastRestored();
31+
});
32+
}
33+
34+
static::deleted(function ($model) {
35+
$model->broadcastDeleted();
36+
});
37+
}
38+
39+
/**
40+
* Broadcast that the model was created.
41+
*
42+
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
43+
* @return \Illuminate\Broadcasting\PendingBroadcast
44+
*/
45+
public function broadcastCreated($channels = null)
46+
{
47+
return $this->broadcastIfBroadcastChannelsExistForEvent(
48+
$this->newBroadcastableModelEvent('created'), 'created', $channels
49+
);
50+
}
51+
52+
/**
53+
* Broadcast that the model was updated.
54+
*
55+
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
56+
* @return \Illuminate\Broadcasting\PendingBroadcast
57+
*/
58+
public function broadcastUpdated($channels = null)
59+
{
60+
return $this->broadcastIfBroadcastChannelsExistForEvent(
61+
$this->newBroadcastableModelEvent('updated'), 'updated', $channels
62+
);
63+
}
64+
65+
/**
66+
* Broadcast that the model was trashed.
67+
*
68+
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
69+
* @return \Illuminate\Broadcasting\PendingBroadcast
70+
*/
71+
public function broadcastTrashed($channels = null)
72+
{
73+
return $this->broadcastIfBroadcastChannelsExistForEvent(
74+
$this->newBroadcastableModelEvent('trashed'), 'trashed', $channels
75+
);
76+
}
77+
78+
/**
79+
* Broadcast that the model was restored.
80+
*
81+
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
82+
* @return \Illuminate\Broadcasting\PendingBroadcast
83+
*/
84+
public function broadcastRestored($channels = null)
85+
{
86+
return $this->broadcastIfBroadcastChannelsExistForEvent(
87+
$this->newBroadcastableModelEvent('restored'), 'restored', $channels
88+
);
89+
}
90+
91+
/**
92+
* Broadcast that the model was deleted.
93+
*
94+
* @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
95+
* @return \Illuminate\Broadcasting\PendingBroadcast
96+
*/
97+
public function broadcastDeleted($channels = null)
98+
{
99+
return $this->broadcastIfBroadcastChannelsExistForEvent(
100+
$this->newBroadcastableModelEvent('deleted'), 'deleted', $channels
101+
);
102+
}
103+
104+
/**
105+
* Broadcast the given event instance if channels are configured for the model event.
106+
*
107+
* @param mixed $instance
108+
* @param string $event
109+
* @param mixed $channels
110+
* @return \Illuminate\Broadcasting\PendingBroadcast|null
111+
*/
112+
protected function broadcastIfBroadcastChannelsExistForEvent($instance, $event, $channels = null)
113+
{
114+
if (! empty($this->broadcastOn($event)) || ! empty($channels)) {
115+
return broadcast($instance->onChannels(Arr::wrap($channels)));
116+
}
117+
}
118+
119+
/**
120+
* Create a new broadcastable model event event.
121+
*
122+
* @param string $event
123+
* @return mixed
124+
*/
125+
public function newBroadcastableModelEvent($event)
126+
{
127+
return tap(new BroadcastableModelEventOccurred($this, $event), function ($event) {
128+
$event->connection = property_exists($this, 'broadcastConnection')
129+
? $this->broadcastConnection
130+
: $this->broadcastConnection();
131+
132+
$event->queue = property_exists($this, 'broadcastQueue')
133+
? $this->broadcastQueue
134+
: $this->broadcastQueue();
135+
136+
$event->afterCommit = property_exists($this, 'broadcastAfterCommit')
137+
? $this->broadcastAfterCommit
138+
: $this->broadcastAfterCommit();
139+
});
140+
}
141+
142+
/**
143+
* Get the channels that model events should broadcast on.
144+
*
145+
* @param string $event
146+
* @return \Illuminate\Broadcasting\Channel|array
147+
*/
148+
public function broadcastOn($event)
149+
{
150+
return [$this];
151+
}
152+
153+
/**
154+
* Get the queue connection that should be used to broadcast model events.
155+
*
156+
* @return string|null
157+
*/
158+
public function broadcastConnection()
159+
{
160+
//
161+
}
162+
163+
/**
164+
* Get the queue that should be used to broadcast model events.
165+
*
166+
* @return string|null
167+
*/
168+
public function broadcastQueue()
169+
{
170+
//
171+
}
172+
173+
/**
174+
* Determine if the model event broadcast queued job should be dispatched after all transactions are committed.
175+
*
176+
* @return bool
177+
*/
178+
public function broadcastAfterCommit()
179+
{
180+
return false;
181+
}
182+
}

0 commit comments

Comments
 (0)