読者です 読者をやめる 読者になる 読者になる

ITの隊長のブログ

ITの隊長のブログです。いや、まだ隊長と呼べるほどには至っていないけど、日々がんばります。CakePHPとPlayFrameworkを使って仕事しています。最近はAngular2をさわりはじめたお(^ω^ = ^ω^)

【解】助けて先輩!俺のPHPの配列の組み直しは最適なのか確認したい

CakePHP MySQL

スポンサードリンク

http://www.flickr.com/photos/13863357@N03/1584989760
photo by PS-OV-ART Patty Sue O'Hair-Vicknair, Artist


前回の記事。から無事先輩に教えてもらいました。ありがとうございます!!(^^




助けて先輩!俺のPHPの配列の組み直しは最適なのか確認したい - ITの隊長のブログ


先輩の記事

配列のめも - yamashiro0110の日記



色々試したところ、わかりやすくできたので、そのステップをログにメモ。

先輩から教えてもらったこと


ざっくりですが、「エンティティクラスを使いなさい」ってことでした。


言い訳にはなりませんが、ここ1、2年スクリプト言語をよく触っていたので、クラスを扱うことが少なくなっていました。なので、この発想は自分の頭の中には全然なかった!!!(´Д`) 反省します。。。


あと、どうしてもスクリプトだけでやろうとすると、やっぱり複雑で見辛いのでダメですね。誰でもよめるようなプログラムにしないといけないと思いました。


と、いうことで、エンティティクラスを作ろうと思いました。

CakePHPではエンティティクラスは(簡単に)作れない!!


うわぁあああああああああああああああ!!!探しても簡単に作れなさそうorz


調べてみると、CakePHP3からはあるらしいです。だけど、今回は2.5なのよね(´・ω・)


また、いくつかPluginも見つけましたが、うまく動作してくれませんでした。


ふぇええええ。。。どうしよう。。。(´;ω;)

困って、自分のタスクを見直しているとと新たな問題点を発見


エンティティクラス・・・(´;ω;)と、落ち込みながら、ふとこれから消化するタスクを見ていると、その中に「Youtube動画コンテンツを20で区切りて別ページを作成する。ページネイト機能をつける」とあった。


あれ・・・・・・? このままの組んでいくとまずくね?

  • 動画コンテンツテーブルとタグテーブルをJoinして出力するので、タグの数だけ動画コンテンツのテーブル情報が重複する。
  • と、いうことはテーブルからデータを取得する際、Limit & Offset が使えないことになる。


つまり、動画コンテンツのデータを20取得使用とした場合、まずはタグの重複を削除してカウントし、20あればデータを渡す。足りなければ、20になるまで、SQLを実行し続ける。


うん。これは俺でもわかる。糞コードだ!!!


また問題が発覚したので、再度先輩に助けを求めた。


俺)<先輩〜。こういう問題も発覚しちゃったんですけどどうしましょ〜;ω;


id:yamashiro0110)<オラヨ(`・ω・)つURL


頂いたURL



MySQLのgroup_concatで複数レコードを1行にまとめる - 文系プログラマによるTIPSブログ

MySQLSQLで解決ができた!!


なん・・・だと・・・!?


こんな便利な関数のあんのかよ!!!


MySQL :: MySQL 5.5 Reference Manual :: 12.17.1 GROUP BY (Aggregate) Functions


リファレンスが英語なんで、文字化できませんが、ようは

1つの主キーに対し結果が複数行になった場合、それをひとつの文字列としてまとめることができる。


百聞は一見にしかず。つまりは。

-- sql
select
`table_name01`.`id`,
`table_name01`.`title`,
`table_name02`.`table_02`,
`table_name03`.`table_03`
from
`db_name`.`contents` AS `table_name01`
inner join `db_name`.`table_name02`
on (`table_name01`.`id` = `table_name02`.`id`)
right join `db_name`.`table01_relations_table03`
on (`table_name01`.`id` = `table01_relations_table03`.`table01_id`)
inner join `db_name`.`table_name03`
on (`table01_relations_table03`.`table03_id` = `table_name03`.`id`)
;


-- result
array(
	(int) 0 => array(
		'table_name01' => array(
			'id' => '3',
			'title' => '俺、ツインテールになります。1話 テイルレッドが可愛い',
		),
		'table_name02' => array(
			'table_02' => 'yahoo'
		),
		'table_name03' => array(
			'table_03' => 'TV'
		)
	),
	(int) 1 => array(
		'table_name01' => array(
			'id' => '1',
			'title' => '3DS『モンスターハンター4G』 プロモーション映像4',
		),
		'table_name02' => array(
			'table_02' => 'yahoo'
		),
		'table_name03' => array(
			'table_03' => 'モンスターハンター'
		)
	),
	(int) 2 => array(
		'table_name01' => array(
			'id' => '1',
			'title' => '3DS『モンスターハンター4G』 プロモーション映像4',
		),
		'table_name02' => array(
			'table_02' => 'yahoo'
		),
		'table_name03' => array(
			'table_03' => 'アニメ'
		)
	)
)


上記結果だった、私のSQLでgroup_concat()を使うと(あと、group byも使ってあげて)

-- sql
select
`table_name01`.`id`,
`table_name01`.`title`,
`table_name02`.`table_02`,
-- modify group_concat()
group_concat(‘table_name03`.`table_03` separator ",")
from
`db_name`.`contents` AS `table_name01`
inner join `db_name`.`table_name02`
on (`table_name01`.`id` = `table_name02`.`id`)
right join `db_name`.`table01_relations_table03`
on (`table_name01`.`id` = `table01_relations_table03`.`table01_id`)
inner join `db_name`.`table_name03`
on (`table01_relations_table03`.`table03_id` = `table_name03`.`id`)
-- add group by
group by `table_name01`.`id`
;

-- result
array(
	(int) 0 => array(
		'id' => '3',
		'title' => '俺、ツインテールになります。1話 テイルレッドが可愛い',
		'table_02' => 'yahoo'
		'group_concat(‘table_name03`.`table_03` separator ",")' => 'TV'
	),
	(int) 1 => array(
		'id' => '1',
		'title' => '3DS『モンスターハンター4G』 プロモーション映像4',
		'table_02' => 'yahoo',
		// !!!(°ω° )ブッ!!
		'group_concat(‘table_name03`.`table_03` separator ",")' => 'モンスターハンター,アニメ'
	)
)


ほげぇええええええええええええええええええええ!!!!?????!!!?!?!


おっでれぇえた!!! なんやこれ!!? むっちゃ便利やんけ!!


ちなみに、このまま使うと、PHP側に渡した時の連想配列のKeyが「group_concat(...)」となって、見辛いに扱いづらいので、ASしてあげましょう。

〜 省略 〜
-- modify group_concat()
group_concat(‘table_name03`.`table_03` separator ",") AS tag_name
〜 省略 〜

-- result
〜 省略 〜
		// !!!(°ω° )ブッ!!
		'tag_name' => 'モンスターハンター,アニメ'
〜 省略 〜


これでよし。


また、CakePHPのモデルで使う場合はfieldsのところに追加しましょう。

// 省略ばっかでさーせん。。。
〜 省略 〜
		$params = array(
			'fields' => array(
				〜 省略 〜
				'group_concat(‘table_name03`.`table_03` separator ",") AS tag_name',
				〜 省略 〜
			),
			〜 省略 〜
			'conditions' => '',
			'group' => 'table_name01.id',
			〜 省略 〜
		);
		// find()に渡してデータベースを検索
		$data = $this->find('all', $params);
		return $data;

まとめ


自分で調べるのもいいですが、中々答えにたどり付かないし、泥沼にはまるとテンションも落ちてやる気が上がらないので怖いですよね。


1〜2時間ぐらい困ったら(それでも遅いほうだけど)、先輩等、近くのプログラマに聞いたほうが早いと思った、そんな出来事でした。


そうするとこれまで見えてなかったことが見えてくるかも!?


あとリファレンスは一度は全部目を通さなければとも思いました。理解しなくても、なんとなくでいいので感じを掴むべきだと思います。


また、今回のことを機に、早速私は「リーダブルコード」を買ってきました。


リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)


今回の件、改めまして、先輩ありがとうございましたm(_ _ )m


さぁ、もっとがんばるぞー!


おわり

追記

すんません。sqlを変更するととでコードがどんなふうになったかってのを記述が漏れていましたorz

$result_array = array();

// $sql_dataにmysqlから取得したデータが入っています。
foreach($sql_data as $k => $v) {

	$tmp_array_save = array();

	// 連想配列を再帰的に一番下のデータのみを取得して配列を組み直す
	array_walk_recursive($v, function(&$value, $key) use (&$tmp_array_save) {
		$tmp_array_save[$key] = $value;

		// tagデータは配列化したいので、exploadして配列にする
		// tag1, tag2 => array(tag1, tag2)
		if ($key == 'tag_name') {
			$tmp_array_save[$key] = explode(',', $value);
		}
	});

	// 最後に配列を追加
	array_push($result_array, $tmp_array_save);

}

// 確認コード
dumper($result_array);


すげーーーーーーーーーーーーーわかりやすくなりましたとさ。


Thank you!!