Put function in Queue but not loaded relation data | Laravel Queue - laravel

I need to generate a pdf of user with their related data. I have created a function in controller to generate pdf(using snappy-pdf package). Data is loaded properly, pdf generated and uploaded on S3 successfully. Now I am trying to put that function in queue with user data. But data is not loaded for generation pdf. Instead it only laod the user model data but not the related data (like property and contact).
$user = User::where("id", 83)->with(['property', 'contacts'])->first();
$contact_list = [];
if(count($user->contacts)){
foreach ($user->contacts as $contact) {
array_push($contact_list, $contact->contactDetails->name);
}
}
$user->contact_list = $contact_list;
return view('pdf_view', ["user" => $user]);
if($request->has('download')) {
// dispatch the event
/*$pdf = PDF::loadView('pdf_view', ["user" => $user]);
// upload pdf
Storage::disk('s3')->put("user-invoice/user.pdf", $pdf->output(), 'public');*/
dispatch(new GenerateSignedDocument($user));
return "done";
}
And here is my job file.
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;
use PDF;
use Storage;
class GenerateSignedDocument implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
private $user;
public function __construct($user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
// Log::info('Showing user: '.$this->user);
$pdf = PDF::loadView('pdf_view', ["user" => $this->user]);
// upload pdf
Storage::disk('s3')->put("user-invoice/user.pdf", $pdf->output(), 'public');
}
}

The SerializesModels trait only stores the model's key and reloads the model from the database when it is unserialized, so this is likely not going to load your relationships.
This is usually a big benefit as you'll receive fresh data when your queued job actually runs, not the data from when the job was scheduled.
In any case, it's probably best to either:
Load the relationships the job requires in the job itself and not rely on them being preloaded.
or
Pass the property and contacts as separate dependencies to your job class.

I don't know this is the perfect solution or not but I do this and work fine.
I called the required function(uploadUserDocument) for performing pdf generation in the handler function.
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
app('App\Http\Controllers\UserController')->uploadUserDocument($this->user_id);
}
and dispatch the event as it is in the controller
dispatch(new GenerateSignedDocument(83));
and put the required code for generating pdf in the uploadUserDocument function like this.
/**
* Function is used to Save document to s3 cloud
* #param $user_id
* #return Response
*/
public function uploadUserDocument($user_id){
$pdf = PDF::loadView('pdf_view', ["user" => $user]);
// upload pdf
Storage::disk('s3')->put("user-invoice/user.pdf", $pdf->output(), 'public');
}

Related

How to add a mailing feature on laravel 8

I am taking over a project and a client wants me to add a mailing feature like a gmail with inbox, sent messages and the user can also reply to emails from outside the system and the people receiving it can also reply. I have basic knowledge on laravel but this is my first time creating a feature like this can anyone give a stepping stone. This project has a VUE.js as a front end and a laravel as an API
Step -1: Create a Mail using,
php artisan make:mail MailName
Check the file MailName.php inside App/Mail/ directory
Step - 2: Create a function to send the email with the requested data.
public function sendMail(Request $request){
$data = [
'value1'=> $request->value1,
'value2'=> $request->value2,
'value3'=> $request->value3
];
//You can add any function like storing the values into db.
if(someconditions){
\Mail::to('emailid#domainname.com')->send(new MailName($data));
return back()->with('success', 'Email has been sent successfully!');
}
else
{
return back()->with('error', 'Something went wrong!');
}
}
Step - 3: Open the App/Mail/MailName.php
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailables\Address;
//You can include your model here.
class DeleteResponseMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* #return void
*/
public $data;
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*
* #return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
return new Envelope(
from: new Address('fromemail#somedomain.com', 'From Email Name'),
replyTo: [
new Address('admin#yourdomain.com', 'Your email name'),
],
subject: 'Email Subject',
);
}
/**
* Get the message content definition.
*
* #return \Illuminate\Mail\Mailables\Content
*/
public function content()
{
return new Content(
view: 'emails.mail',
);
}
/**
* Get the attachments for the message.
*
* #return array
*/
public function attachments()
{
return [];
}
}
Step - 4: Create mail.blade.php under resources/views/emails
Hi $data['value1'],
We have received your application. Thanks!
Step - 5: Open your .env file and add the email configurations.
MAIL_MAILER=enteryourmailerhere (ex.smtp)
MAIL_HOST=enteryourmailhosthere (ex.smtp.gmail.com)
MAIL_PORT=entertheport (ex. 25)
MAIL_USERNAME=emailid#yourdomain.com
MAIL_PASSWORD=emailpassword
MAIL_ENCRYPTION=""
MAIL_FROM_ADDRESS="fromemail#yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"
That's all! It should work.

Laravel 9 - How to get resolved instance of task in Queue::before event?

I have a multi-tenant project with multiple databases and a single queue. I need to switch between databases before running the job.
Here's the code I have:
Queue::before(function (JobProcessing $event) {
$costumer = DB::table('costumers')
->select('db_password', 'id')
->where('id', 11)
->first();
DB::disconnect('mysql');
config(
[
'database.connections.mysql.database' => 'costumer_'.$costumer->id.'_db',
'database.connections.mysql.username' => 'costumer_'.$costumer->id,
'database.connections.mysql.password' => Crypt::decryptString($costumer->db_password),
'costumer.code' => $costumer->id,
]
);
DB::reconnect('mysql');
});
It's working, but in the where clause, the id must be dynamically set.
So I pass the id in the Job::dispatch() method, but here's the problem: how do I get the job instance to return the data inside it?
I saw in another question the $event->job->instance and $event->job->getResolvedJob().
The first option is a protected property, so it doesn't work (it worked in Laravel 5). The second returns null.
You can set public property or getter in your job, so you can retrieve your id from the job instance, like here in getPodcastId:
<?php
namespace App\Jobs;
use App\Models\Podcast;
use App\Services\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The podcast instance.
*
* #var \App\Models\Podcast
*/
protected $podcast;
/**
* Create a new job instance.
*
* #param App\Models\Podcast $podcast
* #return void
*/
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
public function getPodcastId()
{
return $this->podcast?->id;
}
/**
* Execute the job.
*
* #param App\Services\AudioProcessor $processor
* #return void
*/
public function handle(AudioProcessor $processor)
{
// Process uploaded podcast...
}
}
But, to be honest, I think this is not really safe to change config on the go. The better solution would be to initialize another database connection inside your job and use it in your job dirrectly:
use Illuminate\Database\Connectors\ConnectionFactory;
// ...
public function __construct()
{
$factory = app(ConnectionFactory::class);
return $this->db = $factory->make(/* Config */);
}

Laravel 8 - Insert in related table in model

Whenever I create a "user", I have to create a line in different tables (like account).
I know that in the controller I can create the user and account like this:
$user = User::create($user_inputs);
$account = $user->account()->create($account_inputs);
$OtherTables...
Is there a way to do this in the model? Always when someone creates a user from another controller, will the lines be automatically inserted in the other tables. Or is it always necessary to indicate it in the controller every time?
You can use Laravel observer
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the user "created" event.
*
* #param \App\User $user
* #return void
*/
public function creating(User $user)
{
$user->account()->create([
// your data
]);
}
}
You can use model events for this. https://laravel.com/docs/9.x/eloquent#events-using-closures
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
// This code will be called every time a new user is inserted into the system
static::created(function ($user) {
$user->account()->create([ 'name' => $user->name ])
});
}
}
There are few more events you can use within booted method, the name tells clearly what they do.
creating
created
updating
updated
saving
saved
deleting
deleted

Eloquent how to do something when delete or update operate on a special model

Most of my db table contain create_user_id and update_user_id
How can l update this two field automatic when l use save(), update(), insert(), createOrUpdate() and etc method.
For example, l execute this script:
$model = Model::find(1);
$model->model_f = 'update';
$model->save();
then this record's model_f updated, and update_user_id updated, too.
l know eloquent can manage update_time automatic and l have use it already. But l want to do something else when update or insert or delete
PS: l have a constant named USERID to remember current user's id
You could make use of Observers.
You can hook to the following events on your Model:
retrieved
creating
created
updating
updated
saving
saved
deleting
deleted
restoring
restored
Let me give you an example where we are trying to hook into the events emitted by the App/User model. You can change this to match your particular Model later on.
To create an observer, run the following command:
php artisan make:observer UserObserver --model=User
Then you can hook to specific events in your observer.
<?php
namespace App\Observers;
use App\User;
class UserObserver
{
/**
* Handle the User "saved" event.
*
* #param \App\User $user
* #return void
*/
public function saved(User $user)
{
//
}
/**
* Handle the User "created" event.
*
* #param \App\User $user
* #return void
*/
public function created(User $user)
{
//
}
/**
* Handle the User "updated" event.
*
* #param \App\User $user
* #return void
*/
public function updated(User $user)
{
//
}
}
Since, in your particular case, you want to hook into these 3 events, you can define the events above and perform additional operations to your model when those events are called.
Don't forget to register this observer in your AppServiceProvider.
<?php
namespace App\Providers;
use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}
There is pretty simple way to automatically update the create_user_id and update_user_id
Step1:
Open you app folder and create the new file named as UserStampsTrait.php
Step:2
and paste the following code
<?php
namespace App;
use Illuminate\Support\Facades\Auth;
trait UserStampsTrait
{
public static function boot()
{
parent::boot();
// first we tell the model what to do on a creating event
static::creating(function($modelName='')
{
$createdByColumnName = 'create_user_id ';
$modelName->$createdByColumnName = Auth::id();
});
// // then we tell the model what to do on an updating event
static::updating(function($modelName='')
{
$updatedByColumnName = 'update_user_id';
$modelName->$updatedByColumnName = Auth::id();
});
}
}
Thats it
Step:3
Open you model which needs to updated the corresponding models automatically
for Example it may be Post
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\UserStampsTrait;
class Post extends Model
{
use UserStampsTrait;
}
Thats it

Keep PHPMailler connection alive over Laravel's queue

I want to send many emails.
Currently I write basic code use PHPMailler to send mail using queue. It works, but everytime new queue is run, it have to connect to SMTP again, so i get bad perfomance.
I find SMTPKeepAlive property on PHPMailler documentation:
$phpMailer = New PHPMailer();
$phpMailer->SMTPKeepAlive = true;
Is it imposible and how to keep $phpMailler object for next queue? So PHPMailler have not to connect again by using previous connection.
If you are using Laravel then you have to use Laravel's feature inbuilt.
Please find below documents:
https://laravel.com/docs/5.6/mail
Please find a piece of code for send mail and adding in a queue:
use App\Mail\EmailVerifyMail;
\Mail::queue(new EmailVerifyMail($users));
EmailVerifyMail.php
<?php
namespace App\Mail;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class EmailVerifyMail extends Mailable
{
use Queueable, SerializesModels;
public $user;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$this->to($this->user)->subject(__('mail.subjects.verification_link', ['USERNAME' => $this->user->name]));
return $this->view('mails/emailVerify', ['user' => $this->user]);
}
}

Resources