Laravel 5.6 Job Event Queue::after not firing after queue processed - php

In an application I am working, I've both Job and Event Listener implemented Should Queue. In the queue, I perform a database insert and I want after the queue complete, I want to remove the previous cache. So I use Queue Job Event like this example:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Queue::after(function (JobProcessed $event) {
Log::info('[QUEUE COMPLETE]', $event->job->getName());
});
}
public function register()
{
//
}
}
But the event is never fired and there is no log found in storage/log folder. I use daily logging channel.
Why is it not logging?

Answering my own question after solving this.
All the code is fine, I just needed to stop the queue:work and start it again (restart). After this, the Queue::after event started to fire and all worked perfectly.

Related

Laravel queue::after function is not working?

I want to add some additional functionality when the queue is finished. I am using the queue::after function in the AppServiceProvider but this function is not being triggered. I have tried many solutions which were provided in the same kind of questions on StackOverflow like restarting the queue worker, clearing the cache and composer dump-autoload but didn't get any help.
public function boot()
{
Queue::after(function (JobProcessed $event) {
\Log::debug("Queue after"); // it should be printed in the logger
});
}
Any idea where am I going wrong?

Laravel Listener not fired even though the event was triggered

I am developing a web application using Laravel framework. I am trying to trying to use event and listener in my application. But the event was trigged and the listener for the trigged event is not fired.
This is my controller action
public function store(Request $request)
{
//other code
$item = Item::create($request->all())
broadcast(new ItemCreated($item));
return "Item created successfully";
}
This is my Events\ItemCreated.php
class ItemCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $item;
public function __construct($item)
{
$this->item = $item;
}
}
Then I have a listener for that event.
Listeners/EmailSubscribedUsers.php
class EmailSubscribedUsers
{
public function __construct()
{
//this constructor is not triggered
}
public function handle(ItemCreated $event)
{
//This method is not fired
}
}
In the EventServiceProvider I registered the event and the listener like this
protected $listen = [
ItemCreated::class => [
EmailSubscribedUsers::class
]
];
The event is trigged. But the listener is not fired. Why? What is wrong?
I tried the following solutions.
php artisan optimize
composer dumpautoload
php artisan clear-compiled
Sorry everyone. The issue was I was unit testing. In the unit testing if I used Event::fake(), the event listeners are not triggered. I wanted to tested the logic in the event listeners. Therefore, I removed the Event::fake() and tested the logic in the listener instead.
First of all as pointed in comments use
event(new ItemCreated($item));
and not
broadcast(new ItemCreated($item));
In addition make sure you have set QUEUE_CONNECTION to sync in your .env file. If you used some other connection (for example database or Redis) make sure you run in console command:
php artisan queue:work
The last thing - verify your error log in storage/logs directory. You might have some other errors (for example missing import) and that's why your listener fails.
Also make sure in EventServiceProvider that you use valid classes and imported valid namespaces - otherwise listener won't be triggered.

Testing listeners with Queue::fake()

My Laravel 5.5 application has a Product model. The Product model has a dispatchesEvents property that looks like this:
/**
* The event map for the model.
*
* #var array
*/
protected $dispatchesEvents = [
'created' => ProductCreated::class,
'updated' => ProductUpdated::class,
'deleted' => ProductDeleted::class
];
I also have a listener that is called CreateProductInMagento which is mapped to the ProductCreated event in the EventServiceProvider. This listener implements the ShouldQueue interface.
When a product is created, the ProductCreated event is fired and the CreateProductInMagento listener is pushed to the queue and is run.
I am now trying to write a test for all of this. Here is what I have:
/** #test */
public function a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento()
{
Queue::fake();
factory(Product::class)->create();
Queue::assertPushed(CreateProductInMagento::class);
}
But I get a The expected [App\Listeners\Magento\Product\CreateProductInMagento] job was not pushed. error message.
How do I test queueable listeners using Laravel's Queue::fake() method?
The problem here is that the listener is not the job pushed to the queue. Instead, there's a Illuminate\Events\CallQueuedListener job that is queued and will in turn call the appropriate listener when resolved.
So you could do your assertion like this:
Queue::assertPushed(CallQueuedListener::class, function ($job) {
return $job->class == CreateProductInMagento::class;
});
Running artisan queue:work won't solve the issue because when testing, Laravel is configured to use the sync driver, which just runs jobs synchronously in your tests. I am not sure why the job is not being pushed, though I would guess it has to do with Laravel handling events differently in tests. Regardless, there is a better approach you can take to writing your tests that should both fix the issue and make your code more extendable.
In your ProductTest, rather than testing that a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento, you should simply test that the event is fired. Your ProductTest doesn't care what the ProductCreated event is; that is the job of a ProductCreatedTest. So, you can use Event Faking to change your test a bit:
/** #test */
public function product_created_event_is_fired_upon_creation()
{
Event::fake();
factory(Product::class)->create();
Event::assertDispatched(ProductCreated::class);
}
Then, create a new ProductCreatedTest to unit test your ProductCreated event. This is where you should place the assertion that a job is pushed to the queue:
/** #test */
public function create_product_in_magento_job_is_pushed()
{
Queue::fake();
// Code to create/handle event.
Queue::assertPushed(CreateProductInMagento::class);
}
This has the added benefit of making your code easier to change in the future, as your tests now more closely follow the practice of only testing the class they are responsible for. Additionally, it should solve the issue you're having where the events fired from a model aren't queuing up your jobs.

how to execute events asynchronously in laravel

Pls I'm still new to laravel and I have used events in laravel a couple of times but I'm curious and would like to know if it's possible to execute an event in laravel asynchronously. Like for instance in the code below:
<?php
namespace mazee\Http\Controllers;
class maincontroller extends Controller
{
public function index(){
Event::fire(new newaccountcreated($user)) ;
//do something
}
Is it possible for the block of code in the event listener of the "newaccountcreated" event to be executed asynchronously after the event is fired ?
Yes of course this is possible. You should read about Laravel Queues. Every driver (only not sync driver) are async. The easiest to configure is the database driver, but you can also want to try RabbitMQ server , here is Laravel bundle for it.
You can also add to your EventListener: newaccountcreated trait Illuminate\Queue\InteractsWithQueue (you can read about him here) which will helps you to connect it with Laravel Queue.
Filip's answer covers it all. I will add a bit more to it. If you push an event it will goto the default queue. You can specify a queue name as well. Have the listener class implements ShouldQueue and just include the queue method in the listener class like below.
/**
* Push a new job onto the queue.
**/
public function queue($queue, $job, $data)
{
return $queue->pushOn('queue-name', $job, $data);
}

How to notify the user that a job has completed in Laravel?

in Laravel, you can use jobs to execute tasks in a back-end queue while the rest of the application does other things. i have a job that is initiated by user input. immediately, through javascript, i give the user a notification that the job is being processed.
i would like to be able to give a similar notification after the job has successfully completed.
i am calling my job from within a model like this:
public function doSomething() {
$job = new \App\Jobs\MyJob();
app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
}
and this is how my job headers look like:
class MyJob extends Job implements SelfHandling, ShouldQueue
{
use InteractsWithQueue, SerializesModels, Queueable;
...
}
the model job call is actually triggered from a controller method:
public function getDoSomething($id) {
$item = Item::findOrFail($id);
$item->doSomething();
return response()->json(true);
}
which is handled by an AJAX call:
$.ajax({
url: $(this).attr('href'),
type: 'GET',
dataType: 'json',
success: $.proxy(function(result) {
this.application.notification.showMessage('Job is being processed.');
}, this),
error: $.proxy(function(result) {
console.error(result);
}, this)
});
You can user Queue::after function on your AppServiceProvider
Import this dependencies
use Illuminate\Support\Facades\Queue;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
And on boot method you would use it
public function boot()
{
Queue::before(function (JobProcessing $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
Queue::after(function (JobProcessed $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
}
Probably I'm late for the party guys, but there are several options i can think of. When user clicks button from the front-end you can give attribute disabled to it and some text like 'processing'. Then you can:
Just ping your endpoint to check if what job is performing is finished
Use websockets
I think Forge is doing websockets using pusher the endpoint to see if server is active when you are trying to deploy new code. I can clearly see the communication if you open Devtools->Resources->Sockets.
You can use queue events, Laravel document explains it: https://laravel.com/docs/5.6/queues#job-events
Job Completion Event
The Queue::after method allows you to register a callback to be executed when a queued job executes successfully. This callback is a great opportunity to perform additional logging, queue a subsequent job, or increment statistics for a dashboard.
This already in the page link you shared from Laravel.

Categories