I have a command in my code that is run daily by cron that sends emails to all new users. It used to work ok, but after I have swithched the queue driver to SQS and upgraded Laravel 5.2 to 5.3 it started throwing an error.
InvalidArgumentExceptionvendor/laravel/framework/src/Illuminate/Mail/Mailer.php:379
Invalid view.
I don't know what might cause the error, because I have not deleted the view. Also when I run the command manually it does not throw any errors.
Here is the command code:
public function handle()
{
$subscriptions = Purchase::where('created_at', '>=', Carbon::now()->subDay())
->groupBy('user_id')
->get();
$bar = $this->output->createProgressBar(count($subscriptions));
foreach ($subscriptions as $subscription) {
$user = $subscription->user;
// if ($user->is_active_customer) {
Mail::to($user)->bcc(env('BCC_RECEIPTS_EMAIL'))->send(new NeedHelp());
// }
$bar->advance();
}
$bar->finish();
$this->info("\nSuccess! " . number_format(count($subscriptions)) . ' emails were sent.');
}
Here is the NeedHelp class code (I have changed the email and sender name for this thread):
<?php
namespace App\Mail;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class NeedHelp extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
*/
public function __construct(){
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->subject('Need help?')
->from('default#mail.com', 'Sender')
->view('emails.need-help');
}
}
I have found the error. The reason was that I have accidentally connected two applications to the same queue, which caused them to process jobs and emails of each other which resulted in this error.
Related
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
I have a job that looks like this:
<?php
namespace App\Jobs;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Redis;
class FakeJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** #var User */
private $user;
public $tries = 30;
/**
* Create a new job instance.
*
* #param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
try {
Redis::throttle('key')->allow(1)->every(60)->then(function () {
// Job logic...
throw new \Exception('fake exception');
$this->logInfo($this->user->name);
});
} catch(\Illuminate\Contracts\Redis\LimiterTimeoutException $e) {
$this->logInfo('inside LimiterTimeout catch, ' . $e->getMessage());
$this->release(1);
}
}
public function failed(\Exception $exception) {
// Send user notification of failure, etc...
$this->logInfo('inside failure, ' . $exception->getMessage());
}
public function logInfo($message) {
$path = storage_path('logs/FakeJob.log');
$logText = time() . ' ' . $message;
file_put_contents($path, $logText.PHP_EOL , FILE_APPEND | LOCK_EX);
}
}
Now you'll notice that I have a Redis throttler in the handle function, and that I immediately throw an error inside the callback. This is what I'm testing, and that error is what's going wrong.
You see, I'm trying to distinguish between LimiterTimeoutExceptions and exceptions thrown inside the Throttler callback. When I run the job through the sync connection, everything works as expected: FakeJob::dispatch($user)->onConnection('sync'); Errors that happen because I made a request within 60 seconds of a previous request go into the LimiterTimeoutException catch block, and errors that happen inside the Throttle callback go to the failed function.
But when I schedule the job through my default scheduler, which is Database, it seems as though EVERY error is going through the LimiterTimeoutException catch block, until I hit the retry limit - that error goes into the failed function, but not the 'fake exception' error.
I feel very confused about this.
Of note: even though my 'fake exception' ends up being caught by the LimiterTimeoutException catch block, the $e->getMessage() function does NOT return fake exception there for some reason. But I'm 100% certain that it is that error that is causing it to go there, because it's definitely not due to the throttler in those tests.
OK so actually what's happening is that it won't end up in the failed() function until it hits the retry limit, no matter what, regardless of any exception thrown. If I want to deal with LimiterTimeoutException errors in a different way from every other error, I'll have to wrap my Redis::throttle call in a try-catch and just check the type of error in the catch.
I would like to send a telegram message to a specific user at 17:00 using laravel's Telegram notification channel, I however can't seem to get it going. I currently use a cmd command for testing, but keep getting errors and don't know what to do.
Here are my files for the command and notification:
SendNotification.php
<?php
namespace Rogier\Lab\Console;
use Illuminate\Console\Command;
use Rogier\Lab\Notifications\DailyTelegram;
class SendNotifications extends Command
{
protected $name = 'lab:notifications:send';
protected $description = 'Send notifications';
protected $userid = 919871501;
/**
* Execute the console command.
* #return void
*/
public function handle()
{
$this->output->writeln('Sending notifications');
$notification = new DailyTelegram($this->userid);
$notification->via()->toTelegram();
$this->output->writeln('Done');
}
}
and DailyTelegram.php
<?php
namespace Rogier\Lab\Notifications;
use NotificationChannels\Telegram\TelegramChannel;
use NotificationChannels\Telegram\TelegramMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Notifiable;
class DailyTelegram extends Notification
{
protected $userid = 919871501;
public function via()
{
return [TelegramChannel::class];
}
public function toTelegram()
{
return TelegramMessage::create()
// Optional recipient user id.
->to($this->userid)
// Markdown supported.
->content("Hello there!\nYour invoice has been *PAID*");
}
}
I currently get the error "Call to a member function toTelegram() on array", but I feel like I tried everything, maybe I'm doing it completely wrong. Does anyone know how I should do it?
thanks in advance
Yes, you are doing it wrong. Notifiables have notify() method, you should use it:
$user->notify(new DailyTelegram);
In this example $user is App\User instance (which is notifiable out of the box).
You should check out both Laravel's sending notifications and laravel-notification-channels/telegram docs.
At the moment I currently use laravel-snappy to generate my PDFs from pages, I do this primarily because it plays really nice with some JavaScript scripts I have on a few of my generated pages.
I know it's possible to attach an attachment of sorts to a mailable, which is what I have been using at the suggestions of others until the notifiables get a few more things worked on with them.
But I am stuck as to whether or not it's possible to attach a generated PDF to a mailable.
At the moment, I send the PDFs to generate through a route:
Route::get('/shipments/download/{shipment}','ShipmentController#downloadPDF');
Which is then sent to here in the controller:
public function downloadPDF(Shipment $shipment){
$shipment_details = $shipment->shipment_details;
$pdf = PDF::loadView('shipments.pdf', compact('shipment','shipment_details'))
->setOption('images', true)
->setOption('enable-javascript', true)
->setOption('javascript-delay', 100);
return $pdf->download('shipment'.$shipment->url_string.'.pdf');
}
My mailable is defined as such:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Shipment;
class newFreightBill extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $shipment;
public function __construct(Shipment $shipment)
{
$this->shipment = $shipment;
$this->attachmentURL = $shipment->url_string;
$this->proNumber = $shipment->pro_number;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->from('billing#cmxtrucking.com')
->subject('New Freight Bill Created - '. $this->proNumber)
->view('emails.shipments.created');
}
}
So, is it possible to attach a generated PDF?
You can use attach to send files via mail. For example:-
public function build()
{
return $this->from('billing#cmxtrucking.com')
->subject('New Freight Bill Created - '. $this->proNumber)
->view('emails.shipments.created')
->attach($your_pdf_file_path_goes_here);
}
This worked for me while i was trying. Hope it works for you too.
Here i am using laravel package "vsmoraes/laravel-pdf" for pdf generation
steps :
1) render view with dynamic data
2) load this view to pdf
3) attach pdf data to the mail
$html = view('pdf',compact('result','score'))->render();
$pdf = PDF::Load($html);
\Mail::send('mail.analysis',['user'=>$data],function($message) use($pdf,$user){
$message->to($user->email)->subject("Page Speed Aanlysis Generated");
$message->attachData($pdf->output(),'pdf_name.pdf');
});
I'm using Laravel 5.0 and I've created a queued command with the Artisan CLI:
php artisan make:command SendEmail --queued
I need to use the DB::table() query builder method into this command, but I'm not able to make it work.
This is an excerpt of my code:
<?php namespace App\Commands;
use App\Commands\Command;
use DB;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldBeQueued;
class SendEmail extends Command implements SelfHandling, ShouldBeQueued {
use InteractsWithQueue, SerializesModels;
protected $message;
public function __construct($message)
{
$this->message = $message;
}
public function handle()
{
$data = DB::table('structures')->where('id', '=', '1')->first();
// $data is always empty even if database connection works outside the command!!! <-------------------
// no error exception is thrown
}
}
What am I doing wrong?
I found the error. I had to restart the supervisor queue in order the code to be correctly re-read:
supervisorctl
supervisor> restart myQueue