[RFC] Decouple Broadcasting from Models
tonysm opened this issue · 0 comments
I want to change the broadcasting feature to allow broadcasting independent of Eloquent models.
Right now, broadcasting is only available from a model that uses the Broadcasts
trait:
class Comment extends Model
{
use Broadcasts;
}
// Then:
$comment->broadcastAppendTo($comment->post)
->toOthers()
->later();
We could decouple broadcasting Turbo Streams from models and change the modal broadcasting system to use this new way.
An example API would be:
// Using the Facade:
Turbo::broadcastAppend(
channel: "general",
target: "notifications",
content: view('layouts.notification', ['message' => __('Hey users!')]),
)->toOthers();
This assumes your application has a public broadcasting channel called general
. Since this would return a pending broadcast builder class, we could also chain the channel like so:
Turbo::broadcastAppend(/* ... */)
->channel('general', type: 'public')
->toOthers();
In the builder we could have some helper for private/presence channels:
Turbo::broadcastAppend(/* ... */)
->privateChannel('general')
->toOthers()
Turbo::broadcastAppend(/* ... */)
->presenceChannel('general')
->toOthers()
Changing The Broadcasting
With this new abstraction in place, we could change the Broadcasts
trait to use this new system. I think only this method would have to change, which is where we return the PendingBroadcast
class. This would use the Turbo Facade. We could even return the same response type, making this a non-breaking change.
Testing
Our current way of testing broadcasting is by faking the BroadcastAction
job, as described in the docs.
Since the new broadcast system would use the facade, we could improve testing! Introducing a Broadcasting manager, we could then offer the ability to run Turbo::fakeBroadcasting()
or something. Then we could allow asserting on what was broadcasted instead of asserting the Broadcast job was dispatched.
Think something like this:
use App\Models\Todo;
use Tonysm\TurboLaravel\Facades\TurboStream;
class CreatesCommentsTest extends TestCase
{
/** @test */
public function creates_comments()
{
TurboStream::fake();
$todo = Todo::factory()->create();
$this->turbo()->post(route('todos.comments.store', $todo), [
'content' => 'Hey, this is really nice!',
])->assertTurboStream();
TurboStream::assertBroadcasted(function ($broadcast) use ($todo) {
return count($broadcast->channels) === 1
&& $broadcast->channels[0]->name === sprintf('private-%s', $todo->broadcastChannel())
&& $broadcast->target === 'comments'
&& $broadcast->action === 'append'
&& $broadcast->view === 'comments._comment'
&& $broadcast->see('Hey, this is really nice!')
});
}
}