Laravel trait saving event is fired on database query - php

In my code, I created a trait to clear out some cache. It's code looks like below.
<?php
namespace App\Models\Traits;
use App\Classes\Utilities\ResponseCache as ResponseCache;
trait ClearsResponseCache {
public static function bootClearsResponseCache() {
static::saving(function () {
ResponseCache::removeAll();
});
static::updating(function () {
ResponseCache::removeAll();
});
static::deleting(function () {
ResponseCache::removeAll();
});
}
}
In my controller I have a custom query to select some rows from the database.
public function fetchAllItems() {
$items = DB::select('My custom query');
return response()->json($items);
}
Now, when this function is run, the saving event of ClearsResponseCache is fired, leading to my cache being deleted. Any idea why that event is being fired?

Related

How to check if a Laravel eloquest relation is eagerLoaded on the retrieved event of a model?

I will try to simplify the problem as much as I can.
I want to disable a relation load from a trait on a resource that happens at the retrieved event.
There is a model we will name Post that uses a trait named HasComments.
class Post extends Model
{
use HasComments;
...
}
The trait listenes for the retrieved event on the model and loads the comments relation.
trait HasComment
{
public static function bootHasComment(): void
{
self::retrieved(function ($model) {
$model->load('comments');
});
}
public function comments(): BelongsTo
{
return $this->belongsTo(Comment::class);
}
}
I want to be able to check if the comments relation was eager loaded and NOT load the relation again.
I tried to check if the relation is loaded but failed.
ex:
self::retrieved(function ($model) {
if (!isset($model->relations['comments'])) {
dd('still loads!');
$model->load('comments');
}
});
or
self::retrieved(function ($model) {
if (!$model->relationLoaded('comments')) {
dd('still loads!');
$model->load('comments');
}
});
I was also thinking of maybe there is a way to disable this behavior when constructin the query but failed again.
ex:
trait HasComment
{
public bool $load = true;
public static function bootHasComment(): void
{
self::retrieved(function ($model) {
if (!$this->load) {
dd('still loads!');
$model->load('comments');
}
});
}
public function comments(): BelongsTo
{
return $this->belongsTo(Comment::class);
}
public function disableRetrievedLoad()
{
$this->load = false;
}
}
Has someone encountered something similar and can give me some help?
There is a method named relationLoaded that returns a boolean. Seems like the right fit for your needs.
Alternatively, if you want to load a relationship if it's not been loaded yet, there's loadMissing.
$post = Post::first();
$post->relationLoaded('comments'); // false
$post->loadMissing('comments'); // makes the queries to load the relationship
$post->relationLoaded('comments'); // true
$post->loadMissing('comments'); // does nothing. comments is already loaded

Laravel Singleton still creating new objects

I have a singleton setup in my AppServiceProvider.php, such as this:
public function boot()
{
$this->app->singleton(Breadcrumbs::class, function($app){
return new Breadcrumbs();
});
View::composer(['partials.*'], function($view){
$view->with('breadcrumbs', new Breadcrumbs());
});
}
Breadcrumbs is just a simple class that manages an array of breadcrumbs and I want there only to be 1 object across the whole app (so every time you call new Breadcrumbs() you actually get the existing object, not a new one. (i think this is what singletons are for?)
But now have added this to the JetStreamServiceProvider.php
public function boot()
{
$this->configurePermissions();
Fortify::loginView(function (){
$breadcrumbs = new Breadcrumbs();
$breadcrumbs->add('login','login.php');
return view('auth.login');
});
}
However instead of using the same object as what was created in the AppServiceProvider it is making a new object (so the breadcrumbs object in AppServiceProvider and the breadcrumbs object in the JetStreamServiceProvider are 2 different objects containing a different set of data)...which is no good.
What am I doing wrong?
If you want to resolve the binding you setup you need to use the IoC container that you set the binding on to resolve it. You calling new Class is you telling PHP directly to create a new instance of that class. Laravel does not change how PHP works.
public function register()
{
$this->app->singleton(Breadcrumbs::class, function () { ... });
}
public function boot()
{
View::composer(['partials.*'], function ($view) {
// resolve the instance from the IoC (Application) Container
$view->with('breadcrumbs', $this->app->make(Breadcrumbs::class));
});
// you can do a "view share" to share this with all views instead:
View::share('breadcrumbs', $this->app->make(Breadcrumbs::class));
}
Fortify::loginView(function () {
$this->app->make(Breadcrumbs::class)->add('login', 'login.php');
return view('auth.login');
});
Laravel 8.x Docs - Service Container - Resolving - The make method
This was the solution I used:
AppServiceProvider
public function register()
{
$this->app->singleton(Breadcrumbs::class, function($app){
return new Breadcrumbs();
});
}
public function boot(Breadcrumbs $breadcrumbs)
{
View::composer(['partials.*'], function($view) use ($breadcrumbs){
$view->with('breadcrumbs', $breadcrumbs);
});
}
JetStreamServiceProvider.php
public function boot(Breadcrumbs $breadcrumbs)
{
Fortify::loginView(function () use ($breadcrumbs){
$breadcrumbs->add('Login',Route('login'));
return view('auth.login');
});
}

Laravel 5.6 - Model events: Can you combine static::creating and static::updating on one?

I have model events:
protected static function boot()
{
parent::boot();
static::creating(function ($questionnaire) {
// Same code here
});
static::updating(function ($questionnaire) {
// Same code here
});
}
Is there a way of combining creating and updating together or is it better to put the same code in some sort of partial to reuse in each event?
https://laravel.com/docs/5.6/eloquent#events
When a new model is saved for the first time, the creating and created events will fire. If a model already existed in the database and the save method is called, the updating / updated events will fire. However, in both cases, the saving / saved events will fire.
The saving event is fired when a model is being created or being updated.
The saved method can be used to handle both the created and updated events:
protected static function boot()
{
parent::boot();
static::saved(function ($questionnaire) {
// Code here
});
}
However, in the cases, when more than these two events should run the same code, you can use closures:
protected static function boot()
{
parent::boot();
$closure = function ($questionnaire) {
// Code here
};
static::created($closure);
static::updated($closure);
static::deleted($closure);
}
You can go about it like so...
// store the events you want to capture
protected static $updateOnEvents = ['saved','deleted',...];
protected static function booted()
{
// loop through them and apply the logic
foreach (static::$updateOnEvents as $event) {
static::$event(function($questionnaire){
// your code here
});
}
}
Note that the booted method is available from Laravel 7.x, if you are using a lower version, you can use booting...

similar tasks on different eloquent events in laravel

I want to run a similar task on some different eloquent events in laravel.
For example Suppose I have a Question model. For my purpose, I used boot() function on the model like this :
class Question extends Model
public static function boot ()
{
parent::boot();
static::updated(function ($Question) {
//some Tasks on $Question
});
static::updating(function ($Question) {
//some Tasks on $Question
});
static::created(function ($Question) {
//some Tasks on $Question
});
static::creating(function ($Question) {
//some Tasks on $Question
});
}
}
As you see in all events , similar Tasks run and may have large code.
what is best and short approach to do that?
You have several ways to refactor this code:
Run saved() and saving() instead of the whole four events. saved() will substitute both created() and updated(), while saving() will substitute both creating() and updating().
Fire a the same job inside all of them:
public static function boot ()
{
Question::saved(function ($question) {
dispatch(new \App\Jobs\job($question));
});
}
Add the code to protected function inside the class (this is the least recommended)
By the way, Model events should be placed in the boot method of a Service Provider, not the Model. Model has no boot method.

Determine if is a soft delete in a event handler Laravel

I have this event handler:
protected static function boot() {
parent::boot();
static::deleting(function($user) { // before delete() method call this
$user->comments()->delete();
});
}
When I use $user->forceDelete(); and $user->delete(); this event is triggered and delete all comments. This is not ok because I want this event to be triggered only on $user->forceDelete();. In my case the other tables does not have soft delete implemented
You can check for the forceDeleting property on the model. This will be set (and true) if you're performing a forceDelete
static::deleting(function($user) { // before delete() method call this
if ($user->forceDeleting) {
$user->comments()->delete();
}
});
In the model class (as example):
public static function boot() {
parent::boot();
static::created(function($item){
Log::info("Model Created:".get_class($item)."-ID-".$item->id.'-by user: '.Auth::id());
});
static::updated(function($item){
Log::info("Model Updated:".get_class($item)."-ID-".$item->id.'-by user: '.Auth::id());
});
static::deleted(function($item){
Log::info("Model Soft Delete:".get_class($item)."-ID-".$item->id.'-by user: '.Auth::id());
});
static::forceDeleted(function($item){
Log::info("Model Force Delete:".get_class($item)."-ID-".$item->id.'-by user: '.Auth::id());
});
}

Categories