2016-09-30 5 views
5

私の問題を示すために、私はこれらの4つのテーブル(雄弁モデルとして定義される)との架空のデータベースを作成しました:ユーザー製品、ShoppingItem(ショート:項目)、注文を。関連するすべての関係を持つ。 (唯一の完全を期すためにここに含まユーザー
製品:Eloquent:直接関連するテーブルと間接的に関連するテーブルを結合するにはどうすればいいですか?

  • ID
  • hasManyの(項目)

受注:

  • ID
  • USER_ID
  • 日付
  • hasManyの(項目)

アイテム:

  • ID
  • をPRODUCT_ID
  • だから、顧客がいくつかの製品を購入するたびにレコードが作成される(ショッピング)アイテムを
  • belongsToの(製品)
  • belongsToの(注文)

をORDER_ID。 注文(ショッピングカート)も作成され、ちょうど1人の顧客(ユーザー)に属するの商品と現在のショッピングプロセスが含まれています。

この問題は、製品のリストとその「成功」を分析する方法に関するものです。彼らはどれくらいの頻度で販売されており、最後に売られたのはいつですか?私は、例えば、単一の製品のためのすべての受注を取得するために、したがって、それらが販売されている頻度をカウントするために、次のクエリを使用することができます

$orders = Order::whereHas('items', function ($query) use ($id) { 
    $query->where('product_id', $id) 
      ->where('date', '<', Carbon::now()); 
})->orderBy('date', 'desc')->get(); 

また、私はのリストを取得することができますpurc(

$products = Products::withCount('items') 
        ->orderBy('item_count', 'desc'); 

しかし、私はすべての製品の一覧を取得したい、彼らは最後に命じられたとき、日付順:彼らはこれで販売されている時間の分だけ注文したすべての製品hased)。その結果は、Eloquentクエリまたはクエリビルダセットでなければならないため、ページネーションを使用できます。

public function getLastTimeSoldAttribute($value) { 

    $id = $this->id; 

    // get list of plans using this song 
    $order = Order::whereHas('items', function ($query) use ($id) { 
     $query->where('product_id', $id) 
       ->where('date', '<', Carbon::now()); 
    })->orderBy('date', 'desc')->first(); 

    return $order->date; 
} 

それは、この製品の最後の購入日を返し、私はこのようなビューでそれを使用することができます:

{{ $product->last_time_sold }} 

私はこのような私の製品モデルで定義されたミューテータを、持っています

しかし、私はEloquent OrderByステートメントで 'last_time_sold'を使用することはできません!

ページネーションの機能を失うことなく、上記のようなデータベース関係内の製品の 'last_time_sold'の値を取得する別の方法はありますか?

結局のところ、製品表を照会して注文した最後の時点で製品を注文する適切な方法は何ですか?

あなたの商品には日付がありません。注文(ショッピングカート)には日付のみがあります。

+0

アイテムにdonotがある場合この '$ query - >はどのように( 'date'、 '<'、Carbon :: now()); –

+0

良い質問です!しかし何らかの理由でそれが機能しています...そして、 '日付'は実際にはテーブルオーダーのフィールドです。 'product_id'はテーブルアイテムのフィールドです。そのサブクエリでは、明らかに、両方のテーブルのフィールド名を混在させることができます。名前が異なる限り、私は推測します。私は決してEloquentの専門家ではありません... – matthiku

+0

商品の詳細と実際のショッピングカート(注文)の日付をこの日までに並べた、すべての*ショッピングアイテムのリストを取得できます: '$ items = item :: with( 'product'、 'order') - > orderBy( '​​product_id'、 'date') 'しかし、すべての商品のリストではなく、すべての商品のリストを生成します。 – matthiku

答えて

1

itemsテーブルとordersテーブルを持つLEFT JOINをグループproductsMAX(orders.date)でグループ化します。

$products = Product::leftJoin('items', 'items.product_id', '=', 'products.id') 
    ->leftJoin('orders', 'orders.id', '=', 'items.order_id') 
    ->groupBy('products.id') 
    ->selectRaw('products.*, max(orders.date) as last_ordered') 
    ->orderBy('last_ordered', 'desc') 
    ->get(); 

これは、次のクエリを実行します:あなたは、すでに発注された製品を取得したい場合はからjoin()の代わりleftJoinを使用する:注1

select products.*, max(orders.date) as last_ordered 
from `products` 
left join `items` on `items`.`product_id` = `products`.`id` 
left join `orders` on `orders`.`id` = `items`.`order_id` 
group by `products`.`id` 
order by `last_ordered` desc 

を。

注2:あなたがstrictモード(laravel 5.3のデフォルト)でMySQLを実行する場合は、groupBy()句でproductsテーブルからすべての列を含める必要があります。

注3:注文されていない製品については、last_ordered列はNULLになります。 MySQLでは、NULLはASCを使用する場合は最初に、DESCを使用する場合は最後に順序付けられます。しかし、AFAIKはこれが実証されていません。だから、あなたは、その動作に依存して使用したくない場合:

->orderbyRaw('max(orders.date) is null') 
->orderBy('last_ordered', 'desc') 
0

このクエリを実行することがありますどのくらいの頻度によっては、製品のタイムスタンプのための「last_ordered」列を作成した方がよいかもしれません注文があるたびにそれに触れてください。

+0

あなたは絶対に正しいですが、それは実際には非常に頻繁なクエリではありません。 – matthiku

関連する問題