Laravel5.8 Pusher not working. Cannot receive - php

I am creating realtime chat app.
I have set up pusher in my laravel and vue.js project.
But it doesn't work. Though I don't have any error in the console.
Also, I have no error in network tab.
I need to create messenger app, so I need a realtime chat function.
Now, I can push user's comment but in the other user's window, nothing shows up.
But it does, once I refresh the page. I think my pusher setting has something wrong, because in the pusher debug console, any session is not executed.
Here is my code.
.env
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
PUSHER_APP_ID=my id
PUSHER_APP_KEY=my app key
PUSHER_APP_SECRET= my secret key
PUSHER_APP_CLUSTER=mt1
broadcasting.php
'pusher' => [
'driver' => 'pusher',
'key' => env('my key'),
'secret' => env('my secret key'),
'app_id' => env('my id'),
'options' => [
'cluster' => 'ap3',
'encrypted' => true,
],
BroadcastServiceProvider.php
Broadcast::routes(['middleware' => ['auth:api']]);
require base_path('routes/channels.php');
bootstrap.js
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'my key',
cluster: 'ap3',
encrypted: true
});
NewMessage.php
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class NewMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('messages.' . $this->message->to);
}
public function broadcastWith()
{
$this->message->load('fromContact');
return ["message" => $this->message];
}
}
routes/channel.php
use Illuminate\Support\Facades\Broadcast;
ContactsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use App\Message;
class ContactsController extends Controller
{
public function get() {
$contacts = User::where('id', '!=', auth()->id())->get();//全部のcontactをjson経由でgetする
return response()->json($contacts);
}
public function getMessagesFor($id) {
$messages = Message::where('from', $id)->orWhere('to', $id)->get();
return response() -> json($messages);
}
public function send(Request $request) {
$message = Message::create([
'from' => auth()->id(),
'to' => $request->contact_id,
'text' => $request->text
]);
return response()->json($message);
}
}
Here is what I tried.
run all the command according to the laravel document.
php artisan chache:clear
and run the server again.
run php artisan queue:work
in command terminal

Did you write
window.Echo.private('channelName').listen('EventName',function(e){
})
in your application to listen

In your send() function you should write
broadcast(new NewMessage($message));
to broadcast the message.

Related

Laravel with Pusher simply doenst listen to events

What am i missing? I did everything right but when i sent a new Event my console just doesnt log anything, here is my code:
FRONT END
I imported the required libraries:
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = Echo;
window.pusherEcho = new Echo({
broadcaster: 'pusher',
key: 'my-key',
cluster: 'us2',
forceTLS: true,
wsHost: window.location.host,
wsPort: 8000
});
BACK END
My event class:
<?php
namespace App\Events;
// ... imports
class ReceivedNotification implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('received-notification');
}
}
I registered a route just to test it:
Route::get('/event', function() {
event(new App\Events\ReceivedNotification('Hello world'));
return 'event sent';
});
FRONT END
I'm listening to events in some page with Laravel Echo:
pusherEcho.channel('received-notification')
.listen('ReceivedNotification', (e) => {
console.log(e);
});
When i go to the route /event it never triggers the console.log above, which means something is just wrong, yes my .env file is everything ok i already checked:
BROADCAST_DRIVER=pusher
.
.
.
PUSHER_APP_ID=***
PUSHER_APP_KEY=***
PUSHER_APP_SECRET=***
PUSHER_APP_CLUSTER=us2
As we can see you are creating private channel in App\Events\ReceivedNotification and listen public channel in front-end.
So change your front-end to below code :
pusherEcho.private('received-notification')
.listen('ReceivedNotification', (e) => {
console.log(e);
});
Also register your Event and Listerner in app\Providers\EventServiceProvider like :
protected $listen = [
'App\Events\ReceivedNotification' => [
'App\Listeners\ReceivedNotificationListener'
],
]

Invalid key in subscription auth data , type : websocketError

I am using Laravel 5.8.*, laravel echo, pusher and vuejs for a private chat.
Trying to listen the broadcast-ed event using vuejs as frontend but no data received in callback from pusher. I can see the private channel and data sent to pusher but received empty. I see below messages in console when I connected to pusher :
Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"460f6d3e4959b66a186a:3a759e55e46fc62d0188461344828ee4266edba8f1c0f5a5fd9743beb086d8cf","channel":"private-messages.1"}}
Pusher : Event recd : {"event":"pusher:error","data":{"code":null,"message":"Invalid key in subscription auth data: '460f6d3e4959b66a186a'"}}
Pusher : Error : {"type":"WebSocketError","error":{"type":"PusherError","data":{"code":null,"message":"Invalid key in subscription auth data: '460f6d3e4959b66a186a'"}}}
I tried all the ways I can but unable to find the reason for this subscription error. I had set all the pusher data (channel name, key, secret id, app id, cluster) correctly. I read all the other questions which already mentioned the same issue here in stackoverflow but none of them has relevant solution or answer. So again came here for some help if anyone connect remotely and check on my laptop.
ChatApp.vue
<template>
<div class="chat-app">
<Conversation :contact="selectedContact" :messages="messages" #new="saveNewMessage"/>
<ContactsList :contacts="contacts" #selected="startConversationWith"/>
</div>
</template>
<script>
import Conversation from './Conversation';
import ContactsList from './ContactsList';
export default {
props: {
user: {
type: Object,
required: true
}
},
data(){
return {
selectedContact: null,
messages: [],
contacts: []
};
},
mounted() {
Echo.private(`messages.${this.user.id}`)
.listen('NewMessage', (e) => {
console.log('listening NewMessage');
this.handleIncoming(e.message);
});
axios.get('/contacts')
.then((response)=>{
this.contacts = response.data;
});
},
methods:{
startConversationWith(contact){
this.updateUnreadCount(contact, true);
axios.get(`/conversation/${contact.id}`)
.then((response) => {
this.messages = response.data;
this.selectedContact = contact;
})
},
saveNewMessage(message){
this.messages.push(message);
},
handleIncoming(message){
if (this.selectedContact && message.from == this.selectedContact.id) {
this.saveNewMessage(message);
return;
}
this.updateUnreadCount(message.from_contact, false);
},
updateUnreadCount(contact, reset){
this.contacts = this.contacts.map((single)=>{
if (single.id !== contact.id) {
return single;
}
if (reset)
single.unread = 0;
else
single.unread += 1;
return single;
})
}
},
components: {Conversation, ContactsList}
}
</script>
<style lang="scss" scoped>
.chat-app{
display:flex;
}
</style>
EVENT
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\Message;
class NewMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Message $message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('messages.'.$this->message->to);
}
public function broadcastWith(){
$this->message->load('fromContact');
return ["message" => $this->message];
}
}
channels.php
Broadcast::channel('messages.{id}', function ($user, $id) {
return $user->id === (int) $id;
});
broadcasting.php
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
],
],
connection with laravel echo
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
encrypted: true
});
.env
PUSHER_APP_ID=xxxx
PUSHER_APP_KEY=dbxxxxxxxb29
PUSHER_APP_SECRET=160xxxxxxxx11af
PUSHER_APP_CLUSTER=ap2
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
There are no errors in the error log of pusher dashboard.
The public key in your client JS is different from the one you have set in your .env file.
I can see from the screenshot from the client that the public key begins c07d341 wheres in your 'env file this is set as:
PUSHER_APP_SECRET=160xxxxxxxx11af
Finally the error message you are getting relates to a public key mismatch.
Have you possibly updated your keys without re-compiling your Javascript dependencies using the command
npm run dev
If that doesn't solve the issue, you may also want to check you have not hardcoded the incorrect public key in another location

Laravel broadcasting channel function won't fire

So I'm gonna use laravel broadcasting for a chat app,
I followed Laravel Broadcasting approach,
Uncommented App\Providers\BroadcastServiceProvider
from providers array inside config/app.php
Registered in pusher website, made a channel
and filled the fields below inside .env file with my pusher channel info
PUSHER_APP_ID
PUSHER_APP_KEY
PUSHER_APP_SECRET
PUSHER_APP_CLUSTER
Inside my broadcast.php config file where I set the default driver to pusher, I also added
'options' => [
'cluster' => 'us2',
'encrypted' => true,
],
to pusher array inside connections array based on my channel info in pusher panel
Installed pusher php package on my laravel project using composer require pusher/pusher-php-server "~3.0" command
Here is my event class
<?php
namespace App\Events;
use App\User;
use App\TherapyMessage;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\AppLog;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class TherapyMessageSent implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* User that sent the message
*
* #var User
*/
public $user;
/**
* Message details
*
* #var Message
*/
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user, TherapyMessage $message)
{
$this->user = $user;
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
$message_id = $this->message->id;
$user = $this->user;
AppLog::create([
'file_name' => __FILE__,
'message' => "broadcast before send with Message ID of $message_id from $user->full_name"
]);
return new PrivateChannel("therapy-chat.$message_id");
}
}
The AppLog is a model that I use for logging inside the project
I tried implementing ShouldBroadcast interface at first but that didn't work either
I also registered my event inside EventServiceProvider.php file and run php artisan event:generate command, here is the EventServiceProvider $listen array:
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
TherapyMessageSent::class
],
];
I also imported the event namespace next to other namespaces inside the file:
use \App\Events\TherapyMessageSent;
Here is the channel that I defined inside routes/channels.php file:
use App\AppLog;
Broadcast::channel('therapy-chat.{message_id}', function ($user, $message_id) {
AppLog::create([
'file_name' => __FILE__,
'message' => "broadcast sending with Message ID of $message_id to $user->full_name"
]);
if (!Auth::check()) return false;
$message = \App\TherapyMessage::find($message_id);
if (!$message) {
AppLog::create([
'file_name' => __FILE__,
'message' => "Message with ID of $message_id Not Found for broadcasting"
]);
return false;
}
$will_send = false;
if ($therapist = $user->therapist) {
$will_send = $therapist->id === $message->therapist_id;
} else if ($patient = $user->patient) {
$will_send = $message->patient_id === $patient->id;
}
if ($will_send) {
AppLog::create([
'file_name' => __FILE__,
'message' => "Message with ID of $message_id broadcasted to $user->full_name"
]);
}
return $will_send;
});
Finally, this is my controller method:
public function sendToTherapist(Request $request) {
$validation = \Validator::make($request->all(), ['message' => 'required']);
if ($validation->fails()) return $this->validationError($validation);
$user = \Auth::user();
$patient = $user->patient;
$therapist = $patient->therapist;
if (!$therapist) return $this->errorWithMessage('Patient does not have Therapist');
$message = \App\TherapyMessage::create([
'patient_id' => $patient->id,
'therapist_id' => $therapist->id,
'type' => TherapyMessageType::TEXT,
'text' => $request->message,
'sender_role' => TherapyMessageSenderRole::PATIENT
]);
broadcast(new TherapyMessageSent($user, $message))->toOthers();
return $this->success(['id' => $message->id]);
}
My controller class extends from BaseController which is a custom controller class with helper methods such as success(), validationError() and errorWithMessage()
As you see in the code above
I filled $user and $message with correct values and the request works without any error
I think the channel method won't even be fired,
as I check the AppLog table when I call broadcast method, only the log inside TherapyMessageSent event broadcastOn function is saved
and even the log that I save at the beginning of channels.php method, isn't saved so I think this method is never executed.
If anyone could help me with the problem, I'd be thankful.

Correct way to seed and delete data in the database for Laravel testing?

I am new to Laravel tests, and i am currently building my tests so they use certain data in my database to check if an HTTP request did the appropriate job.
I am trying to figure out how i can "seed" data into my database before i run my tests but also delete this data after all the tests are complete (either succeed of failed, should get deleted anyway).
I tried to understand how to do it correctly reading some articles in the internet but just couldn't find the right solution for me.
I know that in node.js the Mocha tests has a "beforeEach" to manipulate data before every test, is there a similar option in PHP Laravel?
With laravel version greater than 5.5 you can use the RefreshDatabase trait within your test which will reset your database after test have been run. All you will have to do is to add it at the top of your test like bellow
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
class YourModelNameTest extends TestCase
{
use RefreshDatabase;
public function setUp()
{
parent::setUp();
// This will run all your migration
Artisan::call('migrate');
// This will seed your database
Artisan::call('db:seed');
// If you wan to seed a specific table
Artisan::call('db:seed', ['--class' => 'TableNameSeeder ', '--database' => 'testing']);
}
}
The RefreshDatabase trait have define refreshDatabase which migrate the database before and after each test. you can see the code here
RefreshDatabse refreshDatabase
While Yves answer should work (separate database), i have found a set of methods that can help achieving what i needed:
"setUp" and "tearDown".
the "setUp" method will do something before the set of tests will run (the test class) and "tearDown" will do something after all of those tests were executed.
I'm attaching my test class here for an example of how i used those:
<?php
namespace Tests\Feature;
use App\User;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase
{
private $token;
public function setUp(){
parent::setUp();
$userData = [
'email' => 'mytestemail#email.com',
'password' => '123456'
];
$response = json_decode($this->post('/register', $userData)->baseResponse->getContent());
$this->token = $response->token;
}
/**
* A basic test example.
*
* #return void
*/
public function testRegister()
{
$validData = [
'email' => 'testrerere#email.com',
'password' => '123456'
];
$invalidEmail = [
'email' => 'not_a_real_email',
'password' => '123456'
];
$invalidPassword = [
'email' => 'test2#email.com',
'password' => '12'
];
$emptyData = [];
$goodResponse = $this->post('/register', $validData);
$goodResponse->assertStatus(201);
$goodResponse->assertJsonStructure(['token']);
User::where('email', 'testrerere#email.com')->delete();
$invalidEmailResponse = $this->post('/register', $invalidEmail);
$invalidEmailResponse->assertStatus(400);
$invalidPasswordResponse = $this->post('/register', $invalidPassword);
$invalidPasswordResponse->assertStatus(400);
$emptyDataResponse = $this->post('/register', $emptyData);
$emptyDataResponse->assertStatus(400);
}
public function testToken()
{
$validData = [
'email' => 'mytestemail#email.com',
'password' => '123456'
];
$invalidData = [
'email' => 'nonexistingemail#test.com',
'password' => '123456'
];
$validDataResponse = $this->post('/token', $validData);
$validDataResponse->assertStatus(200);
$validDataResponse->assertJsonStructure(['token']);
$invalidDataResponse = $this->post('/token', $invalidData);
$invalidDataResponse->assertStatus(400);
}
//get an account object based on a token
public function testAccount()
{
$goodResponse = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->token,
])->json('GET', '/account');
$goodResponse
->assertStatus(200)
->assertJsonStructure([
'user',
]);
//altering the token to get an invalid token error
$badResponse = $this->withHeaders([
'Authorization' => 'Bearer L' . $this->token,
])->json('GET', '/account');
// print_r($badResponse->baseResponse->getContent());
$badResponse->assertJson(['status' => 'Token is Invalid']);
}
public function tearDown()
{
User::where('email', 'mytestemail#email.com')->delete();
User::where('email', 'testrerere#email.com')->delete();
parent::tearDown();
}
}

How to configure Channel Authorization for Broadcasting in Laravel 5.3 using Pusher Driver?

Laravel Version: 5.3.*
PHP Version: 5.6.17
Database Driver & Version:
mysql
Description:
According to Laravel 5.3 documentation when broadcasting events on private or presence channels, in the boot method of the BroadcastServiceProvider one must provide a callback that resolves if an user has authorization to listen to that channel to the Broadcast facade method channel. This method should return a boolean. In the the BroadcastServiceProvider method boot we should also include Broadcast::routes() that will define the auth route that the client will call to check for permission on the channel. This routes method can receive an array of attributes to apply to the route. Now it's where it gets weird. When the client call this route no matter what callback I passed to the Broadcast::channel method it will give me a 403 forbidden unless (and now comes the weirdest part) I provide an array to the Broadcast::routes with a key named prefix and a value of whatever. If the key is not prefix it will go back to 403 forbidden.
HttpException in PusherBroadcaster.php line 42:
My setup follows. I'm for sure doing something wrong but after a lot of ours trying to understand I can't figure it out. Can someone give an hint?
Steps To Reproduce:
I have created a simple event:
<?php
namespace App\Events;
use App\Models\Presentation;
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 PresentationCreated implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $presentation;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Presentation $presentation)
{
$this->presentation = $presentation;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('presentation');
}
}
that i trigger by calling event(new PresentationCreated($presentation));
I have installed "pusher/pusher-php-server": "^2.5.0" and created an account in pusher.
I put my pusher credentials in .env:
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=*****
PUSHER_APP_KEY=*****************
PUSHER_APP_SECRET=****************
PUSHER_APP_CLUSTER=**
in my config\broadcast.php I have:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => 'eu',
'encrypted' => true,
],
],
My client side:
this.Echo = new Echo({
broadcaster: 'pusher',
key: typeof handover.pak !== 'undefined' ? handover.pak : '',
cluster: 'eu'
});
this.Echo.private(`presentation`)
.listen('PresentationCreated', (e) => {
console.log(e, 'raposa')
});
And finally the BroadcastServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Broadcast::routes();
//The commented line would make the authorization pass even if I return false bellow
//Broadcast::routes(['prefix' => 'I do not know what I am doing']);
/*
* Authenticate the user's personal channel...
*/
Broadcast::channel('presentation', function ($user) {
return false;
});
}
}
EDIT
Thanks to #yazfield answer I was able to understand what was going on. The http error was due to the $request->user() being null. That was because I was not passing the additional middlewares that my route namespace was using. By doing Broadcast::routes(['middleware' => ['web', 'clumsy', 'admin-extra']]); I was able to solve the problem.
This Laravel issue also helped me getting the grasp of the thing.
By giving a parameter to routes you're setting routes attributes and overriding the attributes that default to 'middleware' => ['web'], which basically means you're not using any of the web middlewares when you give any array without middleware attribute, you're not verifying crsfToken...etc.

Categories