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

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

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

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

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

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

Q&A

解決済

1回答

2048閲覧

CakePHP3 複数カラムを参照した平均値の取り方がわかりません

Ryota.I

総合スコア22

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

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

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

0グッド

0クリップ

投稿2019/10/17 10:28

編集2019/10/19 10:15

CakePHP3で投稿サイトを制作中です。
現在投稿された記事内の点数と、他ユーザより投稿されたコメント&点数の、
点数部分を元に平均値を出し、スターレートとして反映させようとしています。
平均値を出すためのカラムが2つのテーブルを参照することになっていますが、
上記の構造を前提にAVG関数での記述をいくつか試しましたが、
思うように結果が得られず、現在も格闘中です。

▽環境▽
AWS Cloud9:無料枠
MySQL:ver5.7.26
CakePHP:ver3.8.2
PHP:ver7.2.19

■実現したいこと
下記2点の、点数の平均値を出したいです。
特定記事情報(Icesテーブル)に紐づく
他ユーザからのコメント情報(Commentsテーブル)の

(カラム名)repeat_rate
(カラム名)stock_rate

それぞれの平均を出し、

view側へ、出力したいです。
※Icesテーブルには
投稿したユーザの記事+投稿ユーザが
感じる上記2点の点数(repeat_rate、stock_rate)を入れ、
Commetsテーブルには
その記事に対し、他ユーザがコメント+上記2点の点数を
入れられるようになっています。

■困っていること
「実現したいこと」に記載の内容の
CakePHPでの記述方法がわかりません。
下記コードを元にどのように追記、修正すべきか、
教えていただきたいです。

▽DBのテーブル構成はusers,ices,commentsの計3つです▽

MySQL

1mysql> show create table users \G 2*************************** 1. row *************************** 3 Table: users 4Create Table: CREATE TABLE `users` ( 5 `id` int(11) NOT NULL AUTO_INCREMENT, 6 7  //一部省略 8 9 PRIMARY KEY (`id`) 10) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 111 row in set (0.00 sec) 12 13mysql> show create table ices \G 14*************************** 1. row *************************** 15 Table: ices 16Create Table: CREATE TABLE `ices` ( 17 `id` int(11) NOT NULL AUTO_INCREMENT, 18 `user_id` int(11) NOT NULL, 19//一部省略 20 `repeat_rate` int(11) DEFAULT NULL, ←"リピート率"の平均値として参照したい 21 `stock_rate` int(11) DEFAULT NULL,  ←"ストック率"の平均値として参照したい 22 PRIMARY KEY (`id`), 23 KEY `ice_fk` (`user_id`), 24 CONSTRAINT `ice_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 25) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 261 row in set (0.00 sec) 27 28mysql> show create table comments \G 29*************************** 1. row *************************** 30 Table: comments 31Create Table: CREATE TABLE `comments` ( 32 `id` int(11) NOT NULL AUTO_INCREMENT, 33 `ice_id` int(11) NOT NULL, 34 `user_id` int(11) NOT NULL, 35 `comment` varchar(100) NOT NULL, 36 `repeat_rate` int(11) DEFAULT NULL,  ←"リピート率"の平均値として参照したい 37 `stock_rate` int(11) DEFAULT NULL,   ←"ストック率"の平均値として参照したい 38 `created` datetime DEFAULT NULL, 39 `modified` datetime DEFAULT NULL, 40 PRIMARY KEY (`id`), 41 KEY `comments_fk` (`user_id`), 42 KEY `comments_ices_fk` (`ice_id`), 43 CONSTRAINT `comments_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 44 CONSTRAINT `comments_ices_fk` FOREIGN KEY (`ice_id`) REFERENCES `ices` (`id`) ON DELETE CASCADE ON UPDATE CASCADE 45) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 461 row in set (0.00 sec)

▽CakePHPのModel部分です▽

php

1/src/Model/Table/UsersTable.php 2class UsersTable extends Table 3{ 4 public function initialize(array $config) 5 { 6 parent::initialize($config); 7 $this->setTable('users'); 8 $this->setDisplayField('id'); 9 $this->setPrimaryKey('id'); 10 $this->addBehavior('Timestamp'); 11 $this->hasMany('Comments',[ 12 'foreignKey' => 'user_id' 13 ]); 14 $this->hasMany('Ices', [ 15 'foreignKey' => 'user_id' 16 ]); 17 } 18 19/src/Model/Table/IcesTable.php 20class IcesTable extends Table 21{ 22public function initialize(array $config) 23 { 24 parent::initialize($config); 25 26 $this->setTable('ices'); 27 $this->setDisplayField('id'); 28 $this->setPrimaryKey('id'); 29 $this->addBehavior('Timestamp'); 30 31 $this->addBehavior('Josegonzalez/Upload.Upload', [ 32 'image_file' => [] 33 ]); 34 35 $this->belongsTo('Users', [ 36 'foreignKey' => 'user_id', 37 'joinType' => 'INNER' 38 ]); 39 $this->hasMany('Comments', [ 40 'foreignKey' => 'ice_id' 41 ]); 42 } 43 44/src/Model/Table/CommentsTable.php 45class CommentsTable extends Table 46{ 47 public function initialize(array $config) 48 { 49 parent::initialize($config); 50 51 $this->setTable('comments'); 52 $this->setDisplayField('comment'); 53 $this->setPrimaryKey('id'); 54 55 $this->addBehavior('Timestamp'); 56 57 $this->belongsTo('Ices', [ 58 'foreignKey' => 'ice_id', 59 'joinType' => 'INNER' 60 ]); 61 62 $this->belongsTo('Users', [ 63 'joinType' => 'INNER' 64 ]); 65 }

▽CakePHPのコントローラ部分です▽

php

1/src/Controller/IcesController.php 2public function search() 3 { 4 $ices = $this->Ices->find('all'); 5 $manufacturer = isset($this->request->query['manufacturer']) ? $this->request->query['manufacturer'] : null; 6 $keyword = isset($this->request->query['keyword']) ? $this->request->query['keyword'] : null; 7 8 if($manufacturer){ 9 $where = ['Ices.manufacturer' => $manufacturer]; 10 11 if ($keyword) { 12 $where['OR']['Ices.ice_fraver LIKE'] = "%$keyword%"; 13 $where['OR']['Ices.simple_comment LIKE'] = "%$keyword%"; 14 } 15 16 $ices = $this->Ices->find('all'); 17 $ices->where($where) 18 ->contain(['Comments.Users','Users']) 19 ->leftJoinWith('Comments') 20 ->group(['Ices.id']) 21 //現在思いつく一番希望に近い記述が下記の記載方法でした 22 ->select(['rerate' => 'AVG(Comments.repeat_rate + Ices.repeat_rate)'] 23 ->select($this->Ices) 24 ->order(['rerate' => 'DESC']) 25 26 ->all(); 27 28 $this->set('manufacturer', $manufacturer); 29 $this->set('keyword', $keyword); 30 31 $this->set('ices', $this->paginate($ices)); 32 $this->render('ranking'); 33 } 34

上記コントローラ内の

->select(['rerate' => 'AVG(Comments.repeat_rate + Ices.repeat_rate)']

の部分が自身の思いつく限りの一番希望に近い記述ではありましたが、
この記載方法では、
Commentsテーブルの中だけでrepeat_rateの平均値を出し、
Icesテーブルの中だけででrepeat_rateの平均値を出し、
上記2点を最後に足す、
という処理となるため、本来希望する算出方法とは異なってしまいます。
お手数をおかけしますが、ご教示、よろしくお願いいたします。

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

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

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

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

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

nojimage

2019/10/21 02:02

欲しい値は、(icesのrepeat_rate + icesに紐付くcommentsのrepeat_rateの総和)/ (1 + icesに紐付くcommentsの件数) ということでよろしいでしょうか? repeat_rateは、NULL許可ですが、NULLの場合の取り扱いはどうしますか?0として扱いますか?
Ryota.I

2019/10/21 03:26

お返事ありがとうございます。 指摘されて気づきましたが、repeat_rateとstock_rateは数値を必ず入れてもらう前提に設計しますので、後程テーブルに修正を加えます。 その上で、 icesのrepeat_rate + icesに紐付くcommentsのrepeat_rateの総和)/ (1 + icesに紐付くcommentsの件数) で平均値を取りたいと考えています。
guest

回答1

0

ベストアンサー

まずは、(icesのrepeat_rate + icesに紐付くcommentsのrepeat_rateの総和)/ (1 + icesに紐付くcommentsの件数) をSQLで表現してみましょう

sql

1SELECT 2 Ices.id, 3 ( 4 (Ices.repeat_rate + SUM(Comments.repeat_rate)) / (1 + COUNT(Comments.repeat_rate)) 5 ) AS avg_of_repeat_rate 6FROM ices as Ices 7LEFT JOIN comments as Comments on (Comments.ice_id=Ices.id) 8GROUP BY Ices.id;

(Ices.repeat_rate + SUM(Comments.repeat_rate)) / (1 + COUNT(Comments.repeat_rate)) がその式となります。

これをCakePHPのクエリでselectに指定します。

php

1$ices = $this->Ices->find() 2 // その他の条件 3 ->select($this->Ices) 4 ->group([$this->Ices->aliasField('id')]) 5 ->leftJoinWith('Comments') 6 ->select([ 7 'avg_of_repeat_rate' => '(Ices.repeat_rate + SUM(Comments.repeat_rate)) / (1 + COUNT(Comments.repeat_rate))', 8 ]) 9 // そのままだと、 avg_of_repeat_rate がstringで返ってくるのでfloatにするよう型変換を指定 10 ->setTypeMap([ 11 'avg_of_repeat_rate' => 'float', 12 ]) 13 ->order(['avg_of_repeat_rate' => 'DESC']) 14 ->all();

stock_rateは同様にselectメソッドとsetTypeMapに追加すれば良いので割愛します。

補足:

私なら、テーブル設計を変えてスコア保存用に以下のような ratings テーブルを追加して、Ices,Commentsのレートを同じテーブルに保存できるようにします。

sql

1// ratings belongs to ices | ices hasOne ratings 2// ratings belongs to comments | comments hasOne ratings 3CREATE TABLE `ratings` ( 4 `id` int(11) NOT NULL AUTO_INCREMENT, 5 `ice_id` int(11) NOT NULL, 6 `comment_id` int(11) DEFAULT NULL, 7 `repeat_rate` int(11) NOT NULL, 8 `stock_rate` int(11) NOT NULL, 9 // timestampフィールド、外部キー制約等は割愛 10 PRIMARY KEY (`id`) 11)

Icesのレートを保存する場合は、ice_idのみセットしcomment_idはNULLで、Commentsのレートを保存する場合は、ice_idとcomment_idをセットします。これであれば、ratingsテーブルに集約関数(AVG等)を掛けるだけで希望の値が取得できるようになります。

投稿2019/10/21 04:33

編集2019/10/21 04:41
nojimage

総合スコア957

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

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

Ryota.I

2019/10/22 06:16

nojima様 丁寧な回答ありがとうございます。 返信が遅くなり申し訳ありません。 今回いただいたお返事の内容を元に実行し、希望する平均値の値のとり方を 自分の環境上でも確認することができました。 また、テーブルの設計を見直し方についても、 非常に参考となる内容を、ご提示いただきありがとうございます。 貴重なお時間を割いていただき返答いただいた内容については、 公式ドキュメント等の資料と照らし合わせをしながら、 より深く、理解していきます。 改めて、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問