I am kinda stuck in broadcast routes. i setup a socket server with redis and configured it with Laravel. For public channel ,everything is working fine but when it comes to private or presence channel, it is somehow bypassing laravel broadcast routes. Can't figured out how & why.
i have attached a repo link so you guys can explore it too. Plus some quick bits are also below.
https://github.com/bilahdsid/socket-laravel/tree/socket
TestEvent.php
class TestEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets;
/**
* Create a new event instance.
*
* #return void
*/
public $data;
public function __construct()
{
$this->data = array(
'power'=> '10'
);
}
public function broadcastOn()
{
return new PrivateChannel('test-channel1');
}
public function broadcastWith()
{
return $this->data;
}
}
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('private-test-channel1', function(err, count) {
console.log(err);
});
redis.on('connection',function (socket,channel) {
console.log(socket+''|+channel);
});
redis.on('message', function(channel, message) {
console.log('Message Recieved: ' + message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
io.on('connection', function(socket){
console.log('a user connected');
});
routes/web-- for firing
Route::get('/', function () {
return view('home');
});
Route::get('fire', function () {
// this fires the event
broadcast(new App\Events\TestEvent());
return "event fired";
});
routes/channel.php -- below line doesn't work-- main issue
Broadcast::channel('private-test-channel', function ($user, $id) {
echo '1111'; exit;
return (int) $user->id === (int) $id;
});
Thanks.
As far as I can see you are defining a channel with the name: test-channel1:
public function broadcastOn()
{
return new PrivateChannel('test-channel1');
}
but in routes/channels.php:
Broadcast::channel('private-test-channel', function ($user, $id) {
Sounds like a typo!
Related
I'm trying to follow this example code https://laravel.com/docs/7.x/broadcasting#using-example-application
I already made a controller with this code:
public function send()
{
$orderId = 5;
broadcast(new TestEvento($orderId));
return view('test.send');
}
public function receive()
{
$orderId = 5;
return view('test.receive', compact('orderId'));
}
An event with this code:
public $orderId;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($orderId)
{
$this->orderId = $orderId;
Log::debug('Construct orderId: '.$orderId);
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
Log::debug('broadcastOn: '.$this->orderId);
return new PrivateChannel('canaltest.'.$this->orderId);
}
A route:
Broadcast::channel('canaltest.{var}', function ($user, $var) {
return true; //temp workaround
return $user->id === Order::findOrNew($var)->user_id;
});
A view with this:
<div id="app">
<test :orderId="{{$orderId}}"></test>
</div>
Added this line to app.js:
Vue.component('test', require('./components/Test.vue').default);
And a .vue with
<script>
export default {
mounted() {
console.log('Component mounted.');
}
}
Echo.private('canaltest.${orderId}')
.listen('TestEvento', (e) => {
console.log(e);
});
</script>
The problem is I just can't find a way to get the js to get the orderId var and there's nowhere in vue documentation that says where should I place the .props array when requiring a component in app.js. So how can I get my test.vue component to read the orderId var?
I believe what you're looking for as below for test.vue
<script>
export default {
props: {
orderId: {
type: [Number, String],
required: true
},
},
mounted() {
console.log('Component mounted.');
}
}
</script>
!!!
Server - socket How can change channal dynamic
When I broadcast using /fire/1 for example, I only want to send to /room/1.
Currently it sends to /room/1, /room/2, /room/3, etc. Because by default here, everything on the server is subscribed to 'test-channel'. I just can't figure this out.
var server = require('http').Server();
var io = require('socket.io')(server);
var Redis = require('ioredis');
var redis = new Redis();
io.on('connection', function(socket){
console.log('New User Conected here');
redis.subscribe('test-channel');
redis.on('message', function(subscribed ,channel, message) {
console.log(channel);
message = JSON.parse(message);
socket.emit(channel + ':' + message.event, message.data);
});
socket.on('joinRoom', function(room ){
console.log('Join in this Room '+ room);
socket.join(room);
});
});
server.listen(3000);
event | php
public function broadcastOn()
{
return ['test-channel']; // static
}
Add a property to your broadcast event, then pass it to the constructor.
class Message implements ShouldBroadcast{
use SerializesModels;
protected $channel;
public function __construct($channel){
$this->channel = $channel;
}
public function broadcastOn(){
return [$this->channel];
}
}
Then when you fire the event, pass the channel in: event(new Message($channel));
Here is event
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class FieldWasUpdated implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $instance;
public $key;
public $value;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($instance, $key, $value)
{
$this->instance = $instance;
$this->key = $key;
$this->value = $value;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new Channel("model." . $this->instance->getModelType() . "." . $this->instance->id);
}
//public function broadcastAs() {
// return "FieldWasUpdated"; commented out for testing
//}
}
Here is frontend code
window.Echo = new window.LaravelEcho({
broadcaster: 'socket.io',
host: window.location.hostname + ':3000'
});
// I use different types of `listen` for testing
Echo.channel("model.ANNOUNCEMENT.2")
.listen(".*", function(e) {
console.log(e);
})
.listen("*", function(e) {
console.log(e);
})
.listen("FieldWasUpdated", function(e) {
console.log(e);
})
.listen(".FieldWasUpdated", function(e) {
console.log(e);
})
node.js code
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
// different types of emit for testing
message = JSON.parse(message);
const ev = channel + ":" + message.event;
io.emit(ev, message.data);
io.emit(channel, message);
io.emit(message, channel);
io.emit(channel, message.event, message.data);
io.emit(channel, message);
io.emit(channel, message.data, message.event);
});
Here is what I see in chrome development tools when Event is fired inside laravel app
So websocket is working but no one handler is called.
When I uncomment broadcastAs function nothing is changed.
When I rename channel to test-channel in backend and frontend - nothing changes.
After some time found a solution:
Server:
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
message = JSON.parse(message);
io.emit(message.event, channel, message.data);
});
Client
// both handlers process same event
Echo.channel("model.ANNOUNCEMENT.2")
.listen("FieldWasUpdated", function(e) {
console.log(e);
})
.listen(".App.Events.FieldWasUpdated", function(e) {
console.log(e);
})
Here is my server.js file:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
message = JSON.parse(message);
io.emit(message.event, channel, message.data);
});
And a simple event:
class FieldWasUpdated implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $instance;
public $key;
public $value;
public function __construct($instance, $key, $value)
{
$this->instance = $instance;
$this->key = $key;
$this->value = $value;
}
public function broadcastOn()
{
return new PrivateChannel("model." . $this->instance->getModelType() . "." . $this->instance->id);
}
}
The client connects to socket.io:
Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':3000'
});
And then listens for events (it is inside a Blade template):
var channel = "model.{{ $instance->getModelType() }}.{{ $instance->id }}";
Echo.private(channel)
.listen("FieldWasUpdated", function(e) {
window.VueBus.$emit("updated", channel, e.key, e.value);
})
.listen("FieldBecameDisabled", function(e) {
window.VueBus.$emit("disabled", channel, e.key);
});
Problem is: authentication is not handled, any user can subscribe to these channels.
Broadcast::channel("model.announcement.*", function($user, $id) {
return false; // this function is not called
})
Here is a sample event from the Chrome developer console (WebSocket):
[
"App\\Events\\FieldWasUpdated",
"private-model.announcement.2",
{
"instance":
{
"type":"ANN_TYPE_MISSED_CALL",
"status":"ANN_STATUS_CANCELLED",
"name":"1233421",
"phone":"+7(222)222-3322",
"email":"sdgsg#mail.com",
"message":"sdgdsgsdgdfdg",
"requirement":null,
"web_type":"ANN_WEB_TYPE_UNKNOWN",
"url":null,
"responsible_id":19,
"recommender_id":18
},
"key":"message",
"value":"sdgdsgsdgdfdg"
}
]
Also there is no /broadcast/auth URL, but BroadcastServiceProvider has a call to Broadcast::routes(); and when the browser loads, no calls to /broadcast/auth occur.
The Laravel documentation goes into detail about this in the Broadcasting chapter: https://laravel.com/docs/5.5/broadcasting#presence-channels. The Authorizing Presence Channels section should be the same for private channels.
I'm trying to save the return value of $http service inside my controller, but I get "undefined" like response
In my controller, I call a service that uses the $http:
//this returns undefined
vm.user_instruments = instruments.getInstruments();
My service:
function instruments($http){
this.getInstruments = function(){
$http.get('url/').
then(function(response) {
/*this console.log print the response,
but this value I can't get it in my controller*/
console.log(response.data);
return response.data;
}, function(error) {
return error.data;
});
}
}//end service
So, what am I doing wrong? My purpose is that the controller be ignorant of any details of HTTP
Several problems . First your service function isn't returning anything .... return $http from it.
this.getInstruments = function(){
// return the request promise
return $http.get('url/').
then(function(response) {
return response.data;
}, function(error) {
return error.data;
});
}
Then in controller assign the scope inside a promise callback:
instruments.getInstruments().then(function(data){
vm.user_instruments = data
});
you have two options to do this:
1. return the promise to the controller and use the promise in the controller
function service ($http) {
this.request = function () {
return $http.request({ /*...*/ });
};
}
function controller (service) {
service.request().then(function (resp) {
console.log(resp);
});
}
2. send callback to service and return the data to the callback
function service ($http) {
this.request = function (callback) {
return $http.request({ /*...*/ }).then(function (resp) {
callback(null, resp);
}, function (err) {
callback(err);
});
};
}
function controller (service) {
service.request(function (err, resp) {
if (err) return console.log(err);
console.log(resp);
});
}
the popular option is to use promises, so use option 1 :)
Try this way
Service:
function instruments($http){
this.get = function(callback){
$http.get('/url').success(function(res){
callback(res);
});
}
} /* end service */
Controller:
instruments.get(function(res){
vm.instruments = res;
});
It should work.
PS: typed in mobile.