Hello Good Developers,
I've tried using solution from This Question but it didn't solve my problem hence I am asking it as a separate question.
TL;DR - I want my subscriber Listeners to get executed in a queue asynchronously. My Subscriber calls method from different class on for each Event. I tried to add implements ShouldQueue in my SubscriberClass | My Listener Class | renamed method to handle in Each Class but nothing works. they are still executed synchronously without queue.
I've a Event Subscriber Class: TesterAPI. I added it in EventServiceProvider subscribe array.
I've an Event Class SourceAPIEvents
class SourceAPIEvents
{
const PROJECT_CREATED = 'project.created';
const PROJECT_PAUSED = 'project.paused';
const PROJECT_RESUME = 'project.resume';
const PROJECT_CLOSED = 'project.closed';
public $project;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Project $project)
{
$this->project = $project;
}
}
TesterAPI - Subscriber
class TesterAPI implements ShouldQueue
{
/**
* Register the listeners for the subscriber.
*
* #param \Illuminate\Events\Dispatcher $events
*/
public function subscribe($events)
{
$events->listen(
SourceAPIEvents::PROJECT_CREATED,
'App\Listeners\Internal\Project\SourceAPI\Tester\ProjectCreate#handle'
);
$events->listen(
SourceAPIEvents::PROJECT_RESUME,
'App\Listeners\Internal\Project\SourceAPI\Tester\ProjectResume#handle'
);
$events->listen(
SourceAPIEvents::PROJECT_PAUSED,
'App\Listeners\Internal\Project\SourceAPI\Tester\ProjectPause#handle'
);
$events->listen(
SourceAPIEvents::PROJECT_CLOSED,
'App\Listeners\Internal\Project\SourceAPI\Tester\ProjectClose#handle'
);
}
}
Here are my Listener Classes:
class ProjectCreate extends TesterBase implements ShouldQueue
{
public function handle(SourceAPIEvents $sourceAPIEvent)
{
//Do Something
}
}
class ProjectPause extends TesterBase implements ShouldQueue
{
public function handle(SourceAPIEvents $sourceAPIEvent)
{
//Do Something
}
}
class ProjectResume extends TesterBase implements ShouldQueue
{
public function handle(SourceAPIEvents $sourceAPIEvent)
{
//Do Something
}
}
I am calling these events like this
event(SourceAPIEvents::PROJECT_CREATED, new SourceAPIEvents($project));
event(SourceAPIEvents::PROJECT_RESUME, new SourceAPIEvents($project));
Help me understand what I am doing wrong.
Related
In Laravel documentation there is Queue::before and Queue::after events. What I'm looking for is "job pushed to queue" or "job created" event, something like Queue::pushed. Is that exists or how can I trigger that event?
In this answer I saw JobPushed event but it's from Laravel Horizon. Is there a way without Horizon?
Queue::before(function (JobProcessing $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
Queue::after(function (JobProcessed $event) {
// $event->connectionName
// $event->job
// $event->job->payload()
});
you can use your job class cunstructor function as a job created event. a job object constructor is executed when you create a new instance of it. but other functions (for example: handle() and ...) would execute when job is running in queue.
also for something near job pushed to queue you can override the dispatch function of you job class. job objects have an static function dispatch that pushes a new job object into queue.
so your edited job class would be like these:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SampleJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct()
{
$this->created();
// rest of code
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
}
public static function dispatch($job)
{
SampleJob::dispatch($job);
$job->pushedToQueue();
}
// events
public function created()
{
// event codes
}
public function pushedToQueue()
{
// event codes
}
}
I'm using a service with an autowired dispatcher to dispatch an event. In order to test events, I've added an event subscriber just before dispatching the event. However, the registered subscriber isn't logging the method I would expect it to do.
The event:
<?php
namespace App\Event;
use Symfony\Component\EventDispatcher\Event;
class GameStartEvent extends Event {
}
The subscriber:
<?php
namespace App\Event;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class GameStartSubscriber implements EventSubscriberInterface {
use LoggerAwareTrait;
public function __construct(LoggerInterface $logger) {
$this->setLogger($logger);
}
public static function getSubscribedEvents() {
return [
"game.started" => [
[
'logEvent',
],
]
];
}
public function logEvent(GameStartEvent $event): void {
$this->logger->info("the game has started");
}
}
And this is the used service that is supposed to dispatch events the event registration is supposed to happen somewhere else in the future. I just did it here for testing:
<?php
namespace App\Service;
use App\Event\GameStartEvent;
use App\Event\GameStartSubscriber;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class GameCacheService {
private $eventDispatcher;
private $logger;
public function __construct(EventDispatcherInterface $eventDispatcher, LoggerInterface $logger) {
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
}
// some other methods...
function startGame() {
// some code...
$gameStartSubscriber = new GameStartSubscriber($this->logger);
$this->eventDispatcher->addSubscriber($gameStartSubscriber);
$this->eventDispatcher->dispatch("game.started", new GameStartEvent());
}
}
After calling the service method, the specified logger message isn't written down.
Replace
`$this->eventDispatcher->dispatch("game.started", new GameStartEvent());`
with
`$this->eventDispatcher->dispatch("game.started", $event);`
You seem to have at least your Event in App/Entity which is not autowired by default and shouldn't be. Have a look at your services.yml, it should be excluded from Autowiring. Move your stuff to something like App/Event
I am in the process of making realtime notifications and stumbled in this weird error. I have in my model a boot method which triggers an event called SendNotificationData (no listener). It handles when there is a new notification made.
Trial Controller
<?php
namespace App\Http\Controllers\Notification;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Models\Notification;
class NotificationController extends Controller
{
/**
* Trigger event to display notifications. This displays 404 error page
*
* #return none
*/
public function displayNotification()
{
$notification = new Notification();
$notification->EmployeeID = "EMP-00001";
$notification->NotificationText = "There is a new notification";
$notification->NotificationStatus = "unread";
$notification->NotificationType = "trial";
$notification->save();
}
}
Notification model boot method:
/**
* Handle booting of model.
*
* #var string
*/
public static function boot()
{
static::created(function ($data) {
event(new SendNotificationData($data));
});
parent::boot();
}
This is my SendNotificationData event:
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class SendNotificationData extends Event implements ShouldBroadcast
{
use SerializesModels;
public $new_notification_data;
/**
* Create a new event instance.
*
* #param $notification_data
* #return void
*/
public function __construct($new_notification_data)
{
$this->new_notification_data = $new_notification_data;
}
/**
* Get the channels the event should be broadcast on.
*
* #return array
*/
public function broadcastOn()
{
return ['new-notification'];
}
/**
* Customize event name.
*
* #return array
*/
public function broadcastAs()
{
return 'private-send-new-notification';
}
}
On Javascript
var newNotificationChannel = pusher.subscribe('new-notification');
newNotificationChannel.bind("private-send-new-notification", function(data) {
addNotification(data);
}); //This gives me no error in the console and the 404 error still shows up even if i remove this..
function addNotification(data)
{
console.log(data);
$('.notification-link').closest('li').append('This is a sample notification!!!');
}
Now, If I try to test adding some random notification in my controller, the event fires. However, it shows me the 404 error page. When I removed the ShouldBroadcast interface or remove the contents of the constructor, the error no longer shows up. I am confused what would be causing such an error when my other events are working fine. I might have missed something so please guide me.
I can't believe it, it was caused by the $incrementing variable in the model being set to false instead of true. If only laravel would show me the proper error stack trace.
The Laravel docs say I should put the model events in the EventServiceProvider boot() method like this.
public function boot(DispatcherContract $events)
{
Raisefund::saved(function ($project) {
//do something
});
}
But I have many models that I want to listen to.
So I was wondering if it is the right way to put it all in the EventServiceProvider.
Yes that's correct, the EventServiceProvider is the best place for it.
However you can create Observers to keep it clean. I will give you a quick example.
EventServiceProvider
<?php
namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use App\Models\Users;
use App\Observers\UserObserver;
/**
* Event service provider class
*/
class EventServiceProvider extends ServiceProvider
{
/**
* Boot function
*
* #param DispatcherContract $events
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
Users::observe(new UserObserver());
}
}
UserObserver
<?php
namespace App\Observers;
/**
* Observes the Users model
*/
class UserObserver
{
/**
* Function will be triggerd when a user is updated
*
* #param Users $model
*/
public function updated($model)
{
}
}
The Observer will be the place where the saved, updated, created, etc.. functions will be executed.
More information about Observers: http://laravel.com/docs/5.0/eloquent#model-observers
You can register listener callbacks in your models boot method, e.g.:
class User extends Eloquent {
protected static function boot() {
parent::boot();
static::deleting(function ($user) {
// deleting listener logic
});
static::saving(function ($user) {
// saving listener logic
});
}
}
I have problem with attaching OnDispatch event in Apigility. I want to have value from custom header in constructor of my abstract Service Class. It works when I simply add it to on bootstrap in Module.php
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$eventManager->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
}
public function onDispatch(\Zend\Mvc\MvcEvent $e){
$e->getRequest()->getHeaders()->get('User-Token')->getFieldValue();
}
But I don't know how to pass this value to ServiceAbstract Constructor.
I also tried with implementing EventManagerAwareInterface and attaching events with attachDefaultListeners() method but that didn't get me any results - attached function was not called. What is the proper approach to attaching to events NOT in controllers? Thanks for any help.
It seems to me that you have two different goals here.
1. Store a header variable from a route event.
2. Get a variable into the constructor function of a class
To answer 1.
You can make a listener class and attach this class to your eventManager. This would look something like this:
<?php
namespace My\Listener;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
use Zend\Http\Headers;
use Zend\Http\Request as HttpRequest;
class MyCustomListener implements ListenerAggregateInterface
{
/**
* #var \Zend\Stdlib\CallbackHandler[]
*/
protected $listeners = array();
/**
* #param EventManagerInterface $eventManager
*/
public function attach(EventManagerInterface $eventManager)
{
// attach on route
$this->listeners[] = $eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
}
/**
* #param EventManagerInterface $eventManager
*/
public function detach(EventManagerInterface $eventManager)
{
foreach ($this->listeners as $index => $listener) {
if ($eventManager->detach($listener)) {
unset($this->listeners[$index]);
}
}
}
/**
* Do your thing on dispatch event with your headers
*
* #param MvcEvent $event
*/
public function onDispatch(MvcEvent $event)
{
$request = $event->getRequest();
if(!$request instanceof HttpRequest){
// Nothing to do
return;
}
$headers = $request->getHeaders();
// You could for example get a service here and store your value
}
}
You attach this listener in Module.php like this:
public function onBootstrap(MvcEvent $event)
{
$event->getApplication();
$application->getEventManager();
$eventManager->attach($serviceManager->get('My\Listener\MyCustomListener'));
}
You have to register your listener in your ServiceManager either under invokables or factories with the key My\Listener\MyCustomListener to be able to attach it here like this.
To answer 2:
To get a variable in your constructor you can make a factory for your class and get the variable from the service that holds the variable that you need (could be from the listener from 1 directly).
<?php
namespace My\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use My\Folder\MyCustomClass;
class MyClassFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return Logger
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$controllerPluginManager = $serviceLocator;
$service = $serviceManager->get(My\Service\MyStorageService);
$dependency = $service->getDependency();
return new MyCustomClass($dependency);
}
}