2015.7.5
0からはじめるPHP#32【Laravel5で作るデータベース#6-設計を知る-】
ということで、
研究そっちのけで作業を進めた結果データベースの方はプロトタイプが完成しました。
後はCSSなどでGUIを整えたり、管理システムの導入
(しばらくはうちのオケで使ってもらうつもりなので)や、各種バリデーションなどの実装が残っています。
上でお見せしているのは
ノーマルシミュレーションと
クロスシミュレーションという機能なのですが、クロスシミュレーションの方はフォームの初期値を設定していないので、これもまたセッションを用いて設定してもらうようにしないとなーといったところですね。
PHPやLaravelの使い方にはある程度慣れてきたので、次は
jQueryにでも手を出せたらなぁとか思ってるんですけどね。
javascriptはちょろっとしか触ったことないので大苦戦でした。でも動的なことってこのサイトじゃあんまやらないからなぁ・・・・・・。
なんかいいアイデアありませんかね?ゲームとかじゃないと使わないよなぁー(-ω-`;)
一応、言語同士の変換っていろいろできるみたいで、PHPで生成した変数をjavascriptで受け取ったり、その逆も出来るってことを知ったのはいい勉強になったかなと思います。
クロスシミュレーションは動的に画面を生成してるので、どうしてもjavascriptでやらなくてはいけなかったんですが、データベースの情報はPHPの変数にオブジェクトとして代入してあり、そこでどうしても受け渡しをしなければいけなかったんですが
json_encode();と
JSON.parse();ってのを使えばいけます。
$s=json_encode($search);
$cc=json_encode($colum_count);
$cr=json_encode($cross);
$db=json_encode($addeds);?><script>
var parameter_DB=JSON.parse('<?php echo $json;?>');//PHPの配列をjavascript用に変換 受け渡しが上手くいかないのでグローバル変数にしてます。
var addeds=JSON.parse('<?php echo $db;?>');//上と同様。
var cc=JSON.parse('<?php echo $cc;?>');//カラム数
var search=JSON.parse('<?php echo $s;?>');//ソート表示用の配列を拝借
var cross=JSON.parse('<?php echo $cr;?>')//cross配列(エラー箇所判定用)
これで
PHPの変数をjavascriptに変換するってのができます。たぶんその逆も似たような関数でできるんじゃないかなと思います。
さて、一通りLaravelの仕組みを理解できたところで、プログラミングにおける一番大事な話をします。
タイトルにあります通り
設計の話です。
設計って軽視されがちで、ガリガリ作りながらその場で対策を考える、というのをやりがちです。
今回のデータベースの場合、Laravelに関して良く分かってなくて、具体的にどんなことができるのかが全くわからなかったので、とにかくガリガリ書きまくりましたが、ここいらで拡張性を上げるために設計をし直そうかなと思います。
プログラミングをしたことがある方ならご存知だと思うのですが
スパゲッティコードというものがあります。
何をやってるのか見てもさっぱり分からない、その内部処理は
作ってる本人にしか分からないというものです。
もっとひどいプログラムになると
作った本人にも理解できないプログラムも存在しますからね。
具体的には
僕が3年の実習で作ったプログラムがそうです。(何)
読みにくいプログラムは拡張性に乏しく、後からの変更がしづらいです。
変数定義の仕方を一つとっても、後から欲しいものが簡単に取り出せる変数であるべきなのに、えらく遠回りしないと欲しい値が取り出せないという使い勝手の悪い変数が存在しうるわけです。
そういう使いにくい変数は、大体その場しのぎで作られており、後から流用して横着しようと試みたものの、なんだかつながりが良くわからなくてバグを量産したり、つぎはぎにつぎはぎを重ねて可読性を更に落としていく要因にもなります。
もっとありがちな例としては、なんか色々変数が定義されているものの、使ってる場所は結局同じで
それ配列で良かったんじゃないの?ということも考えられます。
さらに、配列を上手く使うことで、キーを与えれば欲しい値がすぐ手に入る、という便利な変数として使うこともできますが、そういったものは先々を見越して定義しないと作れません。
こういうことは、だいたい設計の段階できちんと値の定義や使い方を考えてやっていれば問題にならないと思います。そのための設計です。
ということで、軽ーくですが、使い勝手の良い設計をするためのTipsを考えていきましょう。
先に断っておきますが、僕は
設計なんて殆どやったことないので手探りで書いてます。
別に良い設計を教えるのが目的じゃなくて、設計って大事なんですよーっていうのを言いたいので。(笑)
いきなり関数の話に入るとややこしいので、まずはビューの方から考えていきましょう。
@extends('layout')
@section('content')
<table>
<tr>
<th>曲名
<th>副題
<th>作品番号
<th>時間
</tr>
@foreach($test as $tests)
<tr>
<td>{{$test->title}}
<td>{{$test->subtitle}}
<td>{{$test->number}}
<td>{{$test->time}}
</tr>
@endforeach
</table>
@endsection
#2でビューの話をした時、上のコードを例に出して、layoutというものを継承することで、いちいちHTMLやらHEADとか書かなくてもよくなるって話でした。
さて、ここで一つ考えてみてほしいのですが、上のコードは見て分かる通りテーブルを表示するページを表していますね。
ただ、冒頭に示した画像のように
どのページでも楽曲一覧を表示する必要があるんですよ。
そういう時は、新しいページに上のコードをコピーして貼り付ければ良いのですが、新しい列を・・・・・・例えば
難易度みたいなカラムを追加しようとした時、これではどうでしょう。
2つのページを修正する必要がでてきますね。
まずこれがダメです。
じゃあどうすればこの作業がラクになるのかを考えたら、答えは一目瞭然ですね。
外部ファイルにして呼び出せば良いわけです。
<!DOCTYPE HTML>
<html>
<head>
<title>Musel(選曲データベースシステム)
<link rel="stylesheet" href="./css/style.css">{{--スタイルシート--}}
</head>
<body>
{{--ヘッダー--}}
Musel(Music Selector)
<hr>
@yield('content'){{--メイン--}}
</body>
</html>
@extends('layout'){{--基盤ファイル--}}
@include('functions'){{--共通部品--}}
@section('content')
あああああああああああああああああああああああああああああ
@yield('table_start')
@endsection
@section('table_start')
<table>
<tr>
<th>曲名
<th>副題
<th>作品番号
<th>時間
</tr>
@foreach($test as $tests)
<tr>
<td>{{$test->title}}
<td>{{$test->subtitle}}
<td>{{$test->number}}
<td>{{$test->time}}
</tr>
@endforeach
</table>
@endsection
@extends('layout'){{--基盤ファイル--}}
@include('functions'){{--共通部品--}}
@section('content')
いいいいいいいいいいいいいいいいいいいいいいいいいいいいい
@yield('table_start')
@endsection
具体的な実装としてはこのようにやるはずです。
実際にアクセスする
(コントローラーからアクセスする)ページは、ページ1とページ2と名づけたページなのですが、その違いは「ああああああ」と「いいいいいい」の部分で
それ以外は全て同じにしたいという時は、このように実装すれば良いことになります。
これで
「テーブルを修正したい」時の修正はfunction.blade.php内のtable_startセクションのみを修正すれば良いということになることが分かってもらえるかと思います。
ちなみに
@includeというものを使っていますが、これはPHPのそれと似たようなものだと思ってくれれば良いかと思います。
大事なのは
変数がすべて受け渡されるということです。そのため、コントローラーから直接変数を渡されていないはずのfunctions.blade.phpでも変数が使えているわけですね。
このように、ビューをパーツごとに分けていくことで、格段と管理がしやすくなります。
上の例だとファイルで分けていますが、全部をファイルで分けるとそれはそれで邪魔なので、セクションで区切っていくことも可能です。
よく使うパーツは1つのファイルにまとめて、適宜該当するセクションを呼び出す、ということもできます。
例えばヘッダーやフッターなんかは別のファイルに移してセクション扱いにするのが正しいと思います。
ビューに関しては基本はこんな感じです。
ま、ビューに関しては視覚的でわかりやすいかと思われるので先に考えてしまって良いかなと思います。
お次はコントローラーの整理ですね。
この選曲データベースでは、コントローラー・・・・・・つまりクラスは1つしか設けていません。
共通する部品を使うようなページ群は、とりあえず同一のクラスで扱ってもよいだろうとは思います。
が、その中でもコピペに値するようなものはなるべく避けることにしましょう。
そのために有効に使えると思われるのが
コンストラクタと
static変数です。
class YourController extends Controller {
/*クラス内静的変数一覧*/
/*self::$変数名とすれば呼び出せます。値の定義はコンストラクタでやっています。*/
//DB配列(ただしint型カラムのみ。DBと同じ名前にしてください。ドットは使えませんのでアンダーバーにエスケープしてください。項目表示したいカラムをここに書けば反映されます。)//
private static $parameter_DB=array("Fl","(Picc)","Ob",'(E_hr)',"Cl","(Es_Cl)","(Bass_Cl)","Fg","(C_Fg)","Hr","Tp","(Crnt)","Trb","Tub","Perc","(Timp)","Hp","Pf","Cel");
private static $search;//$search[通し番号]=array(データベースカラム名,テーブル列名の日本語表記)
privete static $inputs;
~略~
public function __construct()
{
/****コンストラクタ****/
session_start();//セッションの開始
self::$inputs = \Request::all();//送られた配列データを保存
~略~
javaなどのオブジェクト指向言語を触ったことがなければピンと来ないかも知れませんが、まずコンストラクタの話から。
コンストラクタとはつまり、constructor・・・・・・要するに建設者って意味です。
どういうことかというと
オブジェクトが生成された際に必ず実行される関数です。
もっと噛み砕いていえば
一番最初に無条件に実行される関数って思っておけば良いでしょう。
上の例では__construct()というメソッドになっています。このコントローラーにアクセスされた際に、最初にこれが無条件で実行されます。
次に、static変数ですね。コンストラクタのさらに外のスコープに、priveteなものとして宣言されていますね。別にpublicでもいいんですけど、特に必要がない限り変数はprivateにすべきです。これは
カプセル化とか
隠蔽化とか言うんですけど、これはオブジェクト指向の一番大事な価値観です。
僕も学部の授業でこれを習った当初は全然意味わかんなかったんですけどね。(笑)
staticというのは静的という意味らしいですが、普通の変数と何が違うのかって話はjavaの解説サイトとかで分かりやすく説明されてるので、意味が分からない方は各自でググってみてください。僕は
このページ(一番かんたんなJava入門)が分かりやすくていいんじゃないかなと思います。っていうか僕はこのページで理解しました。
つまり、いろんなメソッドで使ったりすると思われる変数はクラス変数としてクラス自体に紐付けしておけ、って話です。
すると、どのメソッドからでも共通した数値にアクセスできたりしますね。上の例では受け取ったデータを$inputsに入れてますが、その内容はまちまちであるにも関わらず、特別な宣言なしで他のメソッドでもこの変数を扱えるんですよね。
ただし、クラス変数は
頭にself::をつけないと使えないようです。普通の変数とはちょっと呼び出し方が違うってことですね。
あと、大事なのは
クラス変数は関数で値を定義できないということです。
なので、関数を使って値を定義したい・・・・・・たとえば、特定の文字列を操作したり、外部から何かを受け取ったりといったことはできません。
ではどうすれば良いのかというと
コンストラクタで代入すれば良いわけです。上の$inputsが例ですね。コンストラクタは普通のメソッドなので、何の制限も受けません。
後は、共通化した関数を用意するとか、ですね。
public function db_sort($header,$db,$column,$option){
/*渡されたデータベースをソートする*/
if($column=="composer"){
$db=$db->orderBy($header.'composers.composer' ,$option);
}else{
$db=$db->orderBy($header.'compositions.'.$column ,$option);
}
return $db;
}
public function index()
{
~略~
/*ソート*/
if(isset(self::$inputs["sort"])){
$results=$this->db_sort("result_",$results,self::$inputs["sort"],self::$inputs["option"]);
}
}
例えば、ソート機能は色んなところで使おうと思っているのですが、そういった機能は関数にまとめるべきです。
具体的な使い方は、上のようにすれば良いです。
$this->関数名()とすれば、あなた自身(クラス)にあるその関数を呼び出してください、って意味になります。
こういったテクニックというか、まぁ基本的なことですが、繰り返しをとにかく避けることでコードをよりコンパクトにまとめていくことを目指してみましょう。
ってことで、次回は具体的な例を挙げて、拡張性を高くするためにプログラムを作り変えることを考えてみましょう。
ちなみに、企業とかだとクラス図とかアクティビティ図とか書いたりするんですが、そんなめんどくさいことはしません。そんなに大規模なシステムじゃないのではしょっちゃいましょう!(笑)