fire event on pivot sync in Laravel - laravel

i have a pivot model
class UserRoles extends Pivot
{
protected $table="user_roles";
}
i am using syncWithoutDetaching to update the pivot table
$user->roles()
->syncWithoutDetaching(
[ $roleId => [ 'is_active' => $value]]
);
the relation roles() is already using the pivot model class and i created an observer in order to detect the updated event on the pivot model
class UserRolesObserver
{
public function created()
{
Log::info("event fired");
}
public function updated()
{
Log::info("event fired");
}
public function saved()
{
Log::info("event fired");
}
the observer's updated event isn't being fired even when the update is done

Pivot tables do not fire events. You would need to utilize a package like this one to fire pivot events: https://github.com/GeneaLabs/laravel-pivot-events.
Once you install that package you can utilize pivotAttaching and pivotAttached methods on the Observer.

Related

Storing Data and at the same time updating data on a different table in Laravel 8

I have two tables, 'vehicles' and 'assign_vehicles'. Whenever I assign a vehicle to a driver, I want the 'status' column on the 'vehicles' table to be updated to 'assigned'. How do I go about it?
There is some assumptions to be made. You will have to have classes that looks similar to this.
public class Vechicle extends Model {
public function assigned()
{
return $this->hasMany(AssignVehicle::class);
}
}
public class AssignVehicle {
public function vehicle()
{
return $this->belongsTo(Vehicle::class);
}
}
You can monitor the AssignVehicle event and add it to your boot method. Add it in AssignVehicle.php class. From there access the vehicle and update the status. Note there is a creating and created event, creating is before saving to the database and created is after saving to the database
public static function boot() {
parent::boot();
static::created(function (AssignVehicle $assignVehicle) {
$assignVehicle->vehicle->status = 'assigned';
$assignVehicle->vehicle->save();
});
}
For this to trigger remember to use standard Laravel approaches for events to trigger. The following will work.
AssignVehicle::create([
'vehicle_id' => $vehicle->id,
// your fields
])

Call another observer method in current model observer

In laravel I want to call another observers deleting method in my current model observer. Is that possible?
for example lets say I have observers for product and category, So I want to call productobservers deleting method in categoryobservers deleting loop.
public function deleting(Category $Category)
{
DB::transaction(function () use($Category) {
$Category->products->each(function ($products) {
//call product observer deleting function
});
});
}
You can set your event in this array like this
class User extends Authenticatable
{
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
and create listener for those events

Laravel custom pivot with incrementing key observer event error

I have a custom pivot model with an incrementing id. I have set up an Observer, and I get events for things like attach, updateExistingPivot, etc. However, the object that is passed into the observer functions does not include the 'id' field.
This gives me an error when I try to save a related model to the custom pivot (Audit model below).
class Audit extends Model
{
public function auditable()
{
return $this->morphTo();
}
}
class User extends Model
{
public function accounts()
{
return $this->belongsToMany('App\Models\Account')
->withPivot('id')
->using('App\Models\AccountUser');
}
}
class Account extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User')
->withPivot('id')
->using('App\Models\AccountUser');
}
}
class AccountUser extends Pivot
{
public $incrementing = true;
public function account()
{
return $this->belongsTo('App\Models\Account');
}
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function audits()
{
return $this->morphMany('App\Models\Audit', 'auditable');
}
}
If I do
$user->accounts()->updateExistingPivot($acct_id, ['description' => $description]);
an updating event fires, but it does not contain in id:
(
[user_id] => 18000
[account_id] => 111389
[description] => My Description
)
Then if I try to save a new Audit to the custom pivot, I get an error because there is no auditable_id for the SQL statement:
public function updating(AccountUser $auditable)
{
$audit = new Audit();
...
$auditable->audits()->save($audit);
}
Generates
Integrity constraint violation: 1048 Column 'auditable_id' cannot be null (SQL: insert into `audits` (`auditable_id`, `auditable_type`, `updated_at`, `created_at`) values (?, App\\Models\\AccountUser, 2020-04-02 13:24:53, 2020-04-02 13:24:53))
Any help will be appreciated.
Turns out this was fixed in the Laravel 7.x branch, while I am still on 6.x.
i.e. in 7.x, the custom pivot events will have the id field (and all others) if it exists.

How to setup relationship between 2 table which has both one-to-many and many-to-many relationship?

I have a Users table and an Events table.
It's has one-to-many relationship.
Each user can create many event.
Each event belongs to one user.
Also, it has many-to-many relationship.
Each user can join as many event as they want.
Each event can be joined by many user.
This needs pivot table.
Now, I'm stuck.
This is event model.
public function user(){
return $this->belongsTo('App\User');
}
public function users(){
return $this->belongsToMany('App\User')
->withTimestamps();
}
This is user model.
public function events(){
return $this->hasMany('App\Event');
}
public function events(){
return $this->belongsToMany('App\Event');
}
The problem is in the user model where I can't define multiple function with the same name.
So, is there a way to do this correctly?
Quick answer
Of course you can't have two functions with the same name. In your case, try to use more specific names for each function:
public function createdEvents()
{
return $this->hasMany('App\Event');
}
public function joinedEvents()
{
return $this->belongsToMany('App\Event');
}
Recommendation
You can use a single many-to-many relationship to manage both relations with Pivot information:
users table
id
username
...
events table
id
name
...
event_user table
user_id
event_id
is_creator (default FALSE, unsigned integer)
...
Then when creating an event, relate the user and event objects and set the is_creator field to TRUE.
So in your User model:
app/User.php
public function events()
{
return $this->belongsToMany('App\Event')->withPivot('is_creator');
}
Then in your controller when you want to create an event:
app/Http/Controllers/SomeCoolController.php
public function store(CreateEventRequest $request)
{
// Get your event data
$data = $request->only(['your', 'event', 'fields']);
// create your object
$newEvent = Event::create($data);
// create the relationship with the additional pivot flag.
auth()->user()->events()->attach($newEvent, ['is_creator' => true]);
// the rest of your code.
}
And when a user want to 'join' an event:
app/Http/Controllers/SomeCoolController.php
public function join(JoinEventRequest $request)
{
// Get the event
$event = Event::find($request->event_id);
// relate the ev
auth()->user()->events()->attach($newEvent, ['is_creator' => false]);
// or just this, because its already set to false by default:
// auth()->user()->events()->attach($newEvent);
// the rest of your code.
}
It seems like it's many to many relationships between User and Event so there will be pivot name like user_event
user model
public function events() {
return $this->belongsToMany('App\Event')->using('App\UserEvent');
}
Reference: https://laravel.com/docs/5.7/eloquent-relationships#many-to-many

How to catch model events in pivot tables

I want to track (record) the changes made to each database row. This means, saving a log of each action (insert, update, delete) made to each record of each table.
This issue is solved for models, as they extend from a BaseModel and I'm using model events. However, I cannot seem to find a way to record the changed from pivot tables.
Given the following tables users, profiles and profile_user(profile_id, user_id), I have the following code:
class User extends BaseModel {
public function profiles() {
return $this->belongsToMany('Profile');
}
}
class Profile extends BaseModel {
public function users() {
return $this->belongsToMany('User');
}
}
abstract class BaseModel extends Model {
public static function boot() {
parent::boot();
static::created(function($model) {
return LogTracker::saveRowCreatedOrUpdated($model, true);
});
static::updated(function($model) {
return LogTracker::saveRowCreatedOrUpdated($model, false);
});
static::deleted(function($model) {
return LogTracker::saveRowDeleted($model);
});
}
}
This allows me to record the changes from user and profile but not from profile_user.
I've tried to create a ProfileUser model that extends from Pivot (Illuminate\Database\Eloquent\Relations\Pivot) where I defined the model events but that didn't work.
I'm guessing that's because I never create a new instance of this model. So, I've added the following to my User model (and similar code to Profile):
class User extends BaseModel {
// (...)
public function newPivot(Eloquent $parent, array $attributes, $table, $exists) {
if ($parent instanceof Profile) {
return new ProfileUser($parent, $attributes, $table, $exists);
}
return parent::newPivot($parent, $attributes, $table, $exists);
}
// (...)
}
Still, the events are never fired (actually this method is never executed).
I am updating the relationship through sync():
$user->profiles()->sync(explode(',', $values["profiles"]));
I'm looking for a solution that doesn't involve firing a custom event (as this means that I would have to do this for each pivot table in database).
How can I use model events in pivot tables?
I am aware you don't want a custom event situation but I cannot find any non-custom solution.
You can however do a very simple custom one (pretty much pulled right from Laravel docs):
DB::listen(function($sql, $bindings, $time)
{
//regex matching for tables in SQL query
//as well as firing your event handling code
});
http://laravel.com/docs/4.2/database#running-queries

Resources