skip to Main Content

I have many products in my categories and I can get all of the products based on the category successfully. But, when I tried to get the trashed Categories (I used soft delete both in the categories and products), it gave me an error

Call to a member function products() on null

I’ve tried to use a different function name, but the error still occurs.

Models

// CategoryModel
public function products()
{
    return $this->hasMany(Product::class, 'category_id','id');
}
// ProductModel
public function category()
{
    return $this->belongsTo(Categories::class, 'category_id','id')->withTrashed();
}

Controllers

// IndexController
public function productByCategory($name)
{
    $categories = Categories::where('name', $name)->first();
    $productsCategories = $categories->products()->get(); // the error occurs here 
    return view('products-category', compact('productsCategories','categories'));
}
// CategoryController
public function trash()
{
    $categories = Categories::onlyTrashed()->paginate(10);
    return view("categories.trash", compact('categories'));
}
// ProductController
public function trash()
{
    $products = Product::onlyTrashed()->paginate(10);
    return view("products.trash", compact('products'));
}

2

Answers


  1. When querying the categories in productByCategory, include the trashed items as well. I assume that when deleting a category, you don’t remove its associations with the products.

    $category = Category::withTrashed()->where('name', $name)->first();
    

    Of course, more logic can be added, but for now, the issue is caused by the trashed categories that are associated with the products. It is not mandatory to handle deleted categories, but the null check would be important for error handling.

    public function productByCategory($name)
    {
        // or without withTrashed() if the $name exists but the category has already been deleted, the result will still be null
        $category = Category::withTrashed()->where('name', $name)->first();
    
        // Handle case where category is not found
        if (is_null($category)) {
            abort(404, "Category not found: $name.");
        }
    
        // products by category
        $products = $category->products()->get();
    
        return view('products-category', compact('products', 'category'));
    }
    

    Always remember that a query may not necessarily return any results. Such cases should always be handled.

    (Also, I was very confused by the incorrect use of naming conventions: for example, instead of using the singular form "category", you kept using the plural, which made the entire answer confusing at first. I have corrected these in my own response.)

    Update

    Based on the description in the comment, there should be a different return for trashed and non-trashed categories.

    public function productByCategory($name)
    {
        $category = Category::withTrashed()->where('name', $name)->first();
    
        // Handle case where category is not found
        if (is_null($category)) {
            abort(404, "Category not found: $name.");
        }
    
        $products = $category->products()->get();
    
        // Return different views based on whether the category is trashed or not
        // Trashed
        if ($category->trashed()) {
            return view('trashed-products-category', compact('products', 'category'));
        }
    
        // Non-trashed
        return view('products-category', compact('products', 'category'));
    }
    
    Login or Signup to reply.
  2. You’re having this error because in your productByCategory method, you’re trying to access the products relationship of a category, but the
    Categories::where('name', $name)->first() call may return null if there is no category with that name found.

    To properly handle soft-deleted categories and avoid this error, you need to include soft-deleted records in your query using the withTrashed method when fetching the category. Here’s the updated method below:

    public function productByCategory($name)
    {
        // Fetch the category, including soft-deleted ones
        $categories = Categories::withTrashed()->where('name', $name)->first();
    
        // Check if the category exists
        if (!$categories) {
            return redirect()->back()->with('error', 'Category not found.');
        }
    
        // Fetch products for the category
        $productsCategories = $categories->products()->get();
    
        return view('products-category', compact('productsCategories', 'categories'));
    }
    

    Also, you’ll need to update your models, to ensure soft-deleted relationship works properly.
    For the category model, add withTrashed to the products relationship to include soft-deleted products when retrieving categories.

    public function products()
    {
        return $this->hasMany(Product::class, 'category_id', 'id')->withTrashed();
    }
    

    for the product model, the category relationship already uses withTrashed() for categories, so it should work fine as it is

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search