How to run push notifications loop in the background laravel? - php

Now I need to send FCM push notifications to all my users on every record add, but it's take so much time to loop through them all, how to make the loop work in the background?
I'm using brozot/Laravel-FCM package

You might be triggering an event for push notification. so, while triggering an event for push notification you can push the notifications to queue. This queue is basically implemented in class declared in Listeners folder. example:
namespace App\Listeners;
use App\Events\EventName;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\FcmAdapter;
class NotificationClassName implements ShouldQueue
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param EventName $event
* #return void
*/
public function handle(EventName $event)
{
// code for sending FCM notification.
}
}
implements ShouldQueue is the way to implement queue.

Related

Laravel automatically change status in DB after date expires

I've got the tasks table, this table has status and deadline columns. How can status be changed to "expired" automatically when current date will become greater than task's deadline date? Is there any realtime event listeners in Laravel?
I guess that's how event listener class should look like, but I'm not sure what to do next.
<?php
namespace App\Events;
use App\Models\Task;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class DeadlineExpired
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* The task instance.
*
* #var \App\Models\Task
*/
public $task;
/**
* Create a new event instance.
*
* #param \App\Models\Task $task
* #return void
*/
public function __construct(Task $task)
{
$this->task = $task;
}
}
Since you're checking only the date. Your cron needs to run only once at midnight. Use Laravel Scheduler to do your Job.
First create a class
class UpdateTasks
{
public function __invoke()
{
// do your task here...e.g.,
Tasks::whereDate('deadline','<',today())->update(['status'=>'expired']);
}
}
Then in your app\Console\Kernel.php, schedule method-
$schedule->call(new UpdateTasks())->daily();
Finally configure a cron job at your server to run the schedule command daily at midnight.
php artisan schedule:run
There are Realtime event listeners but thes require a action to fire. Example are when a model is Created, Updated or Deleted then these events fire.
There is no built in "listener" to ping every model waiting for a field you defined to change.
If there is further logic you would like to fire when the Task becomes expired (like send email) then your best would be to run a check for any new expired Tasks using the scheduler.
The Scheduler runs every minute - set by cron.

How I make a Laravel Job to fail without restarting so I can use sentry to log the exception?

In my multi-server application I use laravel's Queueing system in order to run background jobs. Sometimes in my logic I want to make my job to throw an exception so I can log it via a sentry using the laravel library that offers.
So in my job:
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use App\Model\Etable\User;
use App\Model\User;
class MyJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
/**
* #var int
*/
private $user_id;
/**
* #param int $user The user that has opted or deopted for newsletter consent
*/
public function __construct(int $id)
{
$this->user_id = $user_id;
}
public function handle(): void
{
/**
* #var User
*/
$user=User::useWritePdo()->find($this->user_id);
if(empty($user)){
throw new \Exception("No such a user with user id: {$this->user_id}");
}
// Rest of logic here
}
}
Once the exception is thrown, I will to be logged into sentry but also it will keep on respawning as laravel's logic for jobs is supposed to do so.
In my case I think it as waste of resources to keep an respawning the MyJob in case that the user does not exist no value to keep on spawning because the logic itself cannot be performed in case that no user exists. On the other hand on any other error I want my job to keep on retrying till be able to sucessfully run again.
So how I can make my job not to respawn on specific errors? Even better would be as well if I can use the default logging method that laravel ofers in order to arbitary log an error into sentry as well via a sentry dedicated channel.
The best and easier approach is once the code failt to check how many times has already be executed:
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use App\Model\Etable\User;
use App\Model\User;
class MyJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
/**
* #var int
*/
private $user_id;
/**
* #param int $user The user that has opted or deopted for newsletter consent
*/
public function __construct(int $id)
{
$this->user_id = $user_id;
}
public function handle(): void
{
/**
* #var User
*/
$user=User::useWritePdo()->find($this->user_id);
if(empty($user)){
if ($this->attempts() > 1) {
return;
}
throw new \Exception("No such a user with user id: {$this->user_id}");
}
// Rest of logic here
}
}
This is being achieved via the:
if ($this->attempts() > 1) {
return;
}
So you throw once the exception, the exception is being logged into the Sentry and then on the second time that will be executed it will just exit and never respawn.
Please note that failed jobs won't be re-run unless you explicitly run
php artisan queue:failed
You can throw an exception and handle it in boot method of AppServiceProvider, deleting the job to avoid its further re-tries. See documentation on how to do it.

How to fix Laravel throwing FatalThrowableError when trying to broadcast a new event

I was following a tutorial to get Laravel to broadcast real-time but got stuck after just a few minutes of following along. Laravel throws the following message to me "Argument 1 passed to Illuminate\Database\Grammar::parameterize() must be of the type array, integer given, called in /home/vagrant/code/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php on line 775"
I've tried to redo the tutorial several times to make sure that I didn't miss a thing but the problem is still the same after several tries.
Even checking the stack trace and documentation didn't give me any clue.
I've uncommented the following line in config/app.php:
App\Providers\BroadcastServiceProvider::class,
I've added the following lines to App\Providers\EventServiceProvider:
use App\Events\RideCreated;
use App\Listeners\RideCreatedListener;
and the following after protected $listen = [ in the same file
RideCreated::class => [
RideCreatedListener::class,
],
this is the setup of the route used for testing (web.php):
Route::get('/test', function(){
event(new RideCreated());
return "test";
});
and this is how RideCreated.php looks like:
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class RideCreated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('RideCreated');
}
}
the listener (RideCreatedListener.php) looks like this:
<?php
namespace App\Listeners;
use App\Events\RideCreated;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class RideCreatedListener
{
/**
* Create the event listener.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param RideCreated $event
* #return void
*/
public function handle(RideCreated $event)
{
//
}
}
I expected when visiting the /test rout to see test on screen but actually got the error message displayed.
my first thought was that the ShouldBroadcast implementation in RideCreated.php somehow causes the problem since removing implement ShouldBroadcast makes the error disappear. the only problem is that removing it is no option since it's needed for Pusher to work.
This may sound strange, but we have been resolving this issue f2f. My answer is just for other people that might read this.
It turned out that the queue was not configured such that a default queue could be resolved by Laravel. The error was fixed by adding the $bradcastQueue property to the RideCreated class. See Broadcast Queue

How to launch an artisan command from a queued Job then delete the Job after execution using database driver

I'm using database driver to queue my jobs.
I need to call an artisan command from a queued Job and when the Job has finished I need to remove it from the queue.
This is my controller's code where I add the job in the queue
dispatch((new SendNewsletter())->onQueue('newsletter'));
This is my queued Job
<?php
namespace App\Jobs;
use App\Console\Commands\Newsletter;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class SendNewsletter implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct()
{
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
app()->make(Newsletter::class)->handle();
}
}
The artisan command I need to call is App\Console\Commands\Newsletter
and when the Job ends, this should remove it from the queue.
This is AppServiceProvider class
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot() {
Queue::after(function ($event) {
if ($event->job->queue == 'newsletter') {
$event->job->delete();
}
});
}
The Job is added correctly to the database queue and when I run php artisan queue:work the job is called multiple times endless.
seems that Queue::after's callback is never called.
any idea what am I missing ?
Probably your job fails and it is added to queue trying to finsh the work correctly. Try calling the command in your job like this
\Artisan::call('your:command');
Instead of:
app()->make(Newsletter::class)->handle();
Where "your:command" is the command name, that you gave in the command class:
protected $signature = 'email:send {user}';

Laravel echo pusher not receiving broadcast events

I've a problem that I'm trying to fix all day now. I've followed
this tutorial. The goal is to make a chat with Laravel echo, vue.js and pusher.
I've done everything exactly like the tutorial but for some reason I do not receive any events in my pusher console. Only the connection shows up:
But no events. The event that I fire looks like this:
<?php
namespace App\Events;
use App\Message;
use App\User;
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;
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* #var
*/
public $user;
/**
* #var
*/
public $message;
/**
* MessageSent constructor.
* #param User $user
* #param Message $message
*/
public function __construct(User $user, Message $message)
{
$this->user = $user;
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('chat');
}
}
I fire the event like this:
broadcast(new MessageSent($user, $message))->toOthers();
When I dd('test'); like this in my MessageSent event class:
public function broadcastOn()
{
dd('test');
return new PrivateChannel('chat');
}
The dd('test'); shows up in my network tab.
I'm using Laravel 5.4 and Vue.js 2.0 with Homestead. What could be going on here?!
It looks likes you are following this tutorial. I also had a hard time to figure it out. I already answered here. Can you please check it out?
I worked on typing feature in the chat system. Please take a look at the code on GitHub.
Let me know if you have any questions. Thanks :)
By the looks of your debug console screenshot you are never managing to subscribe to any channels, have you set up the necessary authentication for the private channel subscription?
The complete demo code for the tutorial that you've been following is on github so you might want to take a look at that and see where yours differs.
If you are on Laravel 5.4, make sure you have set-up the channel authentication.
For example, in your routes/channels.php file, there should be something like this:
Broadcast::channel('chat', function ($user) {
return true; // change this to your authentication logic
});

Categories