Undefined eager-loaded model in Laravel Echo event - laravel

I'm using Laravel Echo to broadcast events from the server to the client.
The application is a forum, where users can create posts in topics.
Here is the controlled method code where the new post is created, and the event dispatched.
$post = Post::create([
'user_id' => 1,
'topic_id' => request('topic_id'),
'body' => request('body'),
]);
// Fetch the post we've just created, with the relationships this time
$post = Post::with('user', 'topic')->find($post->id);
// Broadcast the event
event(new PostCreated($post));
Here is the event class :
class PostCreated implements ShouldBroadcast
{
public $post;
public function __construct(Post $post)
{
$this->post = $post;
}
public function broadcastOn()
{
return new Channel('topics.' . $this->post->topic_id);
}
}
Finally, here is where the event is intercepted in the front-end :
Echo.channel('topics.' + this.topic.id)
.listen('PostCreated', (e) => {
this.posts.push(e.post);
});
The problem is that I can't seem to access the user property from the listen() method on the front-end.
console.log(e.post.user) // Undefined
If I do a console.log() of the post, I can see the properties of the Post (user_id, topic_id, body, created_at, updated_at) but it's not showing the user or topic properties that were eager-loaded in the controller, before the event was sent.
Those properties are accessible from the event class itself :
// In the __construct() method of the PostCreated event
echo $this->post->user->name; // Works, the name is echo'd
... but somehow are not sent to the front-end when the event is broadcasted.
How can I make sure that the user and topic properties are sent to the client, along with the post itself?

Related

Laravel - Passing a variable from controller to model or accessing a variable in the controller from the model

I want to pass a variable in the controller to the model in laravel.
In Controller,
$withoutUser = True;
$post->update([
'status' => 'inactive'
]);
In model,
protected static function boot(): void
{
parent::boot();
static::updated(fn (Model $model) =>
// Need to access the $withoutUser variable from here?
);
}
Is there a method to pass the $withoutUser variable when calling $post->update() or is it possible to access the $withoutUser variable in the controller when the static::updated method is called in model?
Thanks
You can do this by creating a property on the post model. The exact same instance of the post class is sent to the event dispatcher. So you can write your code something like this:
class Post extends Model
{
public bool $updateWithoutUser = false;
...
}
And then in your controller:
$post->updateWithoutUser = true;
$post->update([
'status' => 'inactive',
]);
And in the boot function:
protected static function boot(): void
{
parent::boot();
static::updated(function (Post $post) {
if ($post->updateWithoutUser) {
...
}
});
}
Though you should be careful if you are queueing the listener, because in that case the property will not be preserved as it fetches the post from the database when the listener is run from the queue.

Why Observer's Saved method not working for while update the record in laravel 5.3

I have project in laravel 5.3 and i am using Observer for activity log of user, for that i have created one obeserver with saved() and deleted() method.
The saved() method is working fine for new record, while update the record saved() is not getting call nor updated() method.
I also try with deleted() method, that is also not getting call, here below is my code, please help.
public function __construct()
{
// echo "dsd"; die();
}
public function saved($user)
{
if ($user->wasRecentlyCreated == true) {
// Data was just created
$action = 'created';
} else {
// Data was updated
$action = 'updated';
}
UserAction::create([
'user_id' => Auth::user()->id,
'action' => $action,
'action_model' => $user->getTable(),
'action_id' => $user->id
]);
}
public function deleting($user)
{
dd($user);
}
}
public static function boot() {
parent::boot();
parent::observe(new \App\Observers\UserObserver);
}
Everything seems ok, so i guess something bigger is at fault here. Normally the best practice for registering observers is to do it in a provider class boot() method.
public function boot()
{
User::observe(UserObserver::class);
}
EDIT
For model events to trigger you have to use the model and not update the data through a query.
$discount = Discounts::find($request->edit_id);
$discount->fill($data);
$discount->save();

Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsToMany::routeNotificationFor()

I'm building a messaging system that notifies each user in the conversation when a reply is set.
MessageNotification.php
class MessageNotification extends Notification
{
use Queueable;
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
public function toArray($notifiable)
{
return [
'data' => 'Messenger notification'
];
}
}
InboxController
public function reply($hashedId, Request $request)
{
$this->validate($request, [
'body' => 'required',
]);
$conversation = Conversation::where('hashed_id', $hashedId)->first();
$users = $conversation->participants();
//dd($conversationUserIds);
$notifications = Notification::send($users, new MessageNotification());
$message = $conversation->messages()->create([
'sender_id' => auth()->user()->id,
'body' => $request->body,
]);
return new MessageResource($message);
}
Error
Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsToMany::routeNotificationFor()
Extra Information
I've had to build a custom Notifiable trait due to needing to use both Laravel Sparks notification system and Laravels stock notification system. Tutorial I got code from.
Custom notification trait
namespace App\Traits;
use Illuminate\Notifications\Notifiable as BaseNotifiable;
use App\Notifications\DatabaseNotification;
trait Notifiable {
use BaseNotifiable;
public function notifications() {
return $this->morphMany(DatabaseNotification::class, 'notifiable')->orderBy('created_at', 'desc');
}
}
Also note that $reciever->notify(new MessageNotification()); works just fine when sending a notification to one user. The only other solution I saw on this was: https://laracasts.com/discuss/channels/code-review/call-to-undefined-method-routenotificationfor-while-sending-email-to-multiple-users
I tried to implement that, but I'm using a database channel so it shouldn't make a difference.
This line here:
$users = $conversation->participants();
Will set the $users variable to a QueryBuilder instance (assuming you are using conventional Laravel relationships), rather than a collection of users. This is because the () at the end of a relationship builds the query but doesn't run it yet. So then when you call Notification::send($users, etc...) you are not passing in a collection of users; you are passing in a QueryBuilder object.
Try this instead:
$users = $conversation->participants;
Again - this is assuming that the participants method on the Conversation model is a standard laravel relationship.

Does Laravel 6 disable observers in factories / tests?

Ive just written an observer thats sends an e-mail whenever a user is created.
class UserObserver
{
public function created(User $user)
{
Mail::to($user)->send(new UserAccountCreated(
app('auth.password.broker')->createToken($user),
$user
));
}
}
I ran phpunit to test if my observer works, and it passed. However I was expecting to get an email for each time my tests create a user.
For example:
/** #test */
public function an_admin_can_view_all_clients()
{
$user = factory(User::class)->create(['is_admin' => true]);
$client = factory(Client::class)->create();
$client2 = factory(Client::class)->create();
$this->actingAs($user)->get(route('clients.index'))
->assertSuccessful()
->assertSee($client->name)
->assertSee($client2->name);
}
I would expect an email to be sent when that factory creates the user. But I don't receive one in Mailtrap.
Just wondering if and where laravel disables my observer being triggered when my factory creates that user.
No you have to disable it yourself by using Model::withoutEvents()
For example:
$user = User::first();
User::withoutEvents(function () use ($user) {
$user->delete();
});
Also in this specific case you can also use the Mail fake system provided by Laravel itself

Laravel Notifications - delay email sending and cancel if condition met

I have an app where I am sending a push notification which is fine if the user is logged into the application - however, if they're not / if they have not read the notification within X minutes I'd like to send them an email.
The way I am going about this is to use Laravel Notifications to create a mail, broadcast & database notification. On the toMail() method I'm returning a mailable with a delay -
public function toMail($notifiable)
{
return (new \App\Mail\Order\NewOrder($this->order))
->delay(now()->addMinutes(10));
}
After the minutes are up, the email will send but, before the send goes ahead I'd like to perform a check to see if the push/database notification has already been marked as read and if it has cancel the email send. The only way I can think to do this is to bind to the MessageSending event that is baked into Laravel -
// listen for emails being sent
'Illuminate\Mail\Events\MessageSending' => [
'App\Listeners\Notification\SendingEmail'
],
The only problem is this listener receives a Swift mail event and not the original mailable I was dispatching so I don't know how to cancel it. Any ideas and thanks in advance?
Class extends Notification
public function via($notifiable)
{
if($this->dontSend($notifiable)) {
return [];
}
return ['mail'];
}
public function dontSend($notifiable)
{
return $this->appointment->status === 'cancelled';
}
Class EventServiceProvider
protected $listen = [
NotificationSending::class => [
NotificationSendingListener::class,
],
];
Class NotificationSendingListener
public function handle(NotificationSending $event)
{
if (method_exists($event->notification, 'dontSend')) {
return !$event->notification->dontSend($event->notifiable);
}
return true;
}
For more details look article Handling delayed notifications in Laravel

Resources