Laravel queued jobs processed immediately even with a delay - laravel-5

I'm currently developing my personal application that is about private servers (for example, Minecraft servers) and since querying the server takes some time, I decided to implement queued jobs. However, they are not working properly, and they run immediately when called even though they are delayed, causing a massive latency in a page request.
Here's my HomeController's index() which calls the job to update every server with a 30 seconds delay:
public function index()
{
$servers = Server::all();
foreach($servers as $server)
{
// Job Dispatch
$job = (new UpdateServer($server->id))->delay(30);
$this->dispatch($job);
}
return view('serverlist.index', compact('servers'));
}
The job class that updates the servers is the following:
class UpdateServer extends Job implements SelfHandling, ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $id;
public function __construct($id)
{
$this->id = $id;
}
public function handle(){
$server = Server::findOrFail($this->id);
// Preparing the packet
$test = new RAGBuffer();
$test->addChar('255');
$test->addChar('1');
$test->addShort(1 | 8);
// Finding the server
$serverGame = new RAGServer($server->server_ip);
// Get server information
$status = $serverGame->sendPacket($test);
$server->onlinePlayers = $status->getOnline();
$server->peakPlayers = $status->getPeak();
$server->maxPlayers = $status->getMax();
if (!$server->save()) {
// Error occurred
}
}
}
Whenever the HomeController's index() is run, there's a massive delay in the page request. I followed the tutorial at Laravel's Official Webpage, and I tried to find answers, but I didn't find anything.
So, what am I doing wrong? Why isn't the job getting delayed 30 seconds and then doing this in background in my server?
Also: The handle() is doing what it is supposed to. It queries the server, sends packets, and updates my database with the correct information.

You have to set up the queue driver you want to use in your project's root dir's .env file.
By default, the queue driver is sync which does exactly what you are describing, executing queues immediately.
You can choose of some different queue drivers, such as beanstalked or redis (which would be my choice). There's an excellent freebie on laracasts.com about setting up a beanstalked queue.
To view all available queue driver options in Laravel, have a look here.
Here's a .env example
APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync // <-- Put the desired driver here
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

This was driving me crazy for ages before I realised that Laravel 5.7 renamed QUEUE_DRIVER to QUEUE_CONNECTION in the .env files

For someone who has made the changes from previous answers and still didn't work, check the default value of the queue file like this: dd(Config::get('queue.default'))
For me it didn't change until flushing the config cache:
php artisan config:clear

To test locally you could set the driver to
QUEUE_DRIVER=database
And run php artisan queue:table.
And then php artisan migrate, so you would get your queue saved into the database, so you visually could see what`s going on.
And to run your queues, simply run php artisan queue:listen ... and leave it running as you do with artisan serve.

If you are running on php artisan serve, restart this and run php artisan serve again. This worked for me after hours trying to wonder what it was.
:)

If you are running tests against the queue service through phpunit, make sure that
<env name="QUEUE_DRIVER" value="X"/>
in phpunit.xml doesn't override your desired queue driver.

Ensure that
'default' => env('QUEUE_DRIVER', 'database'),
in file config/queue.php
And
QUEUE_DRIVER=database
in the .env file to ensure the database driver is used.

It's because the delay function takes an absolute date in the future
UpdateServer::dispatch($server->id)->delay(now()->addSeconds(30))

In my case, I had to implement ShouldQueue and use the Queueable trait:
class CustomNotification extends Notification implements ShouldQueue{
use Queueable;
...

Even if you have configured everything properly this can still happen. We had this problem with Laravel 5.4 where we created a lot of jobs, some delayed, and added them to the queue via Queue:bulk($jobs). This call and also Queue::push($job) completely ignore the delay and cause the job to be processed immediately.
If you want your job to be put on the queue as you configured it you must call dispatch($job).

This is the complete steps to create user API and store its history in jobs table.
In Jobs class:
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Repositories\Eloquent\ApiRepo as ApiRepo;
use Log;
use App\Models\User;
class UserProcess implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of times the job may be attempted and override the queue tries.
*
* #var int
*/
public $tries = 3;
/**
* Create a new job instance.
*
* #return void
*/
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
try {
// make api call
Log::info("inside handle".$this->user->id);
$apiRepo = new ApiRepo;
$response = $apiRepo->getUserDetails($this->user->id);
Log::info("Response".$response);
} catch (\Throwable $exception) {
if ($this->attempts() > 3) {
// hard fail after 3 attempts
throw $exception;
}
// requeue this job to be executes
// in 3 minutes (180 seconds) from now
$this->release(180);
return;
}
}
}
In Controller Class:
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\Models\User;
use App\Jobs\UserProcess;
use App\Models\UserHistory;
use Carbon\Carbon;
class UserController extends Controller
{
public function create(Request $request)
{
$rules = [
'first_name' => 'required|string|max:100',
'last_name' => 'required|string|max:100',
'email' => 'required|string|email|unique:users,email',
'phone_number' => 'required|string|max:10',
'address' => 'string',
'date_of_birth' => 'string|date_format:Y-m-d|before:today',
'is_vaccinated' => 'string|in:YES,NO',
'vaccine_name' => 'string|required_if:is_vaccinated,==,YES|in:COVAXIN,COVISHIELD'
];
$validator = Validator::make(array_map('trim', ($request->all())),$rules);
if($validator->fails()){
return response()->json($validator->errors());
}else{
$user = new User;
$user->first_name = $request->first_name;
$user->last_name = $request->last_name;
$user->email = $request->email;
$user->phone_number = $request->phone_number;
$user->address = $request->address;
$user->date_of_birth = $request->date_of_birth;
$user->is_vaccinated = $request->is_vaccinated;
$user->vaccine_name = $request->vaccine_name;
$user->save();
$token = $user->createToken('auth_token')->plainTextToken;
if($user->save()){
$job = (new UserProcess($user))->delay(Carbon::now()->addMinutes(1));
$this->dispatch($job);
return response()
->json(['data' => $user,'status' => '200','message' => 'User Added Successfully','access_token' => $token, 'token_type' => 'Bearer']);
}else{
return response()
->json(['data' => $user,'status' => '409','message' => 'Something went wrong!']);
}
}
}
}
In ApiRepo Class:
namespace App\Repositories\Eloquent;
use App\Repositories\ApiInterface;
use Illuminate\Http\Request;
use App\Http\Requests;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\UserHistory;
use Log;
class ApiRepo implements ApiInterface {
public function getUserDetails($userid) {
Log::info('User ID - '.#$userid);
$user_history = new UserHistory();
$save_user = User::find($userid);
Log::info('User Json Data - '.#$save_user);
$user_history->user_id = $userid;
$user_history->first_name = $save_user->first_name;
$user_history->last_name = $save_user->last_name;
$user_history->email = $save_user->email ;
$user_history->phone_number = $save_user->phone_number;
$user_history->address = $save_user->address;
$user_history->date_of_birth = $save_user->date_of_birth;
$user_history->is_vaccinated = $save_user->is_vaccinated;
$user_history->vaccine_name = $save_user->vaccine_name;
$user_history->save();
if($user_history->save()){
Log::info('User history Saved!');
}
}
}

Related

Laravel mailable, data not being passed to Blade when implements ShouldQueue

Im trying to send an email, with ShouldQueue, and passing data into the email blade view, but it fails if i implement ShouldQueue.
See below for my code.
The code as it is below, does execute the mail and places it in jobs queue (database driver). It works and i can see the job in jobs table.
Then i run php artisan queue:work to start worker on my computer.
When worker tries to execute job, it fails. It is now removed from jobs table and new entry to failed_jobs table. The email is not sent.
In failed_jobs table, exception column, i get this error (stacktrace removed)
Next Facade\Ignition\Exceptions\ViewException: Undefined variable $data (View: /Users/xxx/xxx/xxx/resources/views/mails/ContactForm.blade.php) in /Users/xxx/xxx/xxx/resources/views/mails/ContactForm.blade.php:2
IF I remove "implements ShouldQueue" from the ContactFormMail class, everything works and email is being sent, but then, the email is not being put in the queue.
I have tried passing data both ways described in Laravel docs and i have also tried restarting worker.
Any ideas where i go wrong?
If it was not clear, I want to pass data to ContactForm.blade.php and also place email in queue.
Here is my code
Controller:
public function submit()
{
$this->validate();
$data = [
'name' => $this->name,
'email' => $this->email,
'message' => $this->message,
];
try {
Mail::to('xxx#xxx.xx')->send(new ContactFormMail($data) );
$this->name = "";
$this->email = "";
$this->message = "";
$this->status = "Message sent!";
} catch (Exception $e) {
$this->status = "Something went wrong!";
}
}
ContactFormMail class:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class ContactFormMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('mails.ContactForm');
}
}
ContactForm blade file:
<div>
{{$data['name']}}<br>
{{$data['email']}}<br>
{{$data['message']}}<br>
</div>
Please try restarting the queue worker. Probably something got stuck. After restarting the queue it "should" work.
php artisan queue:restart

Laravel Guzzle not perforning any action as requested in Cron Job

I am developing a web application using Laravel-5.8. Also, I am using guzzlehttp/guzzle-6.3 to consume an external api and save it in my local database.
travelupdate.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp;
use GuzzleHttp\Client;
use App\User;
use App\Activity;
use Avatar;
use Storage;
use App\Travel;
class travelsupdate extends Command {
protected $signature = 'command:travelsupdate';
protected $description = 'travelsupdate';
public function __construct() {
parent::__construct();
}
public function handle()
{
$client = new Client();
$res = $client->request('GET','https://api.abcdef.net/travels/v4/sample');
$trips = json_decode($res->getBody(), true);
foreach($trips as $trip) {
Trip::updateOrCreate([
'trip_id' => $trip->trip_id
],
[
'trip_number' => $trip->trip_no,
'truck_no' => $trip->t_no,
'truck_reg_no' => $trip->reg_no,
'trailer_no' => $trip->trailer_no,
'contract_no' => $trip->contract_no,
'contract' => $trip->contract_name,
'driver_id' => $trip->driver_id,
'driver_name' => $trip->driver_name,
'loading_date' => date_format($trip->loading_date, "Y-m-d"),
'loading_from' => $trip->loading_from
]);
}
}
}
app\Console\Kernel.php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\User;
use App\Activity;
use Avatar;
use Storage;
use Mail;
use App\Travel;
use App\Audit;
use Carbon\Carbon;
// use \Carbon\Carbon;
class Kernel extends ConsoleKernel
{
protected $commands = [
'App\Console\Commands\travelsupdate',
];
protected function schedule(Schedule $schedule)
{
$schedule->command('command:travelsupdate')
->hourly();
}
/**
* Register the commands for the application.
*
* #return void
*/
protected function commands()
{
// $this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
What I want to achieve is that consume the external api using Guzzle GET request. Then save it into the local database. If data already exists (using trip_id), it updates. I set the cron job to every one hour.
I observe that nothing is happening, and no data is being saved to the local database. When I tested on POSTMAN, it displays the data from the api.
However, Laravel log file is not showing me any error.
How do I resolve this?
NOTE: There is no security setting on the external api.
You are retreiving array from api and then you are using it as an object.
Solution 1:
do not decode it as an array
$trips = json_decode($res->getBody());
Solution 2:
use decoded value as an array
foreach($trips as $trip) {
Trip::updateOrCreate([
'trip_id' => $trip['trip_id']
],
[
'trip_number' => $trip['trip_no'],
'truck_no' => $trip['t_no'],
'truck_reg_no' => $trip['reg_no'],
'trailer_no' => $trip['trailer_no'],
'contract_no' => $trip['contract_no'],
'contract' => $trip['contract_name'],
'driver_id' => $trip['driver_id'],
'driver_name' => $trip['driver_name'],
'loading_date' => date_format($trip['loading_date'], "Y-m-d"),
'loading_from' => $trip['loading_from']
]);
}
Everything else seems to be ok.
If you want to display errors in your log you can do it manually:
try {
...
} catch (\Exception $e){
\Log::error($e->getMessage());
}
Not sure if you are misusing the terminology but you don't set the "cron job" for every hour. You set the "cron job" that calls the Laravel Scheduler to run every minute. The scheduler then decides every time it is ran what needs to be run based on how you setup the calls in the scheduler.
To test this you can adjust your scheduled command to run every minute or 5 minutes lets say and manually call the scheduler yourself, php artisan schedule:run, from the command line.

Laravel mail error: Process could not be started [The system cannot find the path specified. ]

I'm developing a new Laravel application. When I'm using mail to send messages via contact form in my website, I'm getting the following error:
Process could not be started [The system cannot find the path specified. ]
I'm developing in my local environment but using my business mail to get messages.
My controller:
namespace App\Http\Controllers;
use App\SendMessage;
use App\Mail\SendEmail;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Controller;
use Session;
class SendMessageController extends Controller
{
public function store(Request $request) {
$this->validate($request, [
"email" => "required|email",
"message" => "min:10",
"subject" => "min:3"
]);
$name = $request->name;
$email = $request->email;
$company = $request->company;
$subject = $request->subject;
$message = $request->message;
Mail::to("audit#auditors.uz")->send(new SendEmail($subject, $message));
Session::flash("success", "Your email was sent");
return back();
}
}
?>
My mailing function:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $sub;
public $mese;
public function __construct($subject, $message)
{
$this->sub = $subject;
$this->mes = $message;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$e_subject = $this->sub;
$e_message = $this->mes;
return $this->view('emails.contact', compact("e_message"))->subject($e_subject);
}
}
?>
My .env file:
MAIL_DRIVER=mail
MAIL_HOST=mail.auditors.uz
MAIL_PORT=465
MAIL_USERNAME=audit#auditors.uz
MAIL_PASSWORD=mypassword
MAIL_ENCRYPTION=tls
I googled it a lot, but did not find the appropriate answer. If anybody of you can help me, I'll be really happy. Because I've been looking for a solution for a long time.
Your MAIL_DRIVER is set to mail, which does not exist by default. If you are using an SMTP mail server, you should use smtp as the driver.
Please make sure that your email provider supports port 465 and TLS encryption. Most of the providers support these automatically though.
Use MAIL_DRIVER="smtp"
Do not forget to Re-Serve or clear cache of Laravel
[ php artisan optimize:clear / php artisan config:clear / php artisan cache:clear ]

Problems trying to send email with Laravel 5.5

I made an API with Laravel and it is registering the data correctly
Now I'm trying to get this to trigger registration confirmation emails, however these are not going
I'm using Mailtrap for the tests
In .env I put like this:
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=291ac7fdcf52bb
MAIL_PASSWORD=610b2e5e9782d3
No Http/Mail/DataManifestMail.php tenho:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class DataManifestMail extends Mailable
{
use Queueable, SerializesModels;
protected $inputs;
public function __construct($inputs)
{
$this->inputs = $inputs;
}
public function build()
{
return $this->view('mails.data');
}
}
In views/mails/data.blade.php I only have one warning message:
<h1>Test e-mail</h1>
Then in my Http/Controller/ManifestationController.php I put it like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Manifestation;
use App\Mail\DataManifestationMail;
use Mail;
class ManifestationController extends Controller
{
private $manifestation;
public function __construct(Manifestation $manifestation)
{
$this->manifestation = $manifestation;
}
public function store(Request $request)
{
$nr = Manifestation::max('nrmanifestation');
$this->manifestation->nrmanifestation = $nr + 1;
$this->manifestation->dspass = $request->dspass;
$this->manifestation->eeemail = $request->eeemail;
$this->manifestation->address = $request->address;
$this->manifestation->name = $request->name;
$this->manifestation->latitude = $request->latitude;
$this->manifestation->longitude = $request->longitude;
$this->manifestation->save();
Mail::to('vazag#c1oramn.com')->send(new DataManifestationMail());
return response()->json([
'result' =>
$this->manifestation->nrmanifestation
]);
}
}
I have reread the documentation and my code several times and have not found any errors
What can it be?
There might be multiple reasons:
configuration is cached (use php artisan config:cache)
you used invalid mailtrap data (you should have log in laravel.log file)
your server has somehow blocked 2525 port
Also looking at your code it seems you miss passing $input parameter to constructor. You have:
public function __construct($inputs)
but you run:
->send(new DataManifestationMail());
without passing any value

Send email in background : Laravel 5.4

I am using an inbuilt code in Laravel to send Email Notification. Code is below. I am using smtp to send email
class RegisterNotification extends Notification
{
use Queueable;
public function __construct($token)
{
$this->token = $token;
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('hi');
}
public function toArray($notifiable)
{
return [
//
];
}
}
Here the problem is, it takes around 5 seconds to complete the process and control does not come back. I am assuming that if it come back and do the email sending work in background...it would save a lot of time.
Is there any inbuilt work to do the same? I meant, control should come back and should say email sent...and then it should do the work in background.
Email Sending code in Controller
class apiRegisterController extends Controller
{
public function Register(RegisterRequest $request) {
$RegisterNotification = new RegisterNotification($Token);
$User->notify($RegisterNotification);
}
}
Code for Queue
Controller Code
$job = (new SendForgotPasswordEmail($Data))->onConnection('database');
dispatch($job);
Job
class SendForgotPasswordEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $Data;
public $tries = 5;
public function __construct($Data)
{
$this->Data = $Data;
}
public function handle()
{
$Token = $this->Data["Token"];
$User = $this->Data["User"];
$RegisterNotification = new RegisterNotification($Token);
$User->notify($RegisterNotification);
}
}
Step 1: Change class RegisterNotification extends Notification to class RegisterNotification extends Notification implements ShouldQueue
Step 2: Implement a queue driver. In your config/queue.php make sure your driver is not set to sync like so: 'default' => env('QUEUE_DRIVER', 'sync'), and make sure your .env doesnt have QUEUE_DRIVER=sync. You can look at the Laravel documentation for queues to choose an appropriate queue driver
You can use the build-in API.
$user = User::findOrFail($id);
Mail::queue('emails.welcome', $data, function ($message) use ($user){
$message->from('hello#app.com', 'Your Application');
$message->to($user->email, $user->name)->subject('Your Reminder!');
});
But first you have to configure the queues.
Add in your .env file the line QUEUE_DRIVER=sync and then write on the terminal php artisan queue:listen.
If you want the queue to run forever on the server use Supervisor. Queues documentation explains how you can use it.
you can use laravel job queue https://laravel.com/docs/5.4/queues
Mail::queue(

Resources