Laravel model events for attach/detach - events

I have 3 tables, products, taxonomies and product_taxonomy, as you can tell the 3rd table is a pivoting table. In taxonomies table, I hold a field called num_products which keeps track of the quantity of products that belongs to this taxonomy. Now how can I trigger a model event every time a product is attached to or detached from a taxonomy? I want to update that num_products value in the model event.

Laravel models have events that you can hook into. You have the following options:
creating
created
updating
updated
saving
saved
deleting
deleted
restoring
restored
You'd code it like this:
User::creating(function($user)
{
if ( ! $user->isValid()) return false;
});
Docs: http://laravel.com/docs/4.2/eloquent#model-events
Or you could use Model Observers that are baked in. You have the following options:
creating
updating
saving
You would write a method on your model:
class UserObserver {
public function saving($model)
{
//
}
public function saved($model)
{
//
}
}
You can then register then register the observer:
User::observe(new UserObserver);
Docs: http://laravel.com/docs/4.2/eloquent#model-observers
Hope it helps.

Related

Updating laravel relationship data

When we inserting or updating the data via Eloquent relationship model, which is the best approach to use?
Example
$user->profile->update(['salary' => 5000]);
vs
$user->profile()->update(['salary' => 5000]);
I understand that
$user->profile() will return the relationship class such as Illuminate/Database/Eloquent/Relations/HasOne
$user->profile will return the actual UserProfile model class
I somehow remember I saw someone recommended to use $user->profile->update() instead of $user->profile()->update() but I couldn't find the article or reference link anymore
However, I found that if $user->profile is null, then it might caused an error such as
Call to a member function update() on null
So will it be easier to use relationship function update all the time?
$user->profile()->create()
$user->profile()->update()
$user->profile()->save()
$user->profile()->delete()
Is there any situation we should use the $user->profile->save() instead?
Or should one use it when it is in the multiple nested relationship?
$user->profile->bank()->create()
$user->profile()->bank()->create()
Update
reference link (for my own understanding)
https://github.com/laravel/framework/issues/13568
https://github.com/laravel/framework/issues/2536
Eloquent attach/detach/sync fires any event?
Conclusion
For now, will use code below in application, both will trigger events
if ($user->bank === null) {
$user->bank()->save(new UserBankAccount($input)); // trigger created event
// $user->bank()->create($input);// trigger created event
} else {
$user->bank->update($input); // trigger updated event
// $user->bank()->update($input); // will NOT trigger updated event
}
You can use forceFill()
Example:
$user->bank->forceFill($data)->save();

Laravel Create multiple records in Pivot table

I'm trying to create a function in our Laravel 5.8 app that would add multiple records to a pivot table. At present we have the following setup;
Users
Training Courses
Users Training Courses (pivot table for the above relationships, with a few extra fields)
I want to be able to show all users in the database, then check their name, pick a training course and hit "Add" and it'll create a record in the pivot table for each user that was selected.
I can't figure out where to start with this - it seems like I need to have a "for each user selected, run the store function" loop in the controller, but I have no idea where to start.
I wasn't sure if there was an easy way to do this in eloquent or not. Is there a simple way to do this?
Eloquent does this automatically if you set up the relationships correctly and you don't have to worry about pivot tables.
class Users
{
public function trainingCourses()
{
return $this->hasMany(TrainingCourses::class);
}
}
class TrainingCourses
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Then you can use the save() method to create the relationship. But I find it better to wrap this function inside a helper method that you can use throughout your code:
class Users
{
...
public function assignTrainingCourse(TrainingCourse $trainingCourse)
{
return $this->trainingCourses()->save($trainingCourse);
}
}
In your code, you could then do something as simple as this:
$user = User::find(1);
$trainingCourse = TrainingCourse::find(1);
$user->assignTrainingCourse($trainingCourse);
Building on this, suppose you have the following route to assign a training course, where it expects a trainingcourse_id in the request:
Route::post('/users/{user}/trainingcourses', 'UserTrainingCoursesController#store');
Thanks to route model binding, Laravel can inference the parent model (user) from the URL, and your controller might look like this:
// UserTrainingCoursesController.php
public function store(User $user)
{
$trainingCourse = TrainingCourse::find(request()->input('trainingcourse_id'));
$user->assignTrainingCourse($trainingCourse);
return back();
}
Of course, you'll want to put some validation in here, but this should get you started.

Problem in showing additional field of pivot table in Larvel Nova

I am using Laravel Nova for my admin side.
I have this problem that in my pivot table, I added additional field.
I have user table and event table. User can have many event, and event can have many users. So I created a pivot table for the 2 models. But I want to the status of the event of user. So I added additional field for status in pivot table.
My problem now is in the Laravel Nova.
In my event resource, I have this:
BelongsToMany::make('Pending Users', 'pendingUsers', 'App\Nova\User'),
It shows another table for pending clients. I can already get the list of users in specific event but I can't show to the table the status of the event of user. If the user is approved or still pending.
What strategy is best in my case? I am thinking to make a model for Event and User.
I don't know how to show the additional field of pivot table in BelongsToMany in Laravel Nova. It just shows what I added in User Resource of Nova. Like ID and name. But I don't know where the status of user's event will show. I want to show it in the table of BelongsToMany of events to users.
You need to make sure that you're defining the relationship in BOTH models, and then letting Nova know which fields you want to show like this:
In your event resource:
BelongsToMany::make('Users')
->fields(function() {
return [
Text::make('status')
];
}),
In your event model:
public function users()
{
return $this->belongsToMany(User::class)->withPivot('status');
}
In your user model:
public function events()
{
return $this->belongsToMany(Event::class)->withPivot('status');
}
I got this working by defining ->withPivot('filed name)' on the relationship it drove me insane for a long time.
BelongsToMany::make('your_relationship')
->fields(function () {
return [
Text::make('your_text'),
];
})
Your relation will be here something just like this what i said previous
public function users()
{
return $this->belongsToMany(User::class)->withPivot('filed name');
}

Laravel Eloquent : Set custom formatted ID in Table

Is there any way where I can add custom formatted ID on create?
for example, there is a table 'Customers', in this table there are one primary key - 'ID (auto increment)' and one unique key 'customer_id'
Now, when I will create a new customer, the 'ID' is auto increment so it will automatically set in DB.
I want to set 'customer_id' like '2018-0001' at the time of create.
CurrentYear-000 INSERTED_ID
You could use the created model event for this. docs
Eloquent models fire several events, allowing you to hook into the
following points in a model's lifecycle: retrieved, creating, created,
updating, updated, saving, saved, deleting, deleted, restoring,
restored. Events allow you to easily execute code each time a specific
model class is saved or updated in the database. Each event receives
the instance of the model through its constructor.
After creating the customer you could generate the customer_id and save it:
Customer.php
use Carbon\Carbon;
class Customer extends Model
{
protected static function boot()
{
parent::boot();
static::created(function ($obj) {
$obj->customer_id = Carbon::now()->year.'-000'.$obj->id;
$obj->save();
});
}
}

Laravel Eloquent ORM - Many to Many Delete Pivot Table Values left over

Using Laravel, I have the following code
$review = Review::find(1);
$review->delete();
Review has a many to many relationship defined with a Product entity. When I delete a review, I'd expect it to be detached from the associated products in the pivot table, but this isn't the case. When I run the code above, I still see the linking row in the pivot table.
Have I missed something out here or is this the way Laravel works? I'm aware of the detach() method, but I thought that deleting an entity would also detach it from any related entities automatically.
Review is defined like this:
<?php
class Review extends Eloquent
{
public function products()
{
return $this->belongsToMany('Product');
}
}
Product is defined like this:
<?php
class Product extends Eloquent
{
public function reviews()
{
return $this->belongsToMany('Review');
}
}
Thanks in advance.
The detach method is used to release a relationship from the pivot table, whilst delete will delete the model record itself i.e. the record in the reviews table. My understanding is that delete won't trigger the detach implicitly. You can use model events to trigger a cleanup of the pivot table, though, using something like:
protected static function booted()
{
static::deleting(function ($review) {
$review->product()->detach()
});
}
Also, I would suggest that the relationship would be a one to many, as one product would have many reviews, but one review wouldn't belong to many products (usually).
class Review extends \Eloquent {
public function product()
{
return $this->belongsTo('Product');
}
}
class Product extends \Eloquent {
public function reviews()
{
return $this->hasMany('Review');
}
}
Of course, this would then require that you tweak your database structure. If you wanted to leave the database structure and your current relationships as they are, the other option would be to apply a foreign key constraint on the pivot table, such that when either a review or product is removed, you could cascade the delete on the pivot table.
// Part of a database migration
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->foreign('review_id')->references('id')->on('reviews')->onDelete('cascade');
Edit: In adding the constraint, you push the cleanup work onto the database, and don't have to worry about handling it in code.
Simpler Steps:
In this example an Account has many Tags:
To delete the Tags, then the Account do this:
// delete the relationships with Tags (Pivot table) first.
$account->find($this->accountId)->tags()->detach();
// delete the record from the account table.
$account->delete($this->accountId);
On the Pivot Table make sure you have ->onDelete('cascade');
$table->integer('account_id')->unsigned()->index();
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->integer('tag_id')->unsigned()->index();
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$review->product()->sync([]) also works.
However $review->product()->detach() is much more explicit.
public function destroy($id)
{
$review = Review::find($id);
$review ->tagged_users()->detach();
return $review ->delete();
}
also works
If that way doesn't solve your problem, you can delete Pivot table records to using the DB option like below ;
DB::table('any_pivot_table')->where('x_column', $xParameter)->delete();
You can solve the problem just like this.
Enjoy your coding !
i guess you have a error in your models relationship conception, the product has many reviews but the review associated with one product so you have here one to many relationship.
but in general the answer in the general case will be using:
$product->reviews()->sync([]);

Resources