eloquent events, can I use them in model class? - laravel

I am reading up on laravel softdelete and restore cascade here: Laravel 5: cascade soft delete
where a user said:
You should use Eloquent events for this.
Offers::deleted(function($offer) {
$offer->services()->delete();
});
Offers::restored(function($offer) {
$offer->services()->withTrashed()->restore();
});
He did not mention where to place this code, I am interested in listening for eloquent deleted and restored events. Where can I put this code? Can I listen for it in the model class? If not where to place it?

I think...
<?php
class Attribute extends Model implements Transformable
{
//....
protected static function boot() {
parent::boot();
static::deleting(function($model) {
foreach ($model->attributeValue()->get() as $attributeValue) {
$attributeValue->delete();
}
});
}
Or example:
class BaseModel extends Model
{
public static function boot()
{
static::creating(function ($model) {
// blah blah
});
static::updating(function ($model) {
// bleh bleh
});
static::deleting(function ($model) {
// bluh bluh
});
parent::boot();
}
}

Related

Laravel Observer not triggered on UpdateorCreate

I have Model Model1 where I want to update a particular column rank on saved.
protected static function boot()
{
parent::boot();
static::saved(function ($model) {
$service = new Service();
$service->updateRank($model->id);
});
}
I put a log inside the saved. but it is not called
You should use method booted.
protected static function booted()
{
static::saved(function ($model) {
$service = new Service();
$service->updateRank($model->id);
});
}

Delete all of relationship with static::deleting model

I've tried using event handler for delete the relation but it can't work for delete relation of relation. Event hasMany Person and Person hasMany File
class Event
public static function boot() {
parent::boot();
static::deleting(function($model) { // before delete() method call this
$model->person()->delete();
});
}
}
class Person
public static function boot() {
parent::boot();
static::deleting(function($model) { // before delete() method call this
$model->files()->delete();
});
}
}
So when I delete event, it can delete person but it doesn't delete the files row. What should I do?
You should call it one by one to call boot method on each model instance. So, get the instances and then delete them one by one.
class Event
public static function boot() {
parent::boot();
static::deleting(function($model) { // before delete() method call this
foreach($model->person as $person){
$person->delete();
}
});
}
}
class Person
public static function boot() {
parent::boot();
static::deleting(function($model) { // before delete() method call this
foreach($model->files as $file){
$file->delete();
}
});
}
}

Fetch the data of parent model into static::saving() boot function

I want to use data from the parent model to run the saving() function in the child model
Previously, I inserted a computed data to the table in model "Loan" but now I need to insert it into a child model "InterestAmount"
Loan.php
use Illuminate\Database\Eloquent\Model;
class Loan extends Model
{
protected $fillable ['amount','interest','status','duration','member_id','loan_type_id','interest_type_id','loan_payment_type_id'];
//protected $appends = 'interest_amount
protected static function boot()
{
parent::boot();
static::saving(function($model) {
$model->interest_amount = ($model->amount/100 )* $model->interest;
});
}
public function interest_amount()
{
return $this->hasMany(InterestAmount::class,'loan_id','id');
}
}
I want to remove the saving function from Loan.php and use as below.
Interest.php
use Illuminate\Database\Eloquent\Model;
class InterestAmount extends Model
{
public function loan()
{
$this->belongsTo(Loan::class,'loan_id','id');
}
protected static function boot()
{
parent::boot();
static::saving(function($model) {
$model->interest_amount = ($model->amount/100 )* $model->interest;
});
}
}
How do I fetch "amount" and "interest" in this function?
The $model variable inside InterestAmount model refers to an InterestAmount object :
static::saving(function($model){
$model->interest_amount = ($model->loan->amount/100 )* $model->loan->interest;
});
Then you need to get the related loan using your relationship method loan() then get the properties amount/interest.
NOTE: As #namelivia's comment says you're missing the return in the loan() method:
public function loan()
{
return $this->belongsTo(Loan::class,'loan_id','id');
}

How to implement event/listeners with repository pattern in laravel 5.4

I can't make listeners trigger action update, create or delete when I user patter repository.
Addionally I have added my code in order to help my to solve my problem.
TicketController.php
namespace App\Http\Organizer\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Events\Contracts\IEvent;
use App\Entities\Event;
class TicketController extends Controller
{
protected $IEvent;
public function __construct( IEvent $IEvent )
{
$this->IEvent = $IEvent;
}
public function checkFutbolType ($activityId)
{
// I need to listen this action here
$event = $this->IEvent->update(21927, ['title'=>'new title']);
}
}
My RepoEvent.php:
<?php
namespace App\Http\Events\Repositories;
use App\Http\Events\Contracts\IEvent
;
class RepoEvent implements IEvent
{
protected $model;
public function __construct($model)
{
$this->model = $model;
}
public function update($activityId, $params)
{
return $this->model->where('id', $activityId)->update($params);
}
}
My AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Entities\Event;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//event: creating
Event::creating(function (Event $event) {
return $event->creatingEvent();
});
//event: saving
Event::saving(function (Event $event) {
return $event->savingEvent();
});
//event: updating
Event::updating(function (Event $event) {
return $event->updatingEvent();
});
}
}
My interface IEvent.php:
<?php
namespace App\Http\Events\Contracts;
interface IEvent
{
public function update($activityId, $params);
}
My ServicesOrchestration.php:
<?php
namespace App\Http\Administration\Providers;
use App\Entities\Event;
use App\Http\Administration\Repositories\RepoEvent;
use Illuminate\Support\ServiceProvider;
class ServicesOrchestration extends ServiceProvider
{
public function boot()
{
}
public function register()
{
$this->app->bind('App\Http\Administration\Contracts\IEvent', function () {
return new RepoEvent(new Event());
});
}
}
My model Event.php
<?php
namespace App\Entities;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
public function creatingUser() {
\Log::info('creating event');
}
public function savingUser() {
\Log::info('saving event');
}
public function updatingUser() {
\Log::info('updating event');
}
}
thanks in advance.thanks in advance.thanks in advance.thanks in advance.thanks in advance.thanks in advance
Here's the relevant snipped from the docs (scroll to mass updates):
When issuing a mass update via Eloquent, the saved and updated model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
For your code to work you need to first retrieve the actual model instance like below:
public function update($activityId, $params)
{
$instance = $this->model->find($activityId);
$instance->fill($params);
$instance->save();
}
This will have an additional cost of doing two queries instead of one and only being able to update a single model at a time.
A sidenote: You're passing a model instance to the repository but what you actually want is to pass a query builder instance:
$this->app->bind('App\Http\Administration\Contracts\IEvent', function () {
return new RepoEvent(Event::query());
});

Laravel parent model deletion doesn't fire up child model deleting event

I'm using laravel 5.4. I have a project model as parent and project_images model as child and here is the child model:
Schema::create('project_images', function (Blueprint $table) {
$table->increments('id');
$table->integer('project_id')->unsigned();
$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
$table->string('file', 100);
$table->timestamps();
});
so as expected whenever i delete a project all of its images records will be deleted as well. Then i created a deleting event in images model like this:
protected $events = [
'deleted' => ProjectImageDeleting::class
];
If i delete the image itself then this event also fires up but if i delete the project, then no.
My Question
How should i make this works that when i delete the parent, the child event also fires up? Am i missing something? (I already have event and listener and the code in listener works when i delete the image)
Update
I removed that line in migration and changed the project model to this:
namespace AliMHZ;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
public function images()
{
return $this->hasMany(ProjectImage::class);
}
protected static function boot()
{
parent::boot();
static::deleting(function($project) {
$project->images()->delete();
});
}
}
and this is my image model:
namespace AliMHZ;
use Illuminate\Database\Eloquent\Model;
use AliMHZ\Events\ProjectImageDeleting;
class ProjectImage extends Model
{
protected $events = [
'deleting' => ProjectImageDeleting::class
];
public function project()
{
return $this->belongsTo(Project::class);
}
}
and event:
namespace AliMHZ\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use AliMHZ\ProjectImage;
class ProjectImageDeleting
{
use Dispatchable,
InteractsWithSockets,
SerializesModels;
public $image;
public function __construct(ProjectImage $image)
{
$this->image = $image;
}
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
and finally here is the listener:
namespace AliMHZ\Listeners;
use AliMHZ\Events\ProjectImageDeleting;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ProjectImageDeletingListener
{
public function __construct()
{
//
}
public function handle(ProjectImageDeleting $event)
{
\Log::info("File to be deleted: " . $event->image->file);
}
}
After these changes, when i delete project, all its images also will be deleted but the image event still doesn't fire up but if delete the image only then the event works. I'm testing this in laravel tinker and everytime i change any model i exit tinker and lunch it again.
When you are using the $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade'); the deletion is done on a database level, and as thus model events are not being triggered.
To have all the related model events trigger you would have to write code for this in the models manually.
<?php
class Project extends Eloquent
{
public function images()
{
return $this->has_many('Image');
}
protected static function boot() {
parent::boot();
static::deleting(function($project) {
// This will trigger the ProjectImageDeleting event.
$project->images()->delete();
});
}
}
Edited to answer edited part of question
Could you try the following
namespace AliMHZ;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
public function images()
{
return $this->hasMany(ProjectImage::class);
}
protected static function boot()
{
parent::boot();
static::deleting(function($project) {
$project->images()->get()->each(function ($image) {
$image->delete();
});
});
}
}
If you want to read more about why this is happening you can read about it on this issue on GitHub.

Resources