I have a typical event hook for soft deleting children:
public static function boot()
{
parent::boot();
static::deleted(function($model1)
{
$model1->hasmanyrelation()->delete();
});
}
and
public function hasmanyrelation()
{
return $this->hasMany('Model2');
}
Now when I use:
$model0->model1->each(function($model1){$model1->delete();});
things work as expected and model2 children of model 1 get (soft) deleted. But when I use:
$model0->model1()->delete();
then all related model1 records get deleted but model2 and all its records remain untouched. What is wrong with the latter, simpler syntax that it deletes model1 records but skips its model events??
It's an old one still it may help someone...
Models should be "pulled" first to trigger an event.
See Official Answer Here
refactor this :
$model1->hasmanyrelation()->delete();
to
$models = $model1->hasmanyrelation;
foreach($models as $model){
$model->delete()
}
Related
I have those tables :
- blocks
- bloackables
--reports
--modalities
--reportGroups
Block.php :
public function reports()
{
return $this->morphedByMany(Report::class, 'blockable');
}
public function modalities()
{
return $this->morphedByMany(Modality::class, 'blockable');
}
public function reportsGroups()
{
return $this->morphedByMany(ReportsGroup::class, 'blockable');
}
Report.php :
public function blocks()
{
return $this->morphToMany(Block::class, 'blockable');
}
The same type of relationship exists for ReportGroups and Modalities...
Basically I'd like to load a Block instance and then check if the instances of Report, Modality and ReportGroup are related to this block.
The idea is to create an edition form with a checkbox for every Report, Modality or ReportGroup instances).
What are the strategies ? I read the Eloquent documentation but I am still confused...
Best regards,
Take care...
Nicolas
i'm not sure i understand you completely:
you have
$reportInstances,$modalityInstances,$reportGroupInstances
and you want to determine who has relation to the current block;
ok, you should load the model 'block' with its relations, and check the relation existance:
$blcokWithRelations=Block::with(['reports','modalities','reportsGroups'])->find($block->id):
foreach($reportInstances as $reportInstance)
{
if($blcokWithRelations->reports->where('id','=',$reportInstance->id)->get->first()!=null)
// this report has relation to the current block
else
// this report doesn't have relation to the current block
}
the same goes for the rest of relations
I am trying to update/delete/create in belongsTo relations.
Company has many sports
sports is belonging to Company
Here is two models.
class CompanySports
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
class Company
public function sports()
{
return $this->hasMany(CompanySports::class,"company_id","id");
}
}
at controller, when sports is added or modified or remove, what is the best practice to update?
i know that many to many, sync can be used. In this, what is the best solution? Should i compare everytime after loading all from database which is not good practice i believe.
From your code, I would first recommend putting your models in separate files, and ensuring they are singular. If you use the artisan make:model command to generate the stubs, it should do this for you.
// app/CompanySport.php // <-- NOTE singular
class CompanySport // <-- NOTE singular
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
}
// app/Company.php
class Company {
public function sports()
{
return $this->hasMany(CompanySport::class,"company_id","id"); // singular
}
}
From there, I find it helpful to build helper methods in the various classes so that the grammar sounds natural and more importantly, belongs to the model. For example:
// app/Company.php
class Company
{
...
public function addSport(CompanySport $sport)
{
$this->sports()->save($sport);
}
public function removeSport(CompanySport $sport)
{
$this->sports()->find($sport->id)->delete();
}
}
These helper functions can then be easily called from anywhere, e.g. controller:
// CompanySportsController.php
public function store(Company $company, CompanySport $sport)
{
$company->addSport($sport);
return redirect('/company/' . $company->id);
}
If you are using these helpers, there is no comparing or sync to be done since you are only using a one to many relationship. Eloquent does everything for you.
Also, I've found this cheatsheet particularly helpful when building out the initial relationships and scaffolding of a new app.
While adding new record of Company Model, you need not to do anything as there is no child for it yet.
While updating an instance of a Company model, again you need not to update anything on its children. As relationship are based on id(primary key) which I believe you don't change while updating.
And now for deleting there are some questions. Do you want to delete the children when the parent is deleting? If so, you can use ON DELETE CASCADE which you can set up in migration like
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
in your spors table.
Well you can make your own function too like answered in here
Well if you don't want to delete the children, you can use softdelete on your Model. set up the relations then like
CompanySports
public function company()
{
return $this->belongsTo(Company::class, "company_id","id")->withTrashed();
}
This way you can get the parent of a children without any error though the parent is deleted.
ive implemented nested comments in laravel with parent_id and there's another table votes where related data's are stored.
I've hasMany relation defined in comments model. Now when i delete a comment, it should delete all its replies and votes as well.
To delete votes i used
$review->votes()->delete();
which works perfectly. but i'm stuck with deleting votes for nested replies.
If i use foreach loop how to loop inside all levels which is dynamic.
public function deletereview($id=null){
$review = Review::find($id);
foreach($review->replies as $reply){
$reply->votes()->delete();
//how to do this for all levels?
$reply = $reply->votes(); // this doesn't work
}
return back();
}
Kindly advise on the proper way of doing it.
Note : i've read through the cascade options from migrations but that doesn't explain anything for nested comments(reply of replies and its related data's).
Thanks
-Vijay
// Review Model
public function deleteRelatedData() {
// Delete all votes of this review
$this->votes()->delete();
// Calling the same method to all of the child of this review
$this->replies->each->deleteRelatedData();
}
// Controller
public function deletereview($id=null){
$review = Review::find($id);
$review->deleteRelatedData();
return back();
}
I would recommend use observer for this.
https://laravel.com/docs/5.8/eloquent#observers
public function deleted(Review $review)
{
foreach($review->replies as $reply){
$votes = $reply->votes;
Votes::destroy($votes)
}
Destroy method allow you to delete multiple models.
For any next level you have to use another foreach loop in this case.
$reply = $reply->votes(); doesn't work since you should use
$votes = $reply->votes;
//or
$votes = $reply->votes()->get();
I have two models
Post.php
id
post
show_id
type = 'movie' or 'tv'
Show.php
id // this only auto increment counter ids
show_id
show_type = 'movie' or 'tv'
the thing is show can be either tv or movie and may two with the same show_id for exmaple one tv could have a show_id of 10 and also one movie can have it but the types are diffrent
i have in post model
public function show(){
return $this->belongsTo('App\Show', 'show_id');
}
in show model
public function post(){
return $this->hasMany('App\Post', 'id');
}
this relationship get the first show with matching show id it sees, wheather its a movie or tv, i want to restrict it to match type column on both sides
post.php:
public function show() {
return $this->belongsTo('App\Show', 'show_id', 'show_id')
->where('type', $this->type);
}
show.php
public function posts() {
return $this->hasMany('App\Post', 'show_id', 'show_id')
->where('type', $this->show_type);
}
UPDATE (the code above does not work!)
Trying to use where clauses (like in the example below) won't work when eager loading the relationship because at the time the relationship is processed $this->f2 is null.
Read more here: Compoships
I just came accross a package https://github.com/topclaudy/compoships
what it does it allows creating relationships based on more than one FK, which laravel doesnt support by default
I think what you're looking for is a polymorphic relation. Instead of having a model that may be one of two "types" there should probably be two separate models on the same relation. For example:
class Post
{
public function Show()
{
return $this->morphTo();
}
}
class TvShow
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
class Movie
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
Then your posts table would have a show_id and show_type field that would relate to either a tv show or movie. Check out the docs for more info, I'm not sure of the rest of your project so I'm not 100% this will fit but anytime you start putting "_type" fields in your table you should question whether or not you should be using a polymorphic relation. This will also likely keep your models cleaner and free of a bunch of if statements as you realize there are other differences between movies and shows and how you handle them.
https://laravel.com/docs/5.7/eloquent-relationships#polymorphic-relations
Could someone please clarify this? I've used Model Events a lot of times before but it seems I haven't tried to access any related models on the initial "created" Event.
For example, I have two Models in a M2M relation to each other:
Book() public function authors()
Author() public function books()
I have saved a new Book object, with related authors, elsewhere in my code and by tapping into the "created" or "saved" Model Event (in EventServiceProvider.php), I would like to be able to update some fields in the related objects at the same time like this:
Book::created(function($book) {
$authors = $book->authors;
foreach($authors as $a){
$a->books_authored += 1;
$a->save();
}
});
..but I can't, as $authors call returns no related objects. If this is the usual behaviour (and I haven't done something incorrect here)? Is there a way to get access to these relations on the initial creation/saving Event?
Thanks in advance.
I think the problem you are having is that the created event is firing before you have attached the authors. You haven't attached your code, but I'm assuming:
Book::create(['title' => 'Foo'])->author()->save(new Author['name' => 'Brian']);
This is actually the following:
$book = Book::create(['title' => 'Foo']) // Book created event fired
$relation = $book->author(); // Relation retrieved
$relation->save(new Author['name' => 'Brian']); //related author attached
You should probably manually fire an event when you attach an author in a function on your book model e.g.
class Book extends Eloquent
{
public function saveAuthor($author) {
if($this->save($author)) {
Event::fire(new AuthorWasAttached($this, $author));
}
}
}
Then do your processing in the AuthorWasAttached event class
This may be of help.
You create the book object and then;
$author=new Author(['name'=>'Joo']);
$book->authors()->save($author);