Laravel Job for Bulk Mail - php

this is the first time I am using laravel queue jobs, and somehow i could not get it working.
This is my mail class:
class TopluKabulMektubu extends Mailable
{
use Queueable, SerializesModels;
public $letter;
public function __construct(AcceptLetter $letter)
{
$this->letter = $letter;
}
public function build()
{
$letter = $this->letter;
return $this->subject('Mail Title')
->view('emails.topluKabulSon')
->attach(public_path($letter->pdf), [
'as' => 'AcceptanceLetter.pdf',
'mime' => 'application/pdf',
]);
}
}
And I created a function inside my AcceptanceLetter model to use mail easier :
public function sendAcceptanceLetter(){
Mail::to('******#gmail.com')->queue(new TopluKabulMektubu($this));
if(Mail::failures()){
$this->email_send = 2;
$this->save();
}else{
$this->email_send = 1;
$this->save();
}
}
I created a queue table with php artisan queue:table and migrated, also changed queue connection to database from env file.
And my job file:
class QueueJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $letter;
public function __construct($letter)
{
//
$this->letter = $letter;
}
public function handle()
{
$this->letter->sendAcceptanceLetter();
}
}
Web route triggers my job :
Route::get('/topluDeneme', [PaginationController::class, 'topluQueue']);
And the controller:
public function topluQueue(){
$letters = AcceptLetter::where('email', '!=', null)->where('passport_number','!=','0');
foreach($letters as $letter){
QueueJob::dispatch($letter);
}
}
I expect when i run php artisan queue:listen on terminal and go to /topluDeneme route, mails to be sent. But nothing happens on terminal, mails not sent and nothing changes on job datatable.

I found whats wrong with my code. It seems I forgot to use get() in the controller, here is the correct version :
public function topluQueue(){
$letters = AcceptLetter::where('email', '!=', null)->where('passport_number','!=','0')->get();
foreach($letters as $letter){
QueueJob::dispatch($letter);
}
}

Related

how to send a scheduled report via email to user in laravel

i am trying to send a schedule report (containing charts and graphs) every week to the user via email using Laravel
so , what is the best way to do that
thank you
hi you can make a command and register it in the app/Console/Kernel.php
1)
app/Console/Commands/Reporter.php
use Mail;
use Illuminate\Console\Command;
class Reporter extends Command {
protected $name = 'Reporter';
protected $description = 'This is a test.';
public function __construct()
{
parent::__construct();
}
public function fire()
{
Mail::send('emails.pdf', function($message) use ($path) {
$message->to('testuser1#test.com', 'testuser')->subject
('pdf Report '. Carbon::now()->format('m-d-Y'));
$message->attach($path);
$message->from('no-reply#test.com','test-tech');
});
Storage::disk('pdf')->delete(Storage::disk('pdf')->allFiles());
$this->info('Mail has fired.');
}
}
2.app/Console/Kernel.php
protected $commands = [
'App\Console\Commands\Inspire',
'App\Console\Commands\Reporter',
];
protected function schedule(Schedule $schedule)
{
$schedule->command('inspire')->hourly();
$schedule->command('Reporter')->weekly();
}
you can create pdf with mpdf
https://github.com/mccarlosen/laravel-mpdf
use PDF;
class ReportController extends Controller {
public function generate_pdf()
{
$data = [
'foo' => 'bar'
];
$pdf = PDF::loadView('pdf.document', $data);
return $pdf->stream('document.pdf');
}
}

Detecting if a job has been dispatched using dispatchNow

I have a job that under certain circumstances calls another job
<?php namespace App\Jobs;
use App\Models\Account;
class EnqueueScheduledDownloads implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $account;
public function __construct(Account $account)
{
$this->account = $account;
}
public function handle()
{
foreach($this->account->pending_downloads as $file)
{
DownloadFile::dispatch($file);
}
}
}
While the download job is usually executed in a queue; there are times, for example during testing, where it would make my life much easier if the whole chain was processed synchronously in a blocking fashion. I would like to be able to do something like this:
public function handle()
{
foreach($this->account->pending_downloads as $file)
{
if($this->getDispatchMode() == 'sync') {
DownloadFile::dispatchNow($file);
} else {
DownloadFile::dispatch($file);
}
}
}
Is this possible?
After a bit of poking around I was able to answer my own question. Yes it is possible; if a job is dispatched via dispatchNow() the job property of the Queueable object will be null, whereas if it is dispatched on a connection using dispatch() it will be set to an implementation of Illuminate\Contracts\Queue\Job. So the handle method can be changed as such:
public function handle()
{
foreach($this->account->pending_downloads as $file)
{
if(is_null($this->job)) {
DownloadFile::dispatchNow($file);
} else {
DownloadFile::dispatch($file);
}
}
}
And it will work as expected. I was able to find this solution by creating a new job:
<?php namespace App\Jobs;
class TestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
}
public function handle()
{
dump(get_object_vars($this));
}
}
and dispatching it on various queue and connections as well as with dispatchNow() and observing the output. Furthermore it is possible to retreive the connection and queue the job was dispatched on from the $this->job:
public function handle()
{
echo $this->job->getConnectionName();
echo $this->job->getQueue();
}

How to test Laravel 5 jobs?

I try to catch an event, when job is completed
Test code:
class MyTest extends TestCase {
public function testJobsEvents ()
{
Queue::after(function (JobProcessed $event) {
// if ( $job is 'MyJob1' ) then do test
dump($event->job->payload());
$event->job->payload()
});
$response = $this->post('/api/user', [ 'test' => 'data' ], $this->headers);
$response->assertSuccessful($response->isOk());
}
}
method in UserController:
public function userAction (Request $request) {
MyJob1::dispatch($request->toArray());
MyJob2::dispatch($request->toArray());
return response(null, 200);
}
My job:
class Job1 implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $data = [];
public function __construct($data)
{
$this->data= $data;
}
public function handle()
{
// Process uploaded
}
}
I need to check some data after job is complete but I get serialized data from
$event->job->payload() in Queue::after And I don't understand how to check job ?
Well, to test the logic inside handle method you just need to instantiate the job class & invoke the handle method.
public function testJobsEvents()
{
$job = new \App\Jobs\YourJob;
$job->handle();
// Assert the side effect of your job...
}
Remember, a job is just a class after all.
Laravel version ^5 || ^7
Synchronous Dispatching
If you would like to dispatch a job immediately (synchronously), you may use the dispatchNow method. When using this method, the job will not be queued and will be run immediately within the current process:
Job::dispatchNow()
Laravel 8 update
<?php
namespace Tests\Feature;
use App\Jobs\ShipOrder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Bus;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped()
{
Bus::fake();
// Perform order shipping...
// Assert that a job was dispatched...
Bus::assertDispatched(ShipOrder::class);
// Assert a job was not dispatched...
Bus::assertNotDispatched(AnotherJob::class);
}
}
This my generic method, using a route
Route::get('job-tester/{job}', function ($job) {
if(env('APP_ENV') == 'local'){
$j = "\\App\Jobs\\".$job;
$j::dispatch();
}
});

Test Queue functionality?

According to the Laravel Documentation, I can use Queue::fake(); prevent jobs from being queued.
What is not clear how to test (PHPUnit) a few methods in the Job Class while it is not being queued.
For example:
class ActionJob extends Job
{
public $tries = 3;
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function handle()
{
if ($this->data['action'] == "deleteAllFiles") {
$this->deleteAllFiles();
}
}
protected function deleteAllFiles()
{
//delete all the files then return true
// if failed to delete return false
}
}
Here is example I want to test deleteAllFiles() - do I need to mock it?
The idea of using the fakes is that they're an alternative to mocking. So, yes, if you want to mock that deleteAllFiles() was called, then I don't believe you can do that with the fake.
However, you can assert that a certain attribute exists on the job.
One thing, it's not in your example, but make sure your job is implementing \Illuminate\Contracts\Queue\ShouldQueue.
Something like this
<?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;
class ActionJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
public $data; // Make sure this public so you can access it in your test
public function __construct($data)
{
$this->data = $data;
}
public function handle()
{
if ($this->data['action'] == "deleteAllFiles") {
$this->deleteAllFiles();
}
}
protected function deleteAllFiles()
{
// do stuff
}
}
Then in your test:
// ActionJobTest.php
Queue::fake();
// Do some things to set up date, call an endpoint, etc.
Queue::assertPushed(ActionJob::class, function ($job) {
return $job->data['action'] === 'deleteAllFiles';
});
If you want to assert on $data within the job, then you can make some other state change and assert on that in the Closure.
Side note: If the Job is Disptachable you can also assert like this:
// ActionJobTest.php
Bus::fake();
// Do some things to set up date, call an endpoint, etc.
Bus::assertDispatched(ActionJob::class, function ($job) {
return $job->data['action'] === 'deleteAllFiles';
});

Laravel 5.3 Queue Job is not working

I am trying to dispatch my send email action using Laravel database queue
however this process still continues in my browser instead of working behind.
this is my controller
protected function importUserExcel(UploadedFile $file, Request $request){
$user_role = Role::where('name','=','user')->first();
\Excel::load($file, function($reader) use ($user_role) {
$excel = $reader->select()->get();
foreach($excel[0] as $line){
$user = User::firstOrnew([
'email' => $line['email']]);
$user->email = $line['email'];
$user->name = $line['name'];
$user->password= bcrypt(srand(15));
$user->town = $line['town'];
$user->dealer_code = $line['dealer_code'];
$user->type = $line['type'];
// $user->save();
$user->sendUserEmail();
//$user->attachRole($user_role);
}
});
}
this is my model function
public function sendUserEmail()
{
$delay = Carbon::now()->addMinutes(15);
\Log::info("Request Begins");
$user = new SendEmails($this);
$user->delay($delay);
dispatch($user);
\Log::info("Request Ends");
}
and this is my job
class SendEmails implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(User $user)
{
$this->handle($user);
}
/**
* Execute the job.
*
* #return void
*/
public function handle(User $user)
{
$broker = $user->broker;
$brokerInstance = \Password::broker($broker);
view()->share('locale', app()->getLocale());
$response = $brokerInstance->sendResetLink([ 'email' => $user->email ], function (Message $message) {
$message->subject(trans('emails.welcome_subject'));
});
}
}
however result seems coming eventually not delaying or queueing anything.
Meanwhile my browser also process instead of putting process to behind.
Your job's constructor should not call the handle() method; it should just set properties needed for the handle method. It's up to your queue worker to call the handle method.
Your call to app()->getLocale() may be incorrect if you're setting the locale per-request; a job is executed from another process and without middlewares or an associated http request.
class SendEmails implements ShouldQueue { use InteractsWithQueue, Queueable, SerializesModels;
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function handle() {
$user = $this->user;
$broker = $user->broker;
$brokerInstance = \Password::broker($broker);
view()->share('locale', app()->getLocale());
$response = $brokerInstance->sendResetLink([ 'email' => $user->email ], function (Message $message) {
$message->subject(trans('emails.welcome_subject'));
});
}
}
You can try again in the following way (I assume that you did instructions in Laravel docs but someday it's not working):
drop table jobs in your database.
run command php artisan migrate in console
run command php artisan queue:work in console
retry your app

Categories