Laravel Broadcasting and Queues - laravel

I am developing a app to process videos. I try to push 10 videos into a queue. After a video was processed I fired a event like this:
...
$production->render();
event(new VideoHasProcessed(basename($this->video->path)));
...
And I do following this video to push a notification to users (https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/12)
The problem is: I want to push a notification when each video was processed but in fact, after 10 videos was processed the notification was displayed.
This is my script:
<script>
(function () {
var pusher = new Pusher(PUSHER_ID, {
encrypted: true
});
var channel = pusher.subscribe('test');
channel.bind('App\\Events\\VideoHasProcessed', function(data) {
console.log(data);
});
})();
</script>
Update:
Dispatch a job
foreach ($request->input('files') as $file)
{
...
// Dispatch a job.
$this->dispatch(new ProduceVideo($video));
....
}
And my job class:
public function handle(ProductionRepository $production)
{
...
$production->render();
event(new VideoHasProcessed(basename($this->video->path)));
...
}

After digging into laravel 5.3 core I found out the solution. You event class needs to extend ShouldBrodcastNow interface:
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class MyEvent implements ShouldBrodcastNow
{
...
}
In that way if you need the event to be broadcasted right away, this should do the trick.
NOTE: there is not need for MyEvent to implement both interfaces (ShouldBroadcastNow, ShouldBroadcast) because the interface ShouldBroadcastNow already extends ShouldBroadcast

If each of your 10 videos are processed as separate queued jobs you'll just need to fire your event in each queued job. Here's an example handle function for a job class (see https://laravel.com/docs/5.2/queues#job-class-structure):
/**
* Execute the job.
*/
public function handle()
{
// Your video processing script
// Fire your event
event(new VideoHasProcessed(basename($this->video->path)));
}
If your videos are processed as 1 job you'll need to add your event inside any loop you may be using to process each video:
foreach($videos as $video):
// Your video processing script
// Fire your event
event(new VideoHasProcessed(basename($this->video->path)));
endforeach;

Related

Angular 9 and rxjs - wait for message event after postMessage

I am new to rxjs and not sure how to implement the follow logic. Any suggestion will be appreciated.
Background
I am going to implement the communication between host website and an iframe in it with postMessage. Since postMessage is one-way only, I would like to implement the logic to wait for 'response' by myself when a message is sent from host website to iframe.
I have a sync function called send(message) to invoke the postMessage to send message to iframe. Then I would like to have another function with the follow logic.
public async sendAndWait(message): Promise<responseObj> {
// 1. create an observable to wait to message event with timeout
// my first thought is as follow but I feel like it does not work
// fromEvent(window, 'message')
// .pipe(timeout(timeoutInMs))
// .subscribe(event => {
// console.info(event);
// });
// 2. run `send(message)` function
// 3. do not finish this function until timeout or receive event in the previous subscription.
}
When I use the function, I would like to have
let response = await sendAndWait(message);
Not sure if it is possible to implement? Thank you
You cannot stop code execution in JS (using Async-Await, a Promise object is returned behind the scenes. so that the code is never waiting)
Consider implementing it in the following way:
let response: responseObj;
function main(): void {
sendAndWait(MESSAGE_OBJECT).subscribe(x => response = x)
}
function sendAndWait(message): Observable<responseObj> {
send(message)
return fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first()
)
}
Or optionally returning Promise:
async function sendAndWait(message): Promise<void> {
send(message)
const response = await fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first(),
toPromise()
)
}

Laravel catch string output when CommandFinished event fires

command.php example, something like this:
public function handle()
{
$this->info('just an ordinary message');
}
How can i catch this message "just an ordinary message" outside command (for my example in service provider) where command finished event is fired.
my service provider file:
$this->events->listen(CommandFinished::class, function ($event) {
dump($event->command);
});

Laravel - Event not working - __construct() must be an instance

I want to send mail to admin, when users send message to him in website. I create my events and my mailable class..
At the first, I tried without using event listener and it worked but When I tried with event listener, it didn't work..
it gives this error;
Type error: Argument 1 passed to App\Events\SendMessage::__construct() must be an instance of App\Contact, instance of App\Models\Contact given, called in /var/www/parti/app/Http/Controllers/ContactController.php on line 32
Event
public $mesaj;
public function __construct(Contact $mesaj)
{
$this->mesaj = $mesaj;
}
Listener
public function handle(SendMessage $event)
{
Mail::to(User::first()->email)->send(new SendMessageToAdminAsMail($event->mesaj));
}
Mail Class
public $mesaj;
public function __construct(Contact $mesaj)
{
$this->mesaj = $mesaj;
}
public function build()
{
return $this
->from($this->mesaj->email)
->subject($this->mesaj->name." send a message")
->view('backend.mail.message');
}
What is my mistake ? Thank's for your help...

How to register events from within a module in Yii?

I'm trying to register events from within a submodule in Yii.
It just doesn't seem to work.
The init method is definitely called.
class TestModule extends CWebModule
{
public function init()
{
$this->setImport(array(
'test.models.*',
'test.components.*',
));
Yii::app()->onBeginRequest = array($this, 'onBeginRequest');
}
public function onBeginRequest($event) {
die('Request!');
}
public function beforeControllerAction($controller, $action)
{
if (parent::beforeControllerAction($controller, $action))
{
return true;
}
else
return false;
}
}
To register an event you can do:
$this->getEventHandlers($eventName)->add($eventHandler);
Where $eventHandler is the name of the callback you want to define for the $eventName event.
You can also do it with the following way:
$this->attachEventHandler($eventName, $eventHandler);
I solved the problem myself.
The problem was, that i was actually too late for onBeginRequest (Request was alrdy processed).
So what i did was writing a component with Event Handlers for onBeginRequest and onEndRequest, registering the event handlers in config/main.php and call my Module from this Component.
I basically had to proxy all these events.

Symfony2 Properly Hook in Events from Services

I have a class which is used to generate navigation from a variety of interconnected bundles. I have a Navigation service to accomplish this.
In order to connect this service with the other bits of Navigation, I want to allow the other bundles to define their own services which then listen to the event listener and add their navigation items at the proper time.
The problem is, I can't figure out how to have a service listen to an event without first calling that service manually in order to create it.
Any ideas?
To give a more concrete idea, I have something like this:
// Set up as a service in the bundle.
class Navigation {
// ...
protected $dispatcher; // event dispatcher passed in to service
// ...
public function generateNavigation() {
$items = array();
// add some items
$event = new NavigationEvent($items); // custom event
$this->eventDispatcher->dispatchEvent('navigation_event', $event);
}
}
// Set up as a service in some secondary bundle.
class NavigationWorker {
/**
* #param $dispatcher Same instance as Navigation
*/
public function __construct(EventDispatcher $dispatcher) {
$dispatcher->addListener('navigation_event', array($this, 'doSomething'));
}
}
With this set up, it should work if the NavigationWorker is called at some point and is constructed, but I can't always call them directly, so it is never constructed and the listener is never added.
The way I currently do it is to pass all of the NavigationWorkers to Navigation and have it add their listener, but this is very ugly.
See the Event Listener Documentation. Make NavigationWorker and event listener and it won't need to be explicitly constructed.
I'm changing the answer to this because while that set me on the right path, it wasn't the complete answer. That article really only allows you to hook in to pre-defined kernel events. I however needed my own, so I started working back from there.
In the end, I ended up creating my own tags, a compiler pass to process those tasks. I also added my own extension of EventDispatcher, though that wasn't super-necessary (you could just use the normal one).
Here is what the file solution looked like.
Configuration:
parameters:
my_bundle.navigation.event.class: My\Bundle\DependencyInjection\NavigationEvent
my_bundle.event_dispatcher.class: My\Bundle\DependencyInjection\EventDispatcher
my_bundle.navigation.class: My\Bundle\DependencyInjection\NavigationGenerator
my_bundle.navigation_listener1.class: My\Bundle\DependencyInjection\NavigationListener
my_bundle.navigation_listener2.class: My\Bundle\DependencyInjection\NavigationListener
services:
my_bundle.event_dispatcher:
class: %my_bundle.event_dispatcher.class%
my_bundle.navigation:
class: %my_bundle.navigation.class%
arguments:
- #my_bundle.event_dispatcher
my_bundle.navigation_listener1.class:
class: %my_bundle.navigation_listener1.class%
tags:
- { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation }
my_bundle.navigation_listener2.class:
class: %my_bundle.navigation_listener2.class%
tags:
- { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation }
CompilerPass:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class EventListenerCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('my_bundle.event_dispatcher')) {
return;
}
$definition = $container->getDefinition(
'my_bundle.event_dispatcher'
);
$taggedServices = $container->findTaggedServiceIds(
'my_bundle.event_listener'
);
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
$definition->addMethodCall(
'addListener',
array($this->getEventString($attributes['event'], $container), array(new Reference($id), $attributes['method']))
);
}
}
}
protected function getEventString($str, ContainerBuilder $container)
{
preg_match('/(.*)\.([^.]*)$/', $str, $matches);
$parameterName = $matches[1];
$constName = strtoupper($matches[2]);
$eventClass = $container->getParameter($parameterName . '.event.class');
if (!$eventClass) {
throw new Exception('Unable to find parameter: ' . $eventClass . '.event.class');
}
// Return the value of the constant.
return constant($eventClass . '::' . $constName);
}
Add a function like this to your compiler class (something like MyBundleBundle).
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new EventListenerCompilerPass());
}
Now the EventListener will have added listeners for each of those events. You than just implement everything else exactly as you would expect (Navigation dispatches events which it listens too). You can than hook in new event listeners from any bundle, and they don't even need to share a common class/interface.
This also works for any custom event, as long as the object which has the constant for the event is registered in the parameters with ".event.class" at the end (so my_bundle.navigation.generate looks for the parameter my_bundle.navigation.event.class, uses that class and the constant GENERATE).
Hopefully that'll help anyone else looking to do something similar.

Resources