2
votes

In the home page, I display categories with the products. There are 3 tables in the database that comes into play in home page 'section control', 'categories' and 'products'. The 'section control' table has section name and the category_id they are associated with. The 'categories' table stores the name of the category and parent_id which is the id of the parent category (that is selected from the lists in 'categories' table itself. Means a single table for parent and child category) and the 'products' table has product details and category_id.

Now, when i select a category using section control (backend), it displays products from that category only. If the category selected further has other child categories, it doesn't render the products on those child categories.

Section Control model:

class SectionControl extends Model
{

protected $table = "section_control";
protected $fillable = [
    'name', 'display_name', 'details', 'status', 'has_category', 'category_id'
];

public function category() {
    return $this->belongsTo('App\Models\Category', 'category_id')->with('children');
}

public function products() {
    return $this->category->hasMany('App\Models\Product', 'category_id');
}

}

Category Model:

class Category extends Model
{
protected $table = 'categories';

public function getRouteKeyName() {
    return 'slug';
}

public function children()
{
    return $this->hasMany('App\Models\Category', 'parent_id');
}

// recursive, loads all descendants
public function childrenRecursive()
{
    return $this->children()->with('childrenRecursive');
    // which is equivalent to:
    // return $this->hasMany('Survey', 'parent')->with('childrenRecursive);
}

// parent
public function parentCategory()
{
    return $this->belongsTo('App\Models\Category','parent_id');
}

// all ascendants
public function parentRecursive()
{
    return $this->parentCategory()->with('parentRecursive');
}

public function products() {
    return $this->hasMany('App\Models\Product', 'category_id');
}

public function childrenRecursiveIds() {
    return  $this->childrenRecursive()->pluck('id');
}

public function productsRecursive() {
    $products = \App\Models\Product::whereIn('category_id', $category_ids)->get();

    return $products;
}

}

Product Model:

 class Product extends Model
 {

protected $table = 'products';

public function getRouteKeyName() {
    return 'slug';
}

public function category() {
    return $this->belongsTo('App\Models\Category', 'category_id');
}


public function deals_of_the_week() {
    return $this->hasOne('App\Models\DealOfTheWeek', 'product_id');
}

}

And the getIndex function in the controller is:

    public function getIndex() 
   {
    $section_control = SectionControl::with('category')->get()->keyBy('id');

    $section = [
        'section_control_slider' => $section_control[1],
        'section_control_deals' => $section_control[2],
        'section_control_category_1' => $section_control[3],
    ];

    $data['main_sliders'] = MainSlider::where('status', 1)->orderBy('created_at', 'desc')->get();
    $data['deals_of_the_week'] = DealOfTheWeek::with('product')->orderBy('created_at', 'desc')->limit('20')->get();

     $data['category_1_products'] = $section['section_control_category_1']->products()->with('images')->limit(18)->orderBy('created_at', 'desc')->get();


    return view('frontend.home.index')
                    ->with($section)
                    ->with($data);
}

UPDATE I tried both way by addind the macro in the boot function of AppServiceProvider and in construct funtion in SectionContol Model. None of them worked. Maybe i lack the basics.

public function boot()
{
   Builder::macro('recursive', function () {
        return $this->children->map(function (Category $category) {
            if ($category->children->count()) {
                return $category->children->each->recursive();
            }

            return $category->products;               
       });
    }); 

}

In Controller

   public function getIndex() {
    $section_control = SectionControl::with('category')->get()->keyBy('id');

    $section = [
        'section_control_slider' => $section_control[1],
        'section_control_deals' => $section_control[2],
        'section_control_category_1' => $section_control[3],
        'section_control_category_2' => $section_control[4],
        'section_control_featured' => $section_control[5],
        'section_control_category_3' => $section_control[6],
        'section_control_category_4' => $section_control[7],
        'section_control_category_5' => $section_control[8],
        'section_control_partners' => $section_control[9],
    ];

    $data['category_3_products'] = $section_control[4]->category->recursive();

    return response()->json($data);

}

I would like to explain the scenario again. I am assigning a category to a section. The parent category has a unique category_id and parent_id is null. the subcategories have their own category_id and parent_id that of their parent. There is single table categories for both parent and children. In products table, product are assigned only category_id. if the category assigned to the section is a child, the originally posted code does the trick. But id the category is parent, it doesn't show anything.

Above, I forgot to add SectionControl model. Here it is.

   class SectionControl extends Model
 {
protected $table = "section_control";
protected $fillable = [
    'name', 'display_name', 'details', 'status', 'has_category', 'category_id'
];

public function category() {
    return $this->belongsTo('App\Models\Category', 'category_id')->with('children');
}

public function products() {
    return $this->category->hasMany('App\Models\Product', 'category_id');
}

}

1
You will need to get the entire category tree in of a particular category and then query product table to get all products which have category present in the fetched category array.nice_dev

1 Answers

1
votes

I would add protected $with = ['children']; to the Category model so all categories eager load their children. Then you can recursively walk the child categories to create a flattened array of products:

use Illuminate\Database\Query\Builder;

Builder::macro('recursive', function () {
    return $this->children->map(function (Category $category) {
        if ($category->children->count()) {
            return $category->children->each->recursive();
        }

        return $category->products;               
   });
});


$products = $section_control->category->recursive();

Marcroable Trait