Calling of afterSave method in application.php file slowing down complete platform and showing memory_limit exceed error - events

I am calling afterSave method in application.php to perform action on all models saving event. Issue is whenever I using SAVE method inside afterSave method application showing fatal error:
SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Point is same method working fine in specific model, without any memory exhausted error, I think there is something that need to be fixed over database connection. Below is the code which one I am trying.
//Application.php file
namespace App;
...
...
\Cake\Event\EventManager::instance()->on(
'Model.afterSave',
function (
\Cake\Event\EventInterface $event,
\Cake\Datasource\EntityInterface $entity,
\ArrayObject $options
) {
$auth_data = isset($_SESSION['Auth'])?$_SESSION['Auth']:[];
$ActionLogs = TableRegistry::get('ActionLogs');
$ActionLogsEntity = $ActionLogs->newEmptyEntity();
$ActionLogsEntity->change_log = $change_log;
$ActionLogsEntity->action_taken_by = $auth_data->username;
$ActionLogs->save($ActionLogsEntity); //This statement working fine in specific modelTable
class Application extends BaseApplication
implements AuthenticationServiceProviderInterface
{
...
...
}

Aside from the fact that the code should go into the Application class' bootstrap() method as mentioned in the comments, when you save inside of an afterSave event that listens to all models, then you naturally create a recursion, as saving the log will trigger an afterSave event too.
You have to put a safeguard in place that prevents the logging logic from running when the afterSave event belongs to the logging model, for example:
if ($event->getSubject() instanceof \App\Model\Table\ActionLogsTable) {
return;
}
// ...

Related

How to resolve a contextually bound class from Laravel IOC container

I have a command class with a dependency type-hinted in the constructor:
class RunAnalyticsCommand extends Command
{
public function __construct(Analytics $analytics)
{
//
}
}
The constructor for the Analytics class looks like this:
class Analytics
{
public function __construct(Odoo $odoo, ChannelInterface $channel)
{
//
}
}
In a service provider, I've instructed the application what to instantiate for the Odoo class. If I create an instance of Analytics like this, it works fine. It gets the Odoo instance from the container and uses the channel that I pass in.
$analytics = app(Analytics::class, ['channel' => new ChannelA]);
Now, I'm trying to use contextual binding to accomplish all of this "behind the scenes". I write the following in my service provider's register method:
$this->app->when(RunAnalyticsCommand::class)
->needs(Analytics::class)
->give(function () {
return app(Analytics::class, ['channel' => new ChannelA]);
});
However, now when I run the RunAnalyticsCommand, I get an error that Maximum function nesting level of '256' reached, aborting!
I'm assuming this happens because the give callback is trying to resolve the same Analytics class, and the container treats that call as if it was also coming from the RunAnalyticsCommand class, so it just keeps trying to resolve the same thing over and over.
Is this the expected behavior or a bug with contextual binding? Shouldn't the call to resolve the class from within the give callback not behave as if it were originating from the RunAnalyticsCommand? Is there some other way to tell the container to resolve without using the contextual binding?

Saving an object into the session or cookie

I'm using Instagram API library to connect user to Instagram profile and then do smth with it. So, as Instagram API wiki says:
Once you have initialized the InstagramAPI class, you must login to an account.
$ig = new \InstagramAPI\Instagram();
$ig->login($username, $password); // Will resume if a previous session exists.
I have initialized InstagramAPI class and then I called $ig->login('username', 'password');. But I have to call it in every function where I need to work with Instagram.
So how could I save this object $ig for using it in the future in other controllers without calling login() any more? Can I save $ig object into the session or cookie file?
P.S. I think saving into the session is not safe way to solve the issue.
UPD: I was trying to save $ig object into the session, however the size is large and session become stop working as well.
Regarding the register method you asked in the comments section, all you need to create a new service provider class in your app\providers directory and declare the register method in there for example:
namespace App\Providers;
use InstagramAPI\Instagram;
use Illuminate\Support\ServiceProvider;
class InstagramServiceProvider extends ServiceProvider
{
public function register()
{
// Use singleton because, always you need the same instance
$this->app->singleton(Instagram::class, function ($app) {
return new Instagram();
});
}
}
Then, add your newly created InstagramServiceProvider class in providers array inside the config/app.php file for example:
'providers' => [
// Other ...
App\Providers\InstagramServiceProvider::class,
]
Now on, in any controller class, whenever you need the Instagram instance, all you need to call the App::make('InstagramAPI\Instagram') or simply call the global function app('InstagramAPI\Instagram') or even you can typehint the class in any method/constructor etc. Some examples:
$ig = App::make('InstagramAPI\Instagram');
$ig = App::make(Instagram::class); // if has use statement at the top fo the class
$ig = app('...');
In a class method as a dependency:
public function someMethod(Instagram $ig)
{
// You can use $ig here
}
Hope this helps but read the documentation properly, there will get everything documented.

Laravel queueable notification errors: Serialization of 'Closure' is not allowed

I've created a mail notification that works successfully, but when trying to queue it, I get the following error:
Uncaught Exception: Serialization of 'Closure' is not allowed in /vendor/laravel/framework/src/Illuminate/Queue/Queue.php:125
Below is my code that I believe is causing the error:
public function toMail($notifiable)
{
$view_file = 'emails.verifyEmail';
$view = View::make($view_file, ['invitationToken' => $this->invitationToken, 'team_name' => $this->team->name, 'team_domain' => $this->team->domain ]);
$view = new HtmlString(with(new CssToInlineStyles)->convert($view));
return (new MailMessage)
->subject('Email Verification')
->view('emails.htmlBlank', ['bodyContent' => $view]);
}
I am not exactly sure where the 'Closure' it's trying to serialize is coming from. I tried tacking on ->render() to the end of View::make but that did not seem to make a difference. I believe it may have something to do with the view function of the MailMessage but I'm not really sure.
Once again, this notification works perfectly when it is not being Queued.
Any help would be appreciated.
Even if the question is pretty old, i'm posting this for future reference.
The problem occurs when the Queue tries to serialize the notification instance. This is done by serializing every property of the notification object. I had the same problem because i was doing something like
public function __construct(\Exception $ex){
$this->exception = $exception;
}
in my notification class.
Once the notification is wrapped in SendQueuedNotification it will be serialized by the Queue handler. During this process every property of SendQueuedNotification will be serialized, including our custom notification instance and its properties. Everything will fail when the serializer will try to serialize $exception instance; for some reason the exception class is unserializable because it probably contains a closure within its properties. So what worked for me was changing the constructor as follows
public function __construct(\Exception $ex)
{
$this->exceptionClass = get_class($ex);
$this->exceptionMessage = $ex->getMessage();
$this->exceptionLine = $ex->getFile() . '#' . $ex->getLine();
$this->exceptionCode = $ex->getCode();
}
Now all of the notification properties are fully serializable and everything works as expected.
Another solution is to use __wakeup() and __sleep() methods to customize the serialization and deserialization of your notification instance.
Hope it helps to understand your issue.

Add Custom Variable to Laravel Error Log

I'd like to log the user's name along with the error that is outputted to the log. How do I add a variable to the beginning of an error log entry that outputs an exception?
I think I've got a fairly easy way to do this.
Solution 1
Create a new folder under app called handlers and create a new class called CustomStreamHandler.php which will hold the custom monolog handler.
namespace App\Handlers;
use Monolog\Handler\StreamHandler;
use Auth;
class CustomStreamHandler extends StreamHandler
{
protected function write(array $record)
{
$record['context']['user'] = Auth::check() ? Auth::user()->name : 'guest';
parent::write($record);
}
}
Make sure you set the namespace if you changed it from App and also modify the line where it's setting the user in the context so it works with your users table.
Now we need to drop the current StreamHandler from monolog. Laravel sets this up by default and as far as I can see, there isn't a good way to stop Laravel from doing this.
in app/Providers/AppServiceProvider, we should modify the boot() function to do remove the handler and insert the new one. Add the following...
// Get the underlying instance of monolog
$monolog = \Log::getMonolog();
// Instantiate a new handler.
$customStreamHandler = new \App\Handlers\CustomStreamHandler(storage_path('logs/laravel.log'));
// Set the handlers on monolog. Note this would remove all existing handlers.
$monolog->setHandlers([$customStreamHandler]);
Solution 2
This is a much easier solution but also not exactly what you are looking for (but it might still work for you).
Add the following to AppServiceProvider.php boot().
Log::listen(function()
{
Log::debug('Additional info', ['user' => Auth::check() ? Auth::user()->name : 'guest']);
});
This will simply listen for any logging and also log a debug line containing user information.

How To Get Data From A Custom Event In Magento

I have an observer module that I have written for Magento. It simply monitors an event called mgd_order_prep which is triggered by a custom dispatcher like this:
Mage::dispatchEvent("mgd_order_prep", array('orderdata' => $order));
$order is simply a magento sales/order object.
My event fires and my function in the proper class executes:
function updateOrderPrepPDF($observer)
{
Mage::log("Update Order Prep",null,'orderprep.log');
Mage::log($observer->getOrderdata(),null,'orderprep.log');
}
I see what I should after the first log event, but I dont see ANYTHING for when I try to output the order data (it outputs blank - or null).
How do I get the data I pass in at the dispatch event out at the execution point?
You can directly get Data using getData() method :
function updateOrderPrepPDF($observer)
{
Mage::log(print_r($observer->getData(),true),null,'orderprep.log');
}
Check this log inside var/log directory.
Try this code and let me know if you still have any query.

Resources