Logging all Events in Laravel 5.1 without hitting "nesting" exception - laravel

How can I log all Events in Laravel 5.1? I tried but get this error:
Maximum function nesting level of '100' reached
I have tried setting the xdebug nesting level to a higher value (900) with no luck.

Place the following in your EventServiceProvider's boot function. The key is to filter out "illuminate.log" events. If you don't filter those out, your logging statement will result in recursive evaluation of your Event handler.
Note that the Laravel framework seems to make extensive use of events for it's own purposes, so you will see lots of "internal" framework events listed in your log as well.
public function boot(DispatcherContract $events)
{
parent::boot($events);
Event::listen('*', function()
{
$event = Event::firing();
if(!starts_with($event, "illuminate.log")) {
Log::info("Event fired: " . $event . "\n");
}
});
}

Related

Laravel testing fake event

I am trying to fake event
public function should_assign_order()
{
Event::fake([OrderWasAssigned::class]);
.
.
.
}
and I got this error
Call to undefined method Illuminate\Support\Testing\Fakes\EventFake::getListeners()
Edit:
I tried to disable telescope as this issue
by putenv('TELESCOPE_ENABLED=false');
and same issue
I need fake only this event
I am on laravel 6
PHPUnit 9.5.4.
If I understand correctly, then you are looking for scoped events as you want to test a specific event
/**
* Test order process.
*/
public function test_orders_can_be_processed()
{
$order = Event::fakeFor(function () {
$order = Order::factory()->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run ...
$order->update([...]);
}
}
You can also read about it in the laravels documentation about scoped events

Code after event fire is not executing | Laravel 6

I'm firing a status update event to broadcast on pusher. That's all working fine, however the code after the event fire doesnt work. At all.
Even if i put dd("hello"); nothing comes up. If i remove the event fire, things work as expected. I need this to work as i have a different redirect based on object parameter as shown below.
I can't figure out why this is happening, thanks for the help!
{
$room->start($connector);
event(new RoomStatusUpdated($room)); //problem here
if($room->autoLogin==1)
{
return redirect(route("room.join", $room->id));
}
else
{
notify()->info("","Meeting has started","bottomRight");
return back();
}
}
event(new RoomStatusUpdated($room)); //problem here
if your QUEUE_DRIVE=sync, Better you need to double check your event call as the event needs to be successful. Best practice is to include a temporary Logger by putting try-catch to your event
try{
//your event call
}catch(\Exception e){
Log::info("Code got excited: Line: {$e->getLine()} with error message: {$e->getMessage()}");
}
if queue driver is using a external like sqs or azure put back to sync first to trace back your event.

Laravel 5.2 Event Testing: expectsEvent not seeing the event fired although it is being fired

I have been trying to test events and I had it working yesterday. That was before I started to refactor the test code to keep it from being too repetitious. I added the setUp method call to generate the fake data using ModelFactories. This was done in each test case yesterday and as stated it was working.
I am thinking it has something to do with using the setUp method but I have no idea why that would be the case. First I attempted to use setUpBeforeClass() static method as that is only run once on unit test runs. However the laravel application is not actually setup until the first call to setUp()... A possible bug maybe? It is documented in this SO post Setting up PHPUnit tests in Laravel.
Therefore I opted to use the setUp method and just check to see if the static property is null or not, if it is null then it generates the data, if not it just goes on it's way.
Here is the output from running phpunit on the project
➜ project git:(laravel-5.2-testing) ✗ phpunit
PHPUnit 5.2.10 by Sebastian Bergmann and contributors.
E 1 / 1 (100%)
Time: 8.94 seconds, Memory: 33.50Mb
There was 1 error:
1) UserEmailNotificationsTest::testNotificationSentOnGroupMediaSaving
Exception: These expected events were not fired: [\App\Events\GroupMediaSaving]
/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php:44
/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:127
FAILURES!
Tests: 1, Assertions: 0, Errors: 1, Skipped: 8.
Here is my code for the unit test file that I have created.
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserEmailNotificationsTest extends \TestCase
{
use DatabaseTransactions;
const COMMUNITIES_TO_CREATE = 3;
const USERS_TO_CREATE = 10;
const ADMINS_TO_CREATE = 5;
protected static $communities = null;
protected static $users = null;
protected static $admins = null;
public function setUp()
{
parent::setUp(); // TODO: Change the autogenerated stub
if(is_null(self::$communities)) {
self::$communities = factory(\Community::class, self::COMMUNITIES_TO_CREATE)->create()->each(function ($community) {
self::$users[$community->id] = factory(User::class, self::USERS_TO_CREATE)->create()->each(function (\User $user) use ($community) {
$user->community()->associate($community);
$user->save();
});
self::$admins[$community->id] = factory(User::class, self::ADMINS_TO_CREATE, 'superadmin')->create()->each(function (\User $admin) use ($community) {
$admin->community()->associate($community);
$admin->save();
});
$community->save();
});
}
}
public static function getRandomCommunityWithAssociatedData()
{
$community = self::$communities[mt_rand(0, count(self::$communities)-1)];
return ['community' => $community, 'users' => self::$users[$community->id], 'admins' => self::$admins[$community->id]];
}
/**
* Test that the notification event is fired when a group media
* item is saved.
*/
public function testNotificationSentOnGroupMediaSaving()
{
$data = self::getRandomCommunityWithAssociatedData();
// FOR SOME REASON THIS SAYS THE EVENT IS NEVER FIRED WHEN IT ACTUALLY IS FIRED.
$this->expectsEvents(['\App\Events\GroupMediaSaving']);
$community = $data['community'];
$admin = $data['admins'][0];
$user = $data['users'][0];
$asset = factory(Asset\Video::class)->make();
$asset->community()->associate($community);
$asset->user()->associate($admin);
$asset->save();
$group = factory(Group::class)->make();
$group->community()->associate($community);
$group->created_by = $admin->id;
$group->save();
$groupMedia = factory(GroupMedia::class)->make();
$groupMedia->asset()->associate($asset);
$groupMedia->user()->associate($user);
$groupMedia->group()->associate($group);
$groupMedia->published_date = date('Y-m-d H:i:s', strtotime('-1 day'));
$groupMedia->save();
// I can print_r($groupMedia) here and it does have an ID attribute so it was saved, I also put some debugging in the event object and it is actually fired.....
}
}
Any thoughts as to why it doesn't see the events being fired? I find it odd that they are fired if I create the models inside the test case but seem to be failing when done inside of setUp(). The worst part is I am not creating the GroupMedia model in the setUp method rather that is done in the test case.
I have also dumped the data that is returned from the getRandomCommunityWithAssociatedData method and it is returning proper model objects all with id attributes which tells me they were all saved to the database during creation.
As requested here is the code that is actually firing the event, it is located in the GroupMedia model in the static boot method.
protected static function boot()
{
parent::boot();
static::saving(function($groupMedia) {
Event::fire(new \App\Events\GroupMediaSaving($groupMedia));
});
}
If you look at the source code for expectsEvents (inside the trait Illuminate/Foundation/Testing/Concerns/MocksApplicationServices), you will see that it calls the function withoutEvents, which mocks the application event dispatcher, suppressing and collecting all future events.
The problem for you is that the setUp function will have already been called at this point, so your events will not be caught and logged by the test, and will not show up when the assertion is evaluated.
In order to correctly see the events firing, you should make sure that you declare the assertion before the code which triggers the events.
Same thing happened to me, $this->expectsEvent() was not detecting that the event was being fired or prevent it from propagating to the event listeners..
Previously, I was firing my events using Event::fire(new Event()). I tried changing it to event(new Event()) and the test suddenly works correctly now, with it detecting that the event has been fired and silencing the event.

Is it possible to temporarily disable event in Laravel?

I have the following code in 'saved' model event:
Session::flash('info', 'Data has been saved.')`
so everytime the model is saved I can have a flash message to inform users. The problem is, sometimes I just need to update a field like 'status' or increment a 'counter' and I don't need a flash message for this. So, is it possible to temporarily disable triggering the model event? Or is there any Eloquent method like $model->save() that doesn't trigger 'saved' event?
Solution for Laravel 8.x and 9.x
With Laravel 8 it became even easier, just use saveQuietly method:
$user = User::find(1);
$user->name = 'John';
$user->saveQuietly();
Laravel 8.x docs
Laravel 9.x docs
Solution for Laravel 7.x, 8.x and 9.x
On Laravel 7 (or 8 or 9) wrap your code that throws events as per below:
$user = User::withoutEvents(function () use () {
$user = User::find(1);
$user->name = 'John';
$user->save();
return $user;
});
Laravel 7.x docs
Laravel 8.x docs
Laravel 9.x docs
Solution for Laravel versions from 5.7 to 6.x
The following solution works from the Laravel version 5.7 to 6.x, for older versions check the second part of the answer.
In your model add the following function:
public function saveWithoutEvents(array $options=[])
{
return static::withoutEvents(function() use ($options) {
return $this->save($options);
});
}
Then to save without events proceed as follow:
$user = User::find(1);
$user->name = 'John';
$user->saveWithoutEvents();
For more info check the Laravel 6.x documentation
Solution for Laravel 5.6 and older versions.
In Laravel 5.6 (and previous versions) you can disable and enable again the event observer:
// getting the dispatcher instance (needed to enable again the event observer later on)
$dispatcher = YourModel::getEventDispatcher();
// disabling the events
YourModel::unsetEventDispatcher();
// perform the operation you want
$yourInstance->save();
// enabling the event dispatcher
YourModel::setEventDispatcher($dispatcher);
For more info check the Laravel 5.5 documentation
There is a nice solution, from Taylor's Twitter page:
Add this method to your base model, or if you don't have one, create a trait, or add it to your current model
public function saveQuietly(array $options = [])
{
return static::withoutEvents(function () use ($options) {
return $this->save($options);
});
}
Then in you code, whenever you need to save your model without events get fired, just use this:
$model->foo = 'foo';
$model->bar = 'bar';
$model->saveQuietly();
Very elegant and simple :)
Call the model Object then call unsetEventDispatcher
after that you can do whatever you want without worrying about Event triggering
like this one:
$IncidentModel = new Incident;
$IncidentModel->unsetEventDispatcher();
$incident = $IncidentModel->create($data);
To answer the question for anyone who ends up here looking for the solution, you can disable model listeners on an instance with the unsetEventDispatcher() method:
$flight = App\Flight::create(['name' => 'Flight 10']);
$flight->unsetEventDispatcher();
$flight->save(); // Listeners won't be triggered
In laravel 8.x :
Saving A Single Model Without Events
Sometimes you may wish to "save" a given model without raising any events. You may accomplish this using the saveQuietly method:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();
See Laravel docs
In laravel 7.x you can do that as easy as
use App\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});
See more in Laravel 7.x Muting Events documentation
You shouldnt be mixing session flashing with model events - it is not the responsibility of the model to notify the session when something happens.
It would be better for your controller to call the session flash when it saves the model.
This way you have control over when to actually display the message - thus fixing your problem.
Although it's a long time since the question, I've found a way to turn off all events at once. In my case, I'm making a migration script, but I don't want any event to be fired (either from Eloquent or any other).
The thing is to get all the events from the Event class and remove them with the forget method.
Inside my command:
$events = Event::getRawListeners();
foreach ($events as $event_name => $closure) {
Event::forget($event_name);
}
The only thing that worked for me was using the trait WithoutEvents. This will be executed inside the setUp method and does prevent any dispatch you have added to the code.

Laravel 4.1 + Push Queues + Error Queues

My goal is to somehow notify me if a push message fails after X attempts.
Iron.io push queues docs describe: Error Queues
http://dev.iron.io/mq/reference/push_queues/#error_queues
Following the docs, I have to define an error_queue option in order to failed messages trigger a message in the specified error_queue option.
How can I define an option if push method in IronQueue.php doesn't support option argument. I see that pushRaw does support option argument.
How can I transform the following push example into a pushRaw
Route::get('someroute', function()
{
Queue::push('SomeClass', array('time' => time()));
});
class SomeClass{
public function fire($job, $data)
{
// do something
$job->delete();
}
}
Other ways of detecting push queues fails are also welcome.
As #cmancre said, you can use HUD to set the error queue or you could use the API to set it: http://dev.iron.io/mq/reference/api/#update_a_message_queue
Iron guys just rolled out an UI allowing us to set a error_error via iron admin panel.
In case your error_queue is already firing, to complete the cycle, you need to know which message failed.
To grab the error message information, in the error_queue route just do:
// First we fetch the Request instance
$request = Request::instance();
// Now we can get the content from it
$content = $request->getContent();
Reference: http://www.codingswag.com/2013/07/get-raw-post-data-in-laravel/

Resources