Laravel queue is not doing job in the background - php

I am trying to use laravel queue to send bulk emails. So far I have written down the logic and it works fine, but the problem is that when I wrote the logic in controller it takes a lot of time so I thought of using jobs but again the problem persists.
My Problem
My problem is that I am not able send the email in background even if I am using queue.
Controller
public function newsletter(Request $request)
{
//dd($request->all());
dispatch(new SendEmail($request));
Session::flash('message', 'Email Sent');
Session::flash('class', 'success');
return redirect()->route('news');
}
Jobs
public function handle(Request $request)
{
//
$data = array(
'message' => $request->message,
'subject' => $request->subject,
'file' => $request->file("file")
);
$teachingLevel = $request->highest_teaching_level;
$school = $request->school;
$province = $request->province;
$district = $request->district;
$subject = $request->subject;
if ($teachingLevel != "" && $school != "" && $province != "" && $district != "") {
$email = User::where('highest_teaching_level', $teachingLevel)->where('current_school_name', $school)->where('address','LIKE', '%'.$province.'%')->where('address','LIKE', '%'.$district.'%')->pluck('email');
}else{
$email = User::pluck('email');
}
foreach($email as $e)
{
Mail::to($e)->send(new NewsLetter($data, $subject));
}
}
The email is sent but it doesn't happen in the background. Maybe it has to do with the way I have passed $request variable in the handle() function.
Any help will be appreciated. Thanks!

Here is how I'm using Laravel jobs in my project:
SampleJob.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Services\SampleService;
class SampleJob implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// if you omit this value, you'll be in trouble, believe me
public $tries = 1;
private $param;
public function __construct($param) {
$this->param = $param;
}
public function handle(SampleService $service) {
// do something with $this->param
// Also, notice that you can type hint classes in handle function declaration for DI
$service->doSomething($this->param);
}
}
SampleController.php
namespace App\Http\Controllers;
use App\Jobs\SampleJob;
class SampleController extends Controller {
public function sampleMethod(Request $request) {
$param = $request->input('param');
SampleJob::dispatch($param); // $param will be passed to SampleJob constructor
// ...
}
}
A few points worth to note are:
Read the comments in my code snippets
If you use db-based queue, migrate first with php artisan queue:table && php artisan migrate
Create jobs with artisan command: php artisan make:job Sample
Don't forget to run queue worker: php artisan queue:work. To make it run in background: sudo nohup php artisan queue:work > ./storage/logs/queue-worker.log &
Highly recommended: In deployment, use Supervisor to keep php artisan queue:work running in the background
If you manage to make the job work, all delayed (queued but not handled because of misconfiguration or not starting queue worker) works will be instantly executed.
Common pitfalls:
If you don't set $tries param, and somehow your job throws an error, laravel will try to retry that job again and again until your database is down :(
If http user and php user is different, and if you used Log in your job, nine out of ten times you face permission problem on storage directory. To avoid this problem, add 'permission' => '0666' to your log channel setting in config/logging.php
Queue worker does not detect your code change, thus restart queue worker by php artisan queue:restart after you make some change to code base.
My laravel version: 5.8

If you are going to work with “database” connection is neccesary to run the next migrations:
php artisan queue:table
php artisan migrate
Also have an Event and a Listener that implements the “ShouldQueue” interface, and by last register the event associated with the listener or listeners in your “providers/EventProvider.php” path and in “EventProvider.php” file add your event and listeners with the next notation as example:
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
Its important to understant few points related to following queue:restart command
php artisan queue:restart
For that to work, you need to be running the Queue Listener:
php artisan queue:listen
Ref: https://medium.com/ariel-mejia-dev/run-queues-in-the-background-on-development-in-laravel-8d697d812f29

Related

Laravel 8 - Programmatically run migration and seeder when database has no tables

After connecting to database, I want to programmatically run migration and seeder once the project detects that the database doesn't have any tables.
I think what I should do is inject the code below somewhere, but I don't know what file I should edit.
if (!Schema::hasTable('users')) {
$init_met = ini_get('max_execution_time');
set_time_limit(300);
Artisan::call('migrate:fresh');
Artisan::call('db:seed');
set_time_limit($init_met);
}
Or, is there an alternative way to do this instead of injecting the code?
Thanks in advance.
i'd suggest you to look at composer scripts section - there are a lot of events that could be used as trigger for your code. for example post-autoload-dump event fired after composer dumpautoload which is fired in most common calls like install, update or by itself. the benefit of using composer events is that you don't need to check for existing tables on each request.
the most easy way to achieve this is to create custom artisan command
php artisan make:command PrepareEmptyDatabase
then in app\Console\Commands\PrepareEmptyDatabase.php
<?php
namespace App\Console\Commands;
use Exception;
use App\Http\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;
class PrepareEmptyDatabase extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'db:prepare-empty';
/**
* The console command description.
*
* #var string
*/
protected $description = 'check for users table and run migrations and seed if has not';
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
// don't forget to remove this if database user
// don't have access to dba tables
if (Schema::hasTable('users')) {
return Command::SUCCESS;
}
/*
// if your user doesn't have permission to access to dba tables
// you can simply try to do any request to users table
$needActions = false;
try {
User::first();
} catch (Exception $ex) {
$needActions = true;
}
if (!$needActions) {
return Command::SUCCESS;
}
*/
$init_met = ini_get('max_execution_time');
set_time_limit(300);
Artisan::call('migrate:fresh');
Artisan::call('db:seed');
set_time_limit($init_met);
return Command::SUCCESS;
}
}
and the last step is tie this command with composer event, so in composer.json
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"#php artisan package:discover",
"#php artisan db:prepare-empty"
]
},
now any time you install/update composer dependencies or just run composer dumpatoload application will run your custom command. or you can stick with any of provided in composer docs event on your taste
about running the same code after npm run dev
i'm not quite sure about place to search, guess its about webpack events, but your question tagged with laravel so i assume you're using laravel-mix and there are event hooks
quick googling says that nodejs can run bash scripts using nodejs child process
// webpack.mix.js
const exec = require('child_process')
// or import exec from 'child_process'
mix
.js('resources/assets/js/app.js', 'public/js')
// other configs
.after(() => {
exec.execSync('php artisan db:prepare-empty')
})
pay attention that this code will run on any mix even including npm run prod for example and i don't actually understand why do you need it in frontend build event (if only for testing purpose and even then its questionable)

How can I run my php script in background to send email notification

I can send email to all users after submitting the form but it takes some time. What I want to achieve is to skip that task(sending email) and run it in background after submitting the form. So, users can do other things asides waiting to finish the task(sending email).
I tried to look at https://laravel.com/docs/4.2/queues but I'm beginner in Laravel and I don't understand well the documentaion. by the way I'm using old laravel version which is laravel 4.2.
APKFileController.php
$users = User::All();
foreach($users as $user) {
$data = array(
'apk_name' => Input::get('name'),
'version' => $apk->version,
'download_link' => Input::get('remarks'),
'subject' => 'v' . $apk->version . ' is now available.',
'message' => 'A new version of APK has been released!',
);
$this->userMailer->sendToApp($user, compact('data'));
}
}
UserMailer.php
<?php namespace Sample\Mailers;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use User;
class UserMailer extends Mailer
{
public function sendToApp(User $user, $data)
{
$subject = $data['data']['subject'];
$view = 'emails.clients.apkInfo';
return $this->sendTo($user, $subject, $view, $data);
}
}
Mailer.php
<?php namespace Sample\Mailers;
use Illuminate\Mail\Mailer as Mail;
abstract class Mailer {
private $mail;
function __construct(Mail $mail) {
$this->mail = $mail;
}
public function sendTo($user, $subject, $view, $data = [] )
{
$this->mail->queue($view, $data, function ($message) use ($user, $subject) {
$message->to($user->email)->subject($subject);
});
}
}
You can create a artisan command, just as described here:
Building A Command
Generating The Class
To create a new command, you may use the command:make Artisan command, which will generate a command stub to help you get started:
Generate A New Command Class
php artisan command:make FooCommand
By default, generated commands will be stored in the app/commands
directory; however, you may specify custom path or namespace:
php artisan command:make FooCommand --path=app/classes --namespace=Classes
When creating the command, the --command option may be used to assign
the terminal command name:
php artisan command:make AssignUsers --command=users:assign
Then you create a crontab schedule to run your command as described here:
Add a Cron JobPermalink
Open a crontab for your user in a text editor (vi in most distributions):
crontab -e
Note:
To change the text editor used, add the environment variable to your ~/.bashrc file, exchanging vim for nano, or whatever other
terminal-based editor you prefer.
export EDITOR=vim
Add the Cron job, save, and exit. The crontab will be saved in /var/spool/cron/crontabsas a crontab specific to the user who created
it. To later remove a Cron job from it, delete the line from the
user’s crontab file.
Or you can simple put it in a Queuing system. Refer to this https://laravel.com/docs/4.2/queues

Laravel queue keep processing multiple times for a job

Below is what's happening when i run php artisan queue:listen and at my job table only have one job
and this is my code :
public function handle(Xero $xero)
{
$this->getAndCreateXeroSnapshotID();
$this->importInvoices($xero);
$this->importBankTransaction($xero);
$this->importBankStatement($xero);
$this->importBalanceSheet($xero);
$this->importProfitAndLoss($xero);
}
In order for a job to leave the queue, it must reach the end of the handle function -- without errors and exceptions.
There must be something breaking inside one or more of your functions.
If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. https://laravel.com/docs/5.8/queues
The same behavior can be achieved with
$this->release()
If you can't figure out what is breaking, you can set your job to run only once. If an error is thrown, the job will be considered failed and will be put in the failed jobs queue.
The maximum number of attempts is defined by the --tries switch used
on the queue:work Artisan command. https://laravel.com/docs/5.8/queues
php artisan queue:work --tries=1
If you are using the database queue, (awesome for debugging) run this command to create the failed queue table
php artisan queue:failed
Finally, to find out what is wrong with your code. You can catch and log the error.
public function handle(Xero $xero)
{
try{
$this->getAndCreateXeroSnapshotID();
$this->importInvoices($xero);
$this->importBankTransaction($xero);
$this->importBankStatement($xero);
$this->importBalanceSheet($xero);
$this->importProfitAndLoss($xero);
}catch(\Exception $e){
Log::error($e->getMessage());
}
}
You could also set your error log channel to be slack, bugsnag or whatever. Just be sure to check it. Please don't be offended, it's normal to screw up when dealing with laravel queues. How do you think I got here?
Laravel try to run the job again and again.
php artisan queue:work --tries=3
Upper command will only try to run the jobs 3 times.
Hope this helps
In my case the problem was the payload, I've created the variable private, but it needs to by protected.
class EventJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
// payload
protected $command;
// Maximum tries of this event
public $tries = 5;
public function __construct(CommandInterface $command)
{
$this->command = $command;
}
public function handle()
{
$event = I_Event::create([
'event_type_id' => $command->getEventTypeId(),
'sender_url' => $command->getSenderUrl(),
'sender_ip' => $command->getSenderIp()
]);
return $event;
}
}
The solution that worked for me to delete the job after pushing them into the queue.
Consider the e.g.
class SomeController extends Controller{
public function uploadProductCsv(){
//process file here and push the code inot Queue
Queue::push('SomeController#processFile', $someDataArray);
}
public function processFile($job, $data){
//logic to process the data
$job->delete(); //after complete the process delete the job
}
}
Note: This is implemented for laravel 4.2

How to sleep PHP(Laravel 5.2) in background

I have created an artisan command that I want to run immediately after a method called. But the command contains a sleep(); command. I want to run that artisan command in background because the method need to return immediately response to user. My sample code is a below:
In route file
Route::get('test', function(){
Artisan::queue('close:bidding', ['applicationId' => 1]);
return 'called close:bidding';
});
In close:bidding command
public function handle()
{
$appId = $this->argument('applicationId');
//following code line is making the problem
sleep(1000 * 10);
//close bidding after certain close time
try{
Application::where('id', $appId)->update(['live_bidding_status' => 'closed']);
}catch (\PDOException $e){
$this->info($e->getMessage());//test purpose
}
$this->info($appId.": bid closed after 10 seconds of creation");
}
Problem
When I hit /test url the return string called close:bidding is being shown after 10 seconds browser loading, because there is a sleep(10 * 1000) inside the command.
What I want
I want to run the command in background. I mean when I hit the /test url it should immediately show up the called close:bidding but close:bidding command will be running in background. After 10 seconds it will update the application though the front-end user won't notice anything anything about it.
Partial Questions
Is it somehow related to multi threading?
Is it something that cannot be solve using PHP, should I think differently?
Is there any way to solve this problem even using Laravel queue?
Create a job
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class JobTest implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
private $payload = [];
public function __construct($payload)
{
$this->payload = $payload;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$appId = $this->payload('applicationId');
//following code line is making the problem
sleep(1000 * 10);
}
}
To push job background queue
Route::get('test', function(){
dispatch((new JobTest)->onQueue('queue_name'));
return 'called close:bidding';
});
At this state we have job and you dispath the job to the queue. But it not processed yet. We need queue listener or worker to process those job in background
php artisan queue:listen --queue=queue_name --timeout=0
OR
php artisan queue:work --queue=queue_name --timeout=0 //this will run forever
Note:
May be you can try supervisord,beanstakd for manage queue
For more info refer this
If you don't need all advantages of proper queue, which come at a price, it may be sufficient to use terminate middleware. It will do the job after response was sent to the browser.

Delay laravel push notification for 5 second

I am using ""davibennun/laravel-push-notification": "dev-laravel5" " for sending push notification. What i want is delay in sending notification after hit but dont want to stop the process. Is there any idea how can i do this or is this possible?
Following is the code to send push notification:
$pushNotification = PushNotification::app('appNameAndroid')->to($token);
$pushNotification->adapter->setAdapterParameters(['sslverifypeer' => false]);
$pushNotification->send($message);
Thanks in advance.
I found how to do this.
Following are the steps.
Run the following command
php artisan queue:table
php artisan migrate
Change .env
QUEUE_DRIVER=database
Create a job
php artisan make:job JobName
//In Job file
I have mentioned 2 protected variable in my job file
$message,$deviceToken
In _construct i assigned a value to the above variables.
public function __construct($deviceToken, $message)
{
$this->deviceToken = $deviceToken;
$this->message = $message;
}
In handle method
$pushNotification = PushNotification::app('appNameAndroid')->to($this->deviceToken);
$pushNotification->adapter->setAdapterParameters(['sslverifypeer' => false]);
$pushNotification->send($this->message);
//In my controller
$job = (new JobName($deviceToken, $message))->delay(10);
$this->dispatch($job);

Categories