I followed this tutorial to set up a messenger on my laravel application. My laravel logs show the type of messages below, which tells me that messages are being broadcast, but I'm not seeing them show up on Pusher! I've checked and double checked my pusher credentials and cluster and they are right.
[2020-06-06 14:49:30] local.INFO: Broadcasting [message.posted] on channels [private-
conversation.1] with payload:
{
"message": {
"id": 17,
"content": "h",
"sender_id": 1,
"conversation_id": "1",
"created_at": "2020-06-06T14:49:30.000000Z",
"updated_at": "2020-06-06T14:49:30.000000Z",
"conversation": {
"id": 1,
"new_messages": 0,
"created_at": "2020-06-06T15:41:25.000000Z",
"updated_at": "2020-06-06T14:49:30.000000Z"
}
},
"socket": null
}
I've created an endpoint to send messages and I'm triggering it with Postman. Below are my controller and the event that I'm triggering that should be broadcasting to pusher:
Store method in my controller:
public function store($id, Request $request)
{
$sender_id = auth()->user()->id;
$conversation = Conversation::find($id);
$content = $request->content;
// check that user belongs to the conversation
// updated conversation updated_at
// save message
if (in_array($sender_id, $conversation->users->pluck('id')->toArray())) {
// if the user is a participant in the conversation, they can save message
$message = Message::create([
'sender_id' => $sender_id,
'content' => $content,
'conversation_id' => $id
]);
// update the conversation updated_at
$conversation->touch();
} else {
// if user is not a participant in the conversation
return response([
'message' => 'Sorry, you are not part of this conversation and cannot post messages here'
]);
}
// fire new message posted event
event(new MessageWasPosted($message));
return response([
'message' => 'Message successfully posted',
'content' => $content
]);
}
My event:
class MessageWasPosted implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The message
*
* #var mixed
*/
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('conversation.'.$this->message->conversation->id);
}
/**
* The event's broadcast name
*
* #return string
*
*/
public function broadcastAs()
{
return 'message.posted';
}
}
To make sure auth is not an issue, I just return true in a Channel that I'm using:
<?php
namespace App\Broadcasting;
use App\User;
use App\Conversation;
class ConversationChannel
{
public function __construct()
{
}
/**
* Authenticate the user's access to the channel.
*
* #param \App\User $user
* #return array|bool
*/
public function join(User $user, Conversation $conversation)
{
return true;
return $conversation->users->contains($user);
}
}
On the Pusher debug console I see nothing coming through... Eventhough every now and then I see a random connection happening, no messages are being sent. Is there something obvious I'm missing?
PS: I've tried public channels, and they don't work either.
Related
I am using Laravel's default notifications system. However, I need an extra column in the notifications table to check if the user already has the same unread-notification or not.
For example: If user's profile is not complete then on every login he/she will be reminded until the profile is complete. But if the previously generated notification is still unread then the new notification will not be generated.
Table: notifications
default_fields
notify
...
...
profile_incomplete
...
...
password_change_overdue
...
Notifications class
class NotifyToCompleteProfileNotification extends Notification
{
public function __construct()
{
$this->notify = 'profile_incomplete';
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'data' => '...',
];
}
}
You can use custom notification channel to make this works. Let's assume you want to add a group field to notifications. First, add this field to table, then make file GroupedDbChannel.php:
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class GroupedDbChannel
{
public function send($notifiable, Notification $notification)
{
$data = $notification->toDatabase($notifiable);
return $notifiable->routeNotificationFor('database')->create([
'id' => $notification->id,
'group' => $notification->group,
'type' => get_class($notification),
'data' => $data,
'read_at' => null,
]);
}
}
Next, you need to define custom group and channel for notification:
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class TestNotification extends Notification {
/*
* Here you define additional value that will
* be used in custom notification channel.
*/
public string $group = 'incomplete-profile';
public function via($notifiable) {
return [GroupedDbChannel::class];
}
public function toArray($notifiable) {
return [
// notification data
];
}
/*
* It's important to define toDatabase method due
* it's used in notification channel. Of course,
* you can change it in GroupedDbChannel.
*/
public function toDatabase($notifiable): array
{
return $this->toArray($notifiable);
}
}
And it's done. Use notifications as standard.
$user->notify(new TestNotification());
Now, value incomplete-profile from $group field goes to notifications table to group column.
I have Laravel 6 and use pusher as a broadcast event. But I get this error (see screenshot)
the error
\Illuminate\Broadcasting\Broadcasters\PusherBroadcaster.php:121
screenshot stacktrace error
here is my controller:
public function sendMessage(Request $request)
{
//validate incoming request
$this->validate($request, [
'message' => 'required|string'
]);
try {
$iam = Auth::user();
$message = $iam->family->messages()->create([
'message' => $request->input('message'),
'id_user' => $iam->id
]);
event(new ChatSubmitted('$message'));
//return successful response
return response()->json(['messages' => $request->all(), 'message' => 'Send Message Succesfully'], 200);
} catch (\Exception $e) {dd($e);
//return error message
return response()->json(['message' => 'Send Message Failed!'], 409);
}
}
and here is my event:
class ChatSubmitted 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 Channel('my-channel');
}
}
the routes of api:
Route::get('messages', 'ChatsController#fetchMessages');
Route::post('messages/create', 'ChatsController#sendMessage');
In my Laravel application I am trying to get into feature testing and have started with a model called Announcement.
One test I'm running is whether a user can create an instance of Announcement and persist it to the database.
My test is as follows:
/** #test */
public function a_user_can_create_an_announcement()
{
$this->withoutExceptionHandling();
$this->setupPermissions();
$announcement = factory(Announcement::class)->raw();
$this->actingAs(factory(User::class)->create())->get(route('announcements.index'))->assertStatus(200);
$this->post(route('announcements.store', $announcement))->assertStatus(302);
$this->assertDatabaseHas('announcements', $announcement);
}
Now as far as I understand factory(Announcement::class)->raw(); returns a new Announcement as an array using the relevant model factory.
I then make a request to my store endpoint with the data array and expect to redirected so I add the following:
$this->post(route('announcements.store', $announcement))->assertStatus(302);
The final line is to check the announcement was written to the database table called announcements
I get the following error from the test case:
1) Tests\Feature\AnnouncementsTest::a_user_can_create_an_announcement
Failed asserting that a row in the table [announcements] matches the attributes {
"message": "King. 'When did you.",
"message_details": "The Caterpillar.",
"author": "beatrice-herzog",
"status": "pending",
"published_at": null,
"created_at": {
"date": "2019-05-16 04:13:12.000000",
"timezone_type": 3,
"timezone": "Europe\/London"
},
"updated_at": "2019-08-20T13:37:22.293428Z"
}.
Found: [
{
"id": 5,
"message": "King. 'When did you.",
"message_details": "<p>The Caterpillar.<\/p>",
"author": "hollis-dach",
"status": "pending",
"published_at": null,
"created_at": "2019-08-20 14:37:23",
"updated_at": "2019-08-20 14:37:23"
}
].
Here is my AnnouncementFactory
<?php
/* #var $factory \Illuminate\Database\Eloquent\Factory */
use Faker\Generator as Faker;
use App\User;
use App\Announcement;
use Carbon\Carbon;
$factory->define(Announcement::class, function (Faker $faker) {
$user = factory(User::class)->create();
return [
'message' => $faker->realText(25),
'message_details' => $faker->realText(20),
'author' => $user->username,
'status' => 'pending',
'published_at' => null,
'created_at' => $faker->dateTimeThisYear('now', 'Europe/London'),
'updated_at' => Carbon::now()
];
});
Here is my AnnouncementModel
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Purifier;
use Carbon\Carbon;
use App\Like;
class Announcement extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'message', 'message_details', 'status', 'published_at'
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'published_at',
'created_at',
'updated_at',
];
/**
* Get the user that posted this announcement
*/
public function user()
{
return $this->belongsTo(User::class, 'author', 'username');
}
/**
* Get the users that have liked this article
*
* #return void
*/
public function likes()
{
return $this->morphToMany(User::class, 'likeable');
}
/**
* Purify the content of message details when it is set so that it isn't vulnerable to XXS attacks
*
* #param string $value
* #return void
*/
public function setMessageDetailsAttribute($value)
{
$this->attributes['message_details'] = Purifier::clean($value);
}
/**
* Generate a nicer format for created_at
*
* #return void
*/
public function getCreatedAtAttribute($value)
{
return Carbon::parse($value)->format('d F Y');
}
/**
* Determine whether an announcement is pending
*
* #return void
*/
public function getPendingAttribute()
{
return $this->status == 'pending' ? true : false;
}
/**
* Check if the user has liked this announcement
*
* #return void
*/
public function getUserHasLikedAttribute()
{
$like = $this->likes()->whereUserId(auth()->user()->id)->first();
return (!is_null($like)) ? true : false;
}
/**
* Get the users that have liked this article
*
* #return void
*/
public function getLikesCountAttribute()
{
return $this->likes()->count();
}
/**
* Get count of users who liked the announcement excluding the logged in user
*
* #return void
*/
public function getLikesCountExcludingAuthUserAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->count();
}
/**
* Get random user who liked this announcement
*
* #return void
*/
public function getRandomUserWhoLikedThisAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->inRandomOrder()->first();
}
/**
* Get all users who liked this announcement
*
* #return void
*/
public function getUsersWhoLikedThisAttribute()
{
return $this->likes()->where('username', '<>', auth()->user()->username)->get();
}
/**
* Scope an article by whether or not it's published
*/
public function scopePublished($query)
{
return $query->where('status', 'published');
}
/**
* Scope an article by whether or not it's drafted
*/
public function scopePending($query)
{
return $query->where('status', 'pending');
}
/**
* Scope an article by whether or not it's archived
*/
public function scopeArchived($query)
{
return $query->where('status', 'archived');
}
}
Do the attributes literally have to be identical or am I just using tests incorrectly?
Do factories use model accessors and mutators?
I don't think you need to be testing the created_at and updated_at fields in this case. The reason why your test is failing is because the created_at value on the array is an instance of Carbon\Carbon rather than a string representing the datetime.
I would just update your factory to remove the created_at and updated_at values.
Try changing your factory to the following:
$factory->define(Announcement::class, function (Faker $faker) {
$user = factory(User::class)->create();
return [
'message' => $faker->realText(25),
'message_details' => $faker->realText(20),
'author' => $user->username,
'status' => 'pending',
'published_at' => null
];
});
Basically you are checking the database with the wrong information.
1 - announcements.store is an api which creates a new announcements for you.
2 - when you are calling assertDatabase, it's mean you have some information in your hand which you can check against the database. But if you take a look at the exception you can see you want to check this
"message": "King. 'When did you.",
"message_details": "The Caterpillar.",
"author": "beatrice-herzog",
"status": "pending",
"published_at": null,
"created_at": {
"date": "2019-05-16 04:13:12.000000",
"timezone_type": 3,
"timezone": "Europe\/London"
},
"updated_at": "2019-08-20T13:37:22.293428Z"
with this
"message": "King. 'When did you.",
"message_details": "<p>The Caterpillar.<\/p>",
"author": "hollis-dach",
"status": "pending",
"published_at": null,
"created_at": "2019-08-20 14:37:23",
"updated_at": "2019-08-20 14:37:23"
So either way you need to change the clause you are using. in this case you need to fix this three fields values(created_at, updated_at, message_details) or just remove them.
Yes, the key / value pairs you pass to assertDatabaseHas have to be identical to the record in your database. The function just searches for a row having the given column (key) with the specified value.
Be careful about converting your models to arrays. The resulting array may contain unexpected fields (relations) so better explicitly state the fields you want to check for.
I suggest you only include the properties of your announcement you want to test omitting the timestamps and other meta information.
Like this:
$this->assertDatabaseHas('announcements', Arr::only($announcement, [
'message', 'status'
]));
or like this:
$this->assertDatabaseHas('announcements', [
'message' => $announcement->message,
'status' => $announcement->status,
]);
I'm trying to broadcast a simply test notification to a Laravel app using
Pusher.Within my web page, I've got this JavaScript to listen for the
broadcast in bootstrap.js
import Echo from 'laravel-echo'
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'notmyrealpusherkey',
encrypted: true
});
This is my Notification.vue
Echo.private('App.User.' + this.id)
.notification((notification) => {
console.log(notification);
toastr.info(notification.message, notification.subject);
});
My BroadcastServiceProvider is configured to validate the private channel request:
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int)$user->id === (int)$id;
});
When I check the Pusher console, I can see that the code above successfully subscribed check image here:
https://d1ro8r1rbfn3jf.cloudfront.net/ms_76665/NvEB7BgUnmIA2IR5rO1S3gF1aSP306/Pusher%2B-%2BDebug%2Bconsole%2B2016-12-28%2B19-37-19.png?Expires=1510590716&Signature=Cgykr97sBJUqljH02DfUJztcILiFTMUsUBfsF4kXoYsuJCsHf4lG-3tyhXBmCM70rWGyeCqZ7nrkgqYuOYEYs6Q41nq-ActI9B-vt6fhh5rYgekfe-HM0dERY67OqAbaLVfskq2mUp1Zl7yBPKwgNR9Fw0uzdH8q6ia9L0Za9QBLM1u6Fq3AHoN4OodJaXr5X-ifn1ZVAC7WsYVtLcnWpr3Yx0-w2nzbS4rD2S9KtMnIpsOA8bcHHWW3qoDFm5J0hIB6GVF42-vQTUNo~4B1~L8XT41coOarsF~sTVsaWTYINX93BmpVpKUEDyoarbffEuUsf4sZO6Ee5fILK1wJOQ__&Key-Pair-Id=APKAJHEJJBIZWFB73RSA
When i send notification directly from pusher it will send on console but not send using trigger the image link in description:
https://d1ro8r1rbfn3jf.cloudfront.net/ms_76665/kxgC93iHRWnMPHIdJehBgfsfUG47Lu/Pusher%2B-%2BDebug%2Bconsole%2B2016-12-28%2B19-37-56.png?Expires=1510592071&Signature=f8wvoKGnWLROJ0cXCWqkWz3EVL6rVj6PuJ~-gXpHHgrt7FlZ9gf9YO3lezW2dplmN08419w9qWJh-Cc7cpt4B1dEEwqORjL8eRz528B6l-HKHwnn96iCIE~bfwc80L9qJx9nBbLqTqnEkAlvbugcGlIrr1zB-iMlz9TMloLpYkq-2T-a1Ww8BM3SmbETVunLqhqYxUCbgK3T1swS135tqvg36sA8FWWnBe3djHgSTUuYS6Nv8MyvkF8JS1slxtMzsJ30oEzs5zc~QhEfECfoA8On8VIHP8a4VrOjUyElcrcPHG~7TwnYP8Ajje7O7EW9SNyIV7t3LSyxBGiIdsVDtQ__&Key-Pair-Id=APKAJHEJJBIZWFB73RSA
The NewFriendRequest is just a simple test notification:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class NewFriendRequest extends Notification implements ShouldQueue
{
use Queueable;
public $user;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($user)
{
$this->user = $user;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail','broadcast','database'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('You received a new friend request from ' . $this->user->name)
->action('View Profile', route('profile', ['slug' => $this->user->slug]))
->line('Thank you for using our Tictac!');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'name' => $this->user->name,
'message' => $this->user->name . ' sent you friend request.'
];
}
}
I also tried triggering it from the Laravel side, but it still doesn't reach the user's browser:
$resp = Auth::user()->add_friend($id);
User::find($id)->notify(new \App\Notifications\NewFriendRequest(Auth::user()));
return $resp;
You're missing the toBroadcast() method in your Notification, as stated by the documentation.
class NewFriendRequest extends Notification implements ShouldQueue
{
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'message' => 'Insert your message',
'subject' => 'Insert your subject',
]);
}
}
I am mobile app(Android/iOS) developer, and in the absence of our php developer, I want to add another table in our database and POST and GET some data and store in new table.
Laravel 4 is installed in our server, and with some basic cPanel, phpMyAdmin and php information I tried to create a new table in our existing database.
first I tired and created a table using phpMyAdmin, then I added Model file like this
<?php
class Record extends Eloquent {
protected $table = 'requestRecords';
protected $guarded = array();
public $timestamps = true;
}
But, when i tried add data to requestRecords in my route.php file (which i created manually using phpMyAdmin) using this syntax, nothing happened:
Route::post('api/v1/FLRequest', function()
{
//check right inputs sent to us.
if( !Request::has('userId') or
!Request::has('userName') or
!Request::has('timeStamp') or
!Request::has('requestFor') or
!Request::has('nKey'))
return Response::json(array(
'status' => "error",
'message' => "Missing Parameter"),
500
);
$inputs = Request::all();
if($inputs['nKey']!= "nKey_Goes_Here")
return Response::json(array(
'status' => "error",
'message' => "Wrong Request"),
500
);
$addRecord = Record::create(array(
'userName' => $inputs['userName']));
if($addRecord!==FALSE)
{
return Response::json(array(
'status' => "success",
'message' => "Record Updated"),
200
);
}
else
{
return Response::json(array(
'status' => "error",
'message' => "Failed Updating Record"),
401
);
}
Actually I did not get neither 200 nor 401, but I got HTTP 500 Internal Server Error.
Then I tried adding table using schema, so I created a php file in database/migrations folder, named: 2015_01_02_104814_create_requestRecords_table.php and add these codes there :
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateRequestRecordsTable extends Migration {
public function up()
{
Schema::create('requestRecords', function($table) {
$table->increments('id');
$table->string('userId',255);
$table->string('userName', 255);
$table->string('timeStamp', 255);
$table->string('payLoad',255);
$table->string('requestFor',255);
$table->string('bToken',255);
$table->string('bSituation',255);
});
}
public function down()
{
Schema::drop('requestRecords');
}
}
and again after trying to add data, I did not get neither 200 nor 401, but I got HTTP 500 Internal Server Error.
For more details here is my Controller and Model :
Controller in file /app/controllers/RequestRecordsController.php:
<?php
class RequestRecordsController extends BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
}
}
?>
and Model in /app/models/Record.php :
<?php
class Record extends Eloquent {
protected $table = 'requestRecords';
protected $guarded = array();
public $timestamps = true;
}
What am I doing wrong?
Thanks :)
You are adding lots of validation and always throwing a 500 error yourself on the application.
A best practice would be for you to remove all of that, try to make things work first, and after that. When you see that the code works, then you add validation to make sure that every field you want is on the form.