9

There is a data structure for a e-shop:

Series -> (many to many) -> categories -> (many to many) -> products

For example, series is "Outdoor Series" , categories is "t-shirt" , products are "t-shirt A, t-shirt B, etc... "

And here is the controller that list out products in one category

public function view($series = 0, $cat = 0, $page = 1) {
        $category = Category::find($cat);

        $totalItems = count($category->product);
        $itemsPerPage = 30;
        $currentPage = $page;
        $urlPattern = "/ums/product/view/$series/$cat/(:num)";

        $this->data['product_list'] = $category->product()->orderBy('created_at', 'desc')->skip(($page - 1) * $itemsPerPage)->take($itemsPerPage)->get();
        $this->data['paginator'] = new Paginator($totalItems, $itemsPerPage, $currentPage, $urlPattern);
        $this->data['category'] = $category;
        $this->data['page'] = $page;
        return view('product/list')->with($this->data);
    }

Now, the problem is , I would like to rewrite the code so that instead of showing one category, I would like to show one series as well.

That means if the $series = 0 , then it shows products in one category, if the $cat = 0, then it shows products in multi category

In laravel how to get the products in multi category? try $series->category->product() but no luck, also how to rewrite that function to support showing of the series?

Thanks a lot.

user782104
  • 13,233
  • 55
  • 172
  • 312

4 Answers4

6

Assuming Laravel Model classes - Series, Category and Product

For the Series Model Class, create a function

   public function categories()
   {
        return $this->belongsToMany('App\Category');
   }

For the Category Model Class, create a function

   public function products()
   {
        return $this->belongsToMany('App\products');
   }

Now for a given Series, you can easily retrieve all related categories using the simple function call

$categories = $series->categories();

Finally coming to the main problem of showing products under multiple categories.

for($categories as $category)
{
     $productsOfThisCategory = $categories->products();
     //save into some other data structure, say an array $allProducts
}

$allProducts will have multi-category products for a specific Series.

Refer : Standard eloquent relationship -Many to Many

Paras Jain
  • 393
  • 2
  • 8
  • thanks , how to handle the order by in multi category? – user782104 Jun 06 '16 at 00:20
  • Extend the $category->products() function call to $category->products()->orderBy('field_name') OR $category->products()->orderBy('field_name', 'desc') as per requirement. – Paras Jain Jun 06 '16 at 07:17
1

You can use this answer for sorting.

How to sort by a field of the pivot table of a many-to-many relationship in Eloquent ORM

Community
  • 1
  • 1
Himanshu Raval
  • 790
  • 7
  • 19
1

If I understand you correctly, then your models looks like below

class Series extends Model
{

    // other code

    public function categories() {
        return $this->belongsToMany('App\Category');
    }

    // other code
}

class Category extends Model
{

    // other code

    public function series() {
        return $this->belongsToMany('App\Series');
    }

    public function products() {
        return $this->belongsToMany('App\Product');
    }

    // other code
}

class Product extends Model
{

    // other code

    public function categories() {
        return $this->belongsToMany('App\Category');
    }

    // other code
}

Further to get all products of certain series you need to do so

public function view($series = 0, $cat = 0, $page = 1)
{
    if (!empty($series)) {
        $seria = Series::with(['categories' => function($query) {
            $query->with('products');
        })->find($series);
        // or may be this will work, don't know
        // Series::with('categories.products')->find($series);

        // get all caegories from seria or certain one
        if (empty($cat)) {
            $categories = $seria->categories; 
        }
        else {
            $categories = $seria->categories()->where('id', $cat)->get; 
        }

        // retrieve produts form each category and making from them collection
        $products = $categories->map(function($category) {
            return $category->products;
        })->flatten();

        // or use this approach if above not working
        /*$products = collect([]);

        foreach ($categories as $category) {
            $produts = $products->merge($category->products);
        }*/

        // do your magic
    }
    else {
        // not exactly understand what you want to do when $series is not set
    }

    // do your magic
}
huuuk
  • 4,597
  • 2
  • 20
  • 27
0

My approach would be to create something like a custom relation in the Serie Model:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;

class Serie extends Model
{
    public function categories() {
        return $this->belongsToMany('App\Category');
    }

    public function products()
    {
        return Product::whereIn('id', function($query){
            $query->select('product_id')
                ->from('category_product as cp')
                ->join('category_serie as cs', 'cs.category_id', '=', 'cp.category_id')
                ->where('cs.serie_id', $this->id)
            ;
        });
    }
}

The products() method will return a Builder instance. And you can use it in your controller like:

$serie = Serie::find($series);
$products = $serie->products()->get();

That will execute only two queries:

select * from `series` where `series`.`id` = ? limit 1
select * 
from `products` 
where `id` in (
    select `product_id`
    from `category_product` as `cp`
    inner join `category_serie` as `cs`
        on `cs`.`category_id` = `cp`.`category_id`
    where `cs`.`serie_id` = ?
)

This should also be possible:

$products = $serie->products()
    ->orderBy('created_at', 'desc')
    ->skip(($page - 1) * $itemsPerPage)
    ->take($itemsPerPage)
    ->get()
;
Paul Spiegel
  • 30,925
  • 5
  • 44
  • 53