質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

解決済

2回答

6081閲覧

モデルに実装したメソッドを利用してorderByしたい

sakura_hana

総合スコア11427

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

2クリップ

投稿2017/11/20 08:30

編集2017/11/20 09:08

Laravel 5.4 を使用したWebアプリを作成中です。
基本的な仕組みは出来たのですが、並べ替えについて質問です。

PHP

1class Item extends Model 2{ 3 public function myPrice () { 4 if (\Auth::User()->role == 'admin') { 5 $result = $this->master_price; 6 } else { 7 $data = $this->prices()->where('user_id', \Auth::User()->id)->first(); 8 $result = (is_null($data) ? $this->master_price : $data->price); 9 } 10 return $result; 11 } 12 13 public function prices() 14 { 15 return $this->hasMany(Price::class); 16 } 17}

Controller内で、このモデルクラスのmyPrice()を元に「orderBy」をかけたく思います。
※追記ここから
サンプルにする為コードを単純化していたのですが、単純にしすぎていました。
実際は上記のようなリレーションを挟んでの処理になります。
「該当のリレーションデータが存在しない場合は自クラスのデータを使う」という処理が入ります。
(自クラスのデータは絶対に入っています)
後出し仕様になってしまって申し訳ありません。
※追記ここまで

$itemList = Item::where('aaa', 'bbb')->orderBy('updated_by', 'desc')->paginate(20);
とりあえず上記コードが動くことは確認しました。
が、orderBy('myPrice', 'asc')にすると(当然ながら)動きません。

Controller側でmyPriceと同じ条件分岐をすればいい話なのですが、出来れば同じ処理を2つ書きたくありません。
Item::where('aaa', 'bbb')->get()したデータを元に配列作成、それをソートする方法もあると思いますが、
そうした場合paginateが使えないのでは?と思っています。

何か良い方法があればお教え頂けると助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2017/11/20 09:30 編集

疑問ですが、pricesがhasManyになっているのはどうしてでしょう?myPriceは1個しかデータがない想定で動作しているようですが、myPriceは「複数pricesレコードがあった場合、先頭1件を使う」という仕様なのでしょうか?
masaya_ohashi

2017/11/20 09:35

もう一点、データベースはMySQLですか?
sakura_hana

2017/11/20 10:28

1つのアイテムに対し、複数のユーザーが異なる価格を付ける為このような形としています(1ユーザーが1つのアイテムに付ける価格は1つだけです)。データベースはMySQLです。
guest

回答2

0

masaya_ohashiさんの回答を元に試行錯誤した結果、解決しました。
一部エラー等出ていたのでscopeメソッドの中身だけ変更して動作するようにしました。

PHP

1//Itemクラス内 2public function scopeOrderByMyPrice($query) { 3 if (\Auth::User()->role == 'admin') { 4 return $query->orderBy('master_price', 'asc'); 5 } 6 else { 7 $query 8 ->selectRaw('COALESCE((SELECT price FROM prices WHERE prices.item_id = items.id and prices.user_id = ? limit 1), items.master_price) as myPrice', [\Auth::User()->id]) 9 ->addSelect('items.*') 10 ->orderBy('myPrice', 'asc') 11 ; 12 return $query; 13 } 14}

変更した所

  • メソッド名を「scopeOfOrderByMyPrice」から「scopeOrderByMyPrice」へ(Ofが不要)
  • admin時は直接並べ替え
  • 「:」を使用するプリペアドステートメントだとエラーが出たので「?」を使うように変更
  • joinだとpricesのデータが無い場合アイテムデータも表示されない、leftJoinだとアイテムデータが重複表示される、groupByは何故かエラーが出る……ので、価格だけselectで追加取得を試みてCOALESCEで反映
  • COALESCEはraw内でしか使えないようなのでselectからselectRawへ変更

投稿2017/11/21 14:49

sakura_hana

総合スコア11427

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masaya_ohashi

2017/11/22 01:42

おー、お見事です。Ofはすいませんでした。なにを見て私はOfを追加したのか…
guest

0

ベストアンサー

scopeを使ってはどうでしょう?本来は独自のwhere句を書くための構文ですが、$queryを受け取れるのでこういった使い方もできるかもしれません(未検証)。

PHP

1class Item extends Model 2{ 3 // roleによって使用するカラム名を切り分ける 4 private function myPriceColumnName() { 5 if (\Auth::User()->role == 'admin') { 6 return 'master_price'; 7 } else { 8 return 'my_price'; 9 } 10 } 11 12 public function myPrice () { 13 $column = $this->myPriceColumnName(); 14 return $this->$column; // カラム名を変数を使って指定 15 } 16 17 public function scopeOrderByMyPrice($query) { 18 return $query->orderBy($this->myPriceColumnName(), 'asc'); // scope処理でorderByを付ける 19 } 20}

PHP

1# 使い方 2$itemList = Item::where('aaa', 'bbb') 3 ->orderByMyPrice() // scopeXXXという名前のメソッドはクエリビルダでこうやって呼べる 4 ->paginate(20);

仕様変更後

正直仕様が複雑すぎて、PHP側の処理とSQL側の処理を共通化して書くとか無理です…同じ動作をするように力技でSQLを書くしかないかと思います。

全然動作確認してないので、このまま正しく動くかどうかはわかりませんが…joinとかDB::rawのサブクエリを使えばどうにかできるかと思います。

PHP

1class Item extends Model 2{ 3 public function myPrice () { 4 if (\Auth::User()->role == 'admin') { 5 $result = $this->master_price; 6 } else { 7 $data = $this->prices()->where('user_id', \Auth::User()->id)->first(); 8 $result = (is_null($data) ? $this->master_price : $data->price); 9 } 10 return $result; 11 } 12 13 public function prices() 14 { 15 return $this->hasMany(Price::class); 16 } 17 18 public function scopeOrderByMyPrice($query) { 19 if (\Auth::User()->role == 'admin') { 20 $query 21 ->select( 22 'items.*', // itemsテーブルのカラム全てと 23 'items.master_price AS my_price' // master_priceをmy_priceとして追加 24 ); 25 } 26 else { 27 $query 28 ->join(\DB::raw('(SELECT item_id, price FROM prices WHERE user_id = :user_id) AS prices', ['user_id' => \Auth::User()->id]), 'prices.item_id', 'items.id') // pricesからuser_idの一致するレコードを引き出し、itemsのidと紐付ける 29 ->groupBy('prices.item_id') // pricesのitem_idでグルーピングすることでpricesの結合を1件に絞り込む 30 ->select( 31 'items.*', // itemsテーブルのカラム全てと 32 'COALESCE(prices.price, items.master_price) AS my_price' // joinしたpricesのpriceがあればpriceを、なければmaster_priceをmy_priceとして追加 33 ); 34 } 35 return $query->orderBy('my_price', 'asc'); // my_price順でソート 36 } 37}

使い方は仕様変更前と同じです。Model内の実装だけが変わります。

投稿2017/11/20 08:39

編集2017/11/22 01:33
masaya_ohashi

総合スコア9206

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sakura_hana

2017/11/20 09:09

回答ありがとうございます。scopeは知らなかったのでとても参考になります。 ただ、申し訳無いのですが実際には単純にカラム名を指定する訳ではない状況でして、この場合どうしたらいいのか現在こちらでも調べています。 後出し仕様で申し訳ありませんが、ご確認頂ければ幸いです。
masaya_ohashi

2017/11/20 09:15

うーん、この仕様ですとどうあがいても複雑なクエリ生成をせざるを得ないかと思います。とりあえず書いてみます。
sakura_hana

2017/11/21 14:53

ご協力ありがとうございました! お手数をお掛けして申し訳ありません。お陰様で解決出来ました。 Laravelで複雑なクエリを書いたのは初めてだったので苦労はしましたが、使い方が分かってきたように思います。 scope、COALESCEは初めて知った要素だったので大変ありがたかったです。また何かありましたら宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問