Search code examples
phpclassooplaravel-5polymorphic-associations

Stale objects when using polymorphic many to many relationships in Laravel 5.2


I wrote a class called Categorizable that allows for a polymorphic many to many relationship between a class that extends it and the class Category. It works great but I noticed that if I call a method to add a Category to a categorizable object it adds the entry on the join table but my actual object isn't updated. If I try to call categories on my object, it's not updated with what's on the database. I would like to keep my objects up to date so i don't have to query the database for it each time I add a category.

Categorizable.php

class Categorizable extends Model{

public function categories(){
    return $this->morphToMany(Category::class, 'categorizable');
}

public function addCategory(\App\Category $category){
    if ( $this->isValidCategory($category) ){
        if ( $this->hasCategory($category) ){
            return false;
        }
        else{
            $this->categories()->save($category);
            return true;
        }
    }
    else{
        return false;
    }
}

public function removeCategory(\App\Category $category){
    if ( $this->isValidCategory($category) ) {
        if ($this->hasCategory($category)) {
            $this->categories()->detach($category);
            return true;
        }
        else {
            return false;
        }
    }
    else{
        return false;
    }
}

public function hasCategory(\App\Category $category){
    return $this->categories->contains($category);
}

public function isValidCategory(\App\Category $category){
    if ( $category->category_type === get_class( $this ) ){
        return true;
    }
    else{
        return false;
    }
}

If this is not possible with Laravel 5.2 or considered bad practice, please say so. If you have any recommendations on how I could improve this, that is also welcome. Thanks.


Solution

  • Consider handling this in a controller as explained in https://laravel.com/docs/5.4/eloquent-relationships#updating-many-to-many-relationships

    It will simplify what you want to achieve. Let's assume the categories are related to an article and the controller is ArticleController. $article is the model object and the isValidCategory() check is done. Add a category or remove one becomes:

    $article->categories()->attach($category->id);
    $article->categories()->detach($category->id);
    

    Resetting all relationships of that article:

    $article->categories()->detach();
    $article->categories()->attach($categories); //flat array
    

    Or do this in one line:

    $article->categories()->sync($categories);
    

    Remember, you can either use one id or an array of id's like [1, 2, 3]

    Note: $category->id is an assumption, if $category is the id, then obviously, use $category