I'm curious if you guys know any easy and reliable way to track the source of what dispatched a Laravel job.
My use case for this is that I often have to debug failing jobs and knowing why, where and how they were dispatched would be very useful, e.g. could add it as metadata to Sentry reports.
I mean adding a property to a job would probably do it, but I'm wondering if there's some way that would not involve code changes in individual jobs.
I think you're looking for Job Events (Laravel documentation)
You could add this to your app/Providers/AppServiceProvider.php - boot() method or you could create one QueueJobProvider for example and add your provider in config/app.php inside the 'providers' key.
With this events you can have the payload as you want and more proprities. You can learn more about this issue in Laravel docs here
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobExceptionOccurred;
Queue::before(function (JobProcessing $event) {
// \Log::info($event);
// \Log::info($event->job->payload());
});
Queue::after(function (JobProcessed $event) {
// \Log::info($event);
// \Log::info($event->job->payload());
});
Queue::failing(function (JobFailed $event) {
// \Log::info($event);
// \Log::info($event->job->payload());
});
Queue::exceptionOccurred(function (JobExceptionOccurred $event) {
// \Log::info($event);
// \Log::info($event->job->payload());
});
Related
let's me explain example
User click button
run function trigger in Controller
User wait 30sec because MyModel::doSomeThing take long time to process
MyModel::doSomeThing do many thing. I don't want user to wait for it.
Is it possible to run MyModel::doSomeThing by don't care about result and return to user immediately?
function trigger(Request $request){
$id= $request->get('id');
MyModel::doSomeThing($id); // this one take 30 sec.
return response()->json([], 200);
}
If the result of doSomeThing() method isn't necessary for your response & can be done in background, I suggest using Events and Listeners, which will use queues to run in the background, and the user won't need to wait for this procces to finish. The process is fairly simple. Create event and it's listened with these two commands:
php artisan make:event YourEvent
php artisan make:listener YourListener --event=YourEvent
After that, register your event and listener in the App\Providers\EventServiceProvider, under the $listen array:
protected $listen = [
YourEvent::class => [
YourListener::class,
],
];
Now, when you have that sorted out, you need to build your event instance. Inside your newly created method, in the construct method, add this:
public $yourModel;
public function __construct(YourModel $yourModel)
{
$this->yourModel = $yourModel;
}
After you created your model, time to edit your listener, which will hanlde all the logic ghat you need. Inside this handle method, you will have the access to $yourModel instance that we defined in our event:
public function handle(YourEvent $event)
{
// Access your model using $event->yourModel...
YourModel::doSomeThing($event->yourModel);
}
The only thing left do to is to make your listener queueable. You can do this by adding implements ShouldQueue your listened definition:
class YourListener implements ShouldQueue
{
//
}
Now when we have everything setup, you can change your controller code to call this newly created event, and let the queue handle all the logic:
function trigger(Request $request){
$id= $request->get('id');
YourEvent::dispatch($id); //Calling event which will handle all the logic
return response()->json([], 200);
}
And that should be it. I haven't tested this code, so if you encounter any problems, let me know.
I am developing a Laravel application and doing the unit test. Now, I am having a bit of an issue with faking and un-faking Laravel event in the unit test. What I am trying to do is something like this.
public function test_something()
{
Event::fake();
//Do somethng
//Then I want to stop faking event here something like this
Event::stopFaking(); //maybe
}
I think my code is self-explanatory. How can I achieve something like that in Laravel?
https://laravel.com/docs/5.7/mocking
If you only want to fake event listeners for a portion of your test, you may use the fakeFor method:
$order = Event::fakeFor(function () {
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run ...
$order->update([...]);
Everything inside the function() {} will have faked events. Everything outside will function normally.
The Event::fake function is defined in Illuminate\Support\Facades\Event.
We can see that there is a fakeFor method that only fakes it during the execution of a callback method, then restores the original behavior. You can use it like this:
public function test_something()
{
Event::fakeFor(function () {
//Do somethng
});
}
As a Laravel developer it is often useful to read the source code, there are lots of nice bits and pieces in this framework that are not documented!
I have a question about Laravel's Event Handlers and Listeners. I have no idea where to start.
I would like to know what exactly are Events and when to use them. Also I would like to know what the best way to organize events and listeners is, and where to place them (in which folder).
Any help would be appreciated ;)
I've recently implemented a feed for actions, e.g. when a post is created, a new user is registered, or whatever. Every action fires an event and for every event there's a listener, which saves something like "User XY just registered!" in the database.
Very basic version:
// app/controllers/RegistrationController.php
class RegistrationController {
public function register($name) {
User::create([
'name' => $name
});
Event::fire('user.registered', [$name]);
}
}
// app/events.php
Event::listen('user.registered', function($name) {
DB::table('feed')->insert(
[
'action' => 'User ' . $name . ' just registered!'
// ...
}
);
});
To use the events.php file, add the following line to your app/start/global.php
require app_path().'/events.php';
Now you can put all events in the events.php.
But if you're going to have a lot of events, you shouldn't put all of your events in a single file. See Event Subscribers.
I want to have an event listener binding with a model event updating.
For instance, after a post is updated, there's an alert notifying the updated post title, how to write an event listener to have the notifying (with the post title value passing to the listener?
This post:
http://driesvints.com/blog/using-laravel-4-model-events/
Shows you how to set up event listeners using the "boot()" static function inside the model:
class Post extends eloquent {
public static function boot()
{
parent::boot();
static::creating(function($post)
{
$post->created_by = Auth::user()->id;
$post->updated_by = Auth::user()->id;
});
static::updating(function($post)
{
$post->updated_by = Auth::user()->id;
});
}
}
The list of events that #phill-sparks shared in his answer can be applied to individual modules.
The documentation briefly mentions Model Events. They've all got a helper function on the model so you don't need to know how they're constructed.
Eloquent models fire several events, allowing you to hook into various points in the model's lifecycle using the following methods: creating, created, updating, updated, saving, saved, deleting, deleted. If false is returned from the creating, updating, saving or deleting events, the action will be cancelled.
Project::creating(function($project) { }); // *
Project::created(function($project) { });
Project::updating(function($project) { }); // *
Project::updated(function($project) { });
Project::saving(function($project) { }); // *
Project::saved(function($project) { });
Project::deleting(function($project) { }); // *
Project::deleted(function($project) { });
If you return false from the functions marked * then they will cancel the operation.
For more detail, you can look through Illuminate/Database/Eloquent/Model and you will find all the events in there, look for uses of static::registerModelEvent and $this->fireModelEvent.
Events on Eloquent models are structured as eloquent.{$event}: {$class} and pass the model instance as a parameter.
I got stuck on this because I assumed subscribing to default model events like Event:listen('user.created',function($user) would have worked (as I said in a comment). So far I've seen these options work in the example of the default model user created event:
//This will work in general, but not in the start.php file
User::created(function($user)....
//this will work in the start.php file
Event::listen('eloquent.created: User', function($user)....
Event::listen('eloquent.created: ModelName', function(ModelName $model) {
//...
})
At the moment I don't have any queuing functionality in my Cakephp aplication. I will need that in the near future. An upload will result in a batchjob that uses external API with usage limitations, so it would be best if it was handeled in a seperate threat with a queue.
I don't have any experience with this, so I'm going to try a different, but easier, example.
User actions result in e-mails being send. At the moment, the loading of the page is delayed by the (rather long) time it takes the server to send the e-mail. I'd like to use the Event system to fix this. (I am aware I can also do this using this the afterRender function, or dispatch it to a shellTask, but that way I don't learn anything)
From the example page:http://book.cakephp.org/2.0/en/core-libraries/events.html
I've found this example:
// Cart/Model/Order.php
App::uses('CakeEvent', 'Event');
class Order extends AppModel {
public function place($order) {
if ($this->save($order)) {
$this->Cart->remove($order);
$this->getEventManager()->dispatch(new CakeEvent('Model.Order.afterPlace', $this, array(
'order' => $order
)));
return true;
}
return false;
}
}
Let's say the function was called by a controller action:
public function place_order() {
$result = $this->Order->place($this->request->data);
$this->set('result', $result);
}
Now my question... Will the corresponding view be rendered after all the dispatched events completes? or will the Model function just trigger the event and then forget about it?
The last option seems more logical to me (which also resembles the mentioned jQuery functionality in the article)
The problem is that If this were true, I don't understand the later example:
In the example about using results:
// Using the event result
public function place($order) {
$event = new CakeEvent('Model.Order.beforePlace', $this, array('order' => $order));
$this->getEventManager()->dispatch($event);
if (!empty($event->result['order'])) {
$order = $event->result['order'];
}
if ($this->Order->save($order)) {
// ...
}
// ...
}
if the event was just triggered (and then forgot about) there is no way you can asume it has modified the passed event object on the next line of code!
I would like to use cake as much as possible, but I'm not sure if I can get my desired background behavior without shellTasks and external queue. Any tips about these Cake Events?
Cake Events are triggered synchronously. When an event is triggered, all available listeners are called, before proceeding with other instructions.
You can imagine it on your second example as:
public function place($order) {
$event = new CakeEvent('Model.Order.beforePlace', $this, array('order' => $order));
$this->getEventManager()->dispatch($event); // -> all listeners are called at this point
// ... here you can assume your $event was modified
if (!empty($event->result['order'])) {
$order = $event->result['order'];
}
if ($this->Order->save($order)) {
// ...
}
// ...
}