Railsをかじったことがある人がFuelPHPのプロジェクトをざっと把握しようとしたときのメモ
急遽FuelPHPのコードを読む必要に駆られたので、てきとうすぎるから詳しい人から怒られるかな、と思いつつ、
http://fuelphp.jp/docs/1.7/index.html
を読みながらざっとメモしました。1.8まで出ているのですが、バージョンが1.7なのは読むべきコードが1.7だったからです。
書くためじゃなくて、読むためのメモなのでインストールについては、
http://fuelphp.jp/docs/1.7/installation/instructions.html
などを見て済ませています。。
あと、まだRails触り始めて、10ヶ月くらいの初心者なのでRailsの方もかじったことがある程度で、以下の意見はRails派代表とかそういうわけじゃないですし、用語も若干怪しいです。
内容
- FuelPHPについて
- コントローラについて
- ビューについて
- テンプレート
- コンテンツ
- テンプレートを使わない設定
- モデルについて
- モデルクラスの基本
- Model_Clud
- Orm
- 最終的にどの手順で見ていくと手っ取り早そうか
FuelPHPについて
Railsと比較してみたFuelPHPについての印象
Railsが設定より規約に基づいて設計されているのに対し、FuelPHPは規約より設定に基づいて設計されている、のが特徴らしいです。
たとえば、
ファイルの拡張子に対してどのMIMEタイプを割り振るのかなんて言うことも、
データベースから日付を取り出すとき、どの形式で取り出すかといったことも全部coreディレクトリにはいったFuelPHPの本体のコードに書いてあります。
この辺、Railsでは、そう言う機能使いたいなら対応するgem入れといてね。
RailsやRailsの利用者は詳細なんて(基本的には)意識しないよ、シンプルな記述で処理の流れを書くことに集中してね、
といっている感じがあるのと結構対照的でした。
ビューモデルというRailsのヘルパのような立ち位置のクラスがありますが、
基本的にRailsのMVCで馴染んでいるなら、ぱっとプロジェクトの全体像を把握する程度ならあまり変わらない印象で、正直助かります。
MVCの基本が同じというのもあるのですが、公式の、そしてそれが有志によって翻訳されているドキュメントの構成がとても丁寧で分かりやすい印象を受けました。
一対一対応ではじめるFuelPHPみたいで、既存のプロジェクトに参加するのであれば、順にドキュメントを読んでいけば、チュートリアルは要らないのではないかと思うくらい。
ruby界隈のRailsをはじめとしたイケイケなフレームワークには結構マニュアルやチュートリアルがマッチョイズムで、
Aという事柄を説明するけど、せっかくだから、どうせ後で要るしBもCもDも入れとくね!
というノリのドキュメントやチュートリアルが割と多い印象があります。
チュートリアルのチュートリアルを探すようなことが結構あったことを思うと、本当にありがたかったです。
並べときゃいいってもんじゃねえんだよ(ブーメラン)
もちろん、oilとかcomposerとかいわゆる初心者いじらなくていいよという部分はドキュメントがついていないので、
そこは自分で頑張る必要がありますが、今回はWebAPIを叩いたらどのテンプレートやJSONが返ってくるか把握したい程度なので、まあ。
この記事で紹介する部分のディレクトリの構成
FuelPHPのディレクトリの全体の構成はググってください。
この記事で紹介する部分のディレクトリの構成は以下のようになっています。
コントローラについて
コントローラクラスの基本
一番最初にコントローラを持ってきたのは、なるべくコードを読み込まずにプロジェクトの概要を把握するにはモデル名(モデルを通してDBを触るのでなければ、テーブル名)の一覧とWebAPIのアドレス一覧表を見るのが手っ取り早そうだからです。
そして、FuelPHPでは、WebAPIのルーティングはコントローラのクラス名と、メソッド名を通して行われます。
たとえば、
というクラスが、app/classes/controller/sample.phpに定義されていたとすると、
localhost:3000/sample/index.php
# localhost:3000の部分は起動設定によります。
ないし
localhost:3000/sample
というurlを打ち込むと、Controller_Sampleクラスのaction_index()メソッドがurlに対応するアクションのメソッドとして呼び出されます。
GETリクエストに応答するアクションをurlに即して定義していきたい場合は、action_xxx()メソッドを使用し、POSTやPUTなどRESTなHTTPメソッドを使用したい場合は、コントローラのクラスを作成する際にController_Restクラスを継承して以下のようにHTTPメソッド名からはじまる名前のメソッドを使います。
GET,POST,PUT,PATCH,DELETEに対応しています。
また、before()やafter()といったコールバックメソッドを設定することができ、その中の1つのrouter($method, $params)で上記のクラス名、メソッド名に依らないルーティングをさせることもできるっちゃできるらしいです。
名前空間等を使ってコントローラを階層化させたい場合のディレクトリ構造はRailsとほぼ同じでした。
ベースコントローラ
Railsではログイン確認処理等コントローラに共有させたいコールバックやメソッドがあった場合、ApplicationControllerを用意して、その名前空間であったり、プロジェクトのコントローラはみんなApplicationControllerのクラスを継承しましょう、という話になっていました。
fuel phpではそういったコントローラをベースコントローラと言います。
before_filterメソッドの変わりにbefore()メソッドの中に各アクションを呼びだす前に共通してやらせたい処理を書いていきます。
Railsでは同じディレクトリ・名前空間にApplicationControllerを用意することが多いですが、fuel phpではベースコントローラと、ベースコントローラを継承する他のコントローラのファイルの位置とクラス名は基本的に以下のように違う階層にするので注意します。
ビューを返すコントローラ
.htmlや.php拡張子で書かれたビュー(アクションごとのビューをコンテンツとよびます)を返すアクションを扱うコントローラはController_Templateクラスを継承する必要があります。
Controller_Templateクラスを継承すると、ページのタイトルを指定したり、ページのコンテンツを指定したりする、$this->templateプロパティなどが使用可能になります。
とりあえずこれだけ把握しておかないとどうにもなりません。
API用のコントローラ
API用のコントローラは、Controller_Restクラスを継承して書きます。
そうすることで、POSTやDELETEなどのHTTPメソッドを利用したアクションの定義が行えるというのは一度書きましたが、APIに対して結果のJSON(など。形式は自分で指定できる)の出力の形式を指定する場合は、このクラスを継承することで使える、$this->responseプロパティに書けばいいようです。
じゃあ、実際書いたらどうなるかというと、たとえば、
と書いたら、このアクションにリクエストを投げた場合、
というJSONが返ってきます。
routes.php
app/config/routes.phpでも以下のようにルーティングが行えます。
が、どちらかというと、基本的にはコントローラのクラス名+メソッド名を見てルーティングを行い、404やroot、aboutページなど特別なアドレスの場合だけを扱っている印象です。
ビューについて
FuelPHPのビューはapp/viewsディレクトリ以下にはいっています。classesにはいません。
テンプレート
Railsでいうプロジェクトのページ全体で使う構成を決めるビュー、application.html.erbなど、はFuelPHPではテンプレートと言うみたいです。
デフォルトではfuel/app/views/template.phpを使います。
下手に説明するより見たら分かる感じになっていたので、とりあえず載せておきます。
コントローラごとに使用するテンプレートを変更したい場合は、コントローラのクラス内で、
とそのコントローラで使用したいテンプレート名(拡張子のぞく)を宣言します。
コンテンツ
テンプレートのファイルで、
のところに挿入される、各アクションに対応するビューのファイルです。
Controller_Templateを継承したコントローラのアクションの中で、
のようにコンテンツを指定すると、
の部分にapp/views/sample/index.phpまたはapp/views/sample/index.htmlの内容が挿入されます。
テンプレートを使わない設定
全画面を使う特別なコンテンツを使いたいといった時に、テンプレートを使わないようにするには、以下のようにResponseオブジェクトを新しく生成したものを返せばいいらしいです。
モデルについて
モデルクラスの基本
もちろんモデルのコードも読む必要がありますが、実はモデルのメソッドは、DBと接続する部分自体は、
であったり、
であったりするので割とRailsとも似ていて助かります。
あえていえば、基本的にはモデルのクラスにはすべてModel_というprefixがついているので長ったらしく見えるかもしれないくらいです。
Model_のprefixは、そのモデルのクラスを使用するコントローラ内で、
のようにuse修飾子で宣言すると取ることが出来ます。
むしろ、取られていた場合に、use修飾子を探すような気もします。
Model_Clud
のようにDBヘ送るSQLの生成を簡易化してくれるメソッドを与えてくれるクラスです。
さらに洗練されたormというものもあるみたいです。
ただし、Model_Cludを継承したクラスでは接続先のデータベースやテーブル名、列の名前を設定する必要があります。
Model_Crud - クラス - FuelPHP ドキュメント
Orm
ormパッケージが使われている場合、$_many_manyプロパティや$_belong_toプロパティを利用することで、RailsのActiveRecordにおけるアソシエーション(has_many,belongs_to)のように、一体多、多対多対応をするモデルにおいて、対応を結びつけるキーや対応するモデルを記述することが出来ます。
ActiveRecordのアソシエーションとの違いは、片方のクラスに記述すればよいことです。
そういうわけで、書くのは楽なのですが、見返す時はそのクラスだけ見ても他のクラスとどう関連しているのか分からないことがあります。
まあ、それくらい全部読めよ、と言われればそりゃそうですが。
Introduction - Relations - Orm Package - FuelPHP Documentation
最終的にどの手順で見ていくと手っ取り早そうか
- app/classes/controller内のクラスのクラス名とメソッド名を確認
- app/classes/models内のクラス名と定数群を確認
- app/classes/controller内のベースコントローラのクラスを特にチェック
- app/views以下のディレクトリ構成を把握し、template.phpという名前のファイルは特によくチェック
- あとは必要に応じて地道にまとめる
さんざん書いておいて、これかい。
アプリケーションの構成はルーティング見れば分かると言ってたけど、
DB見た方がどんなアプリケーションか分かりやすい説もあり、モデルが先でもどっちでもいい気がしてきました。
さて、まとめたし、頑張りますか……。
対話型phpを使う(インタラクティブシェルとインタラクティブモード)
久々にphpの記事を。
いや、さわってるのは、最近どころか明日明後日までずっとphpなんですがね。
と思って調べたらあったので、メモ。
phpで対話シェル
PHP 5.1.0 以降では、--with-readline オプションつきで PHP をコンパイルした場合に CLI SAPI で対話シェルが使えるようになりました。 対話シェルは、-a オプションで使うことができます。
というわけで、実際にターミナルに打ち込んでみたら出来たので非常に感動しています。
だけど、実はこのmacの諸言語の設定は自分でやったわけではないので(だめプログラマだ)、自分でインストールしたインターン先のphpは-aオプションをつけても、
Interactive shell
php > echo "Hello World!\n";
Hello World!
php >
という風にはなってくれません。
こういう風になります。
Interactive mode enabled
//何も表示されない!
PHP対話型シェルをWindowsで使う - 農業をITから考える頁
ここと同じことだと思うのですが、
インターン先の環境はmacなので、自分の場合は--with-readlineのオプション(たぶんこちらだけど、検証のために再インストールしたくない)が入ってないせいかと思われます。
では、こちらはどうやって使うのか。
phpで対話モード
もったいぶりましたが、
Interactive mode enabled
<?php
echo "Hello World\n";
?>
//win -> ctrl + z を押す。 mac -> ctrl + dを押す
//macでctrl + z を押すと終了する
Hello World
という風にファイルの中に書くときとおなじく<?php ... ?>で囲って書いてから、実行コマンドを押すだけです。
ちょっとだけめんどくさいので、それだけで実行ファイルをphp exe.phpみたいに実行するのとどちらが楽か迷ったけれど、一応対話的には使えるみたいです。
シェルが使えるかどうかの判別には
php -m
と入力して出てくるコマンド?の一覧にreadlineが入っていたらシェルモードが使えます。
phpを入れ直すか、apacheの設定を直すかしたら使えそうなのですが、
phpの入れ直しは先々週苦戦したばかりだし、
もう少しパスを通すとかその辺の話に慣れてからにします。
別のphpファイルに記述されたソースコードを読み込む(requireなど)
Bitbucketのアカウントをとってみました。
そして、ソースコードの表示が……あまり見やすくならなかった(あれ?)ので、また方式を考えたほうがいいんでしょうか……。
それでは、本題。
require_onceは次から使うというより今度作ることがあったら、これを基にした設計をちゃんとしたいなと思ったので少し気合入れて調べてました。
今回参考にしたのはこちらです。
パーフェクトPHP
- 作者: 小川雄大,柄沢聡太郎,橋口誠
- 出版社/メーカー: 技術評論社
- 発売日: 2010/11/12
- メディア: 大型本
- 購入: 32人 クリック: 1,065回
- この商品を含むブログ (59件) を見る
PHPマニュアル
requireについて
http://www.php.net/manual/ja/function.require.php
includeについて
http://php.net/manual/ja/function.include.php
さて、単体の機能試しているうちはいいのですが、少し大きなものを書こうとした場合、色んなページで共通に行う処理や定数が出てきます。そういった共通部分が含まれている処理を表示するページごとに別のphpで管理していたりすると、無駄に全体のコードの分量増やしたり、修正するときに面倒くさかったりしてよろしくありません。
そういうときに、複数のページから共通して使われる関数をまとめて一つのphpに書いておいて、
require "読み込まれるphpのアドレス"
ないし、
require_once "読み込まれるphpのアドレス"
のようにrequire/require_onceの構文を使って、いま開いているphpから使いたい関数のあるphpファイルの内容を読み込むようにすることで共通機能と個別の機能が分離できて便利です。
この二つの構文は、読み込みたいphpのアドレスが間違っていたり、そのphpファイルが存在しない場合はエラーが出てその時点で実行が終了します。
_onceをつけている場合は、多数のファイルをrequireで読み込んでくるときに、一度しか同名のファイルを読み込まないようにします。
たとえば、php3からphp1とphp2を読み込むのだけれど、
php1の処理のために、php2の処理が必要なのでphp1でもphp2を読み込んでいる場合。
この場合、都合二回php2が呼び出されます。
すると、php2中の関数やclassが2回宣言されました、という旨のエラーが出て実行がとまってしまいます。
たとえば下記のような場合です。
php1.php
php2.php
php3.php
このままの状態で実行するとコメントにあるようなエラーが出ます。
php3.phpの3行目をコメントアウトしてphp3.phpからphp2.phpを読み込むのをやめるとエラーは出ません。
しかし、こんな具合にいちいち何を読み込んだか、記録したり管理するのは面倒です。
一度一つのファイルを読み込んだら、他のファイルを読み込むときにそのファイルを読み込まないようにしてくれるとありがたいです。それができるのがrequire_onceで、上のコードでrequireをrequire_onceに変更するとエラーは出なくなります。
また、別のphpファイルに記述されたコードを読む構文には、include/include_onceがあり、こちらは、ファイルがなくても処理を進めてくれます(ただし警告は出ますし、ファイルがない場合の処理も記述しておくべきだと思います)。
また、このあいだ書いてみるまで気づかなかったのですが、読み込まれるphpにおいて関数の外に命令を書いていると、読み込んだタイミングで順次実行されていきます。
たとえば、php1.phpを以下のようにかいていた場合、
冒頭にやっほー!と出力されてから上記の場合と同じようにphp3.phpの関数の出力が出てきます。
これはつまり、
require "読み込まれるphp"
と書いた場合、この部分に読み込まれるphpファイルのコードをコピーペーストした結果できる
読み込まれるphpファイルのコード
+そのphpファイルに書いてあったコード
のようになっているphpを開くのと同じ、と解釈したらいいのでしょうかね。
読み込まれたphpファイルの途中にreturnの命令を記述していても、それより後に記述した関数を利用できる(関数の外に書いた命令についてはreturnより後に記述した分はは実行されません)、ということからもそんな気がします。
なんだか秋ごろにパーフェクトphpで勉強していた頃は、関数を二回読み込んだらエラーになる、ということだけ覚えていたもので、関数だけ読み込むというふうに誤解していた節があり結構驚きました。
いまは、requireの類を使って読み込みたいようなファイルは、関数集ないし定数集みたいな感じなのだと考えています。
書いてたものが動くようになりました。
一応動くようになりましたが、ああ、時間が。。
なんでローカルホストで動いていた部分が動かなくなるのさ。
設定の問題なのでしょうか、少し余分にログを出していた部分が全部セッションの動作の邪魔になっていて、
途端に動かなくなりましたが、ようやく修正終わった感じで、とりあえず下のアドレスから見れます。
MAIL:111111@gmail.com(このアドレスの方が実在したらごめんなさい)
password:11111111
で動作が確認できます。
授業と授業の間の暇な空コマを利用して気軽にスキルや知識をワンコインでシェアできる身近な"学び"を提供する[casustu]
http://woshidan.main.jp/casustu/index.php
同じ大学の仲間同士で休み時間の空いている時間に待ち合わせして得意教科を教えあおうという感じのサービスがあるとして作ってました。
もともとの企画では同じ大学ではっていう部分はないです。私の好みと判断です。
もともと、このシチュエーションがとても個人的に思い入れがあr
企画についてどう実現するのか(悪用されたりしないか、実際集まれるか)、といわれたときお互いの信用勝負になると考えまして、それだったら学内とか学部内とか小さい集団でやればいいのでは、と思ったら同じ大学の仲間同士なら気心も知れるし、
いざとなったら大学を通して連絡できるし安心かな、と。
実際にそういうところにもっていって稼動させる行動力などはありませんけど。
作業やってる部分として、デザイン部分や、ログイン画面の説明文は
ITスキルアップキャンプで一緒に組んだチームのメンバーの人が
考えてくださったものの残りが大きいですが、php,JavaScriptで動かす部分は私です。
行数の問題ではないのですが、
大体php3500行、JavaScript2000行ほどでした。
昨日ようやくrequireで別のphpファイルに書いてある関数を読み込むことを覚えて800行弱削ったのですが←
もう少し詳しく明日あたりまとめます。
最初の画面から、
二桁目1~3,三桁目1~5,四桁目0~2の半角数字6桁を入れて
新規登録を押すと遊べます。
データもまだまだ足りてないのですが、
とがつく科目や建築ってつく科目で検索すると動いている様子が確認できます。
動作確認は最新版Chromeのみですが……←
結局、メール送信機能は設定だけのダミーです。
また、入力してあるデータ・設定は架空のものです。
自分がいま動かしたら、結構重かったので、
もっと考えて書かないといけないんだろうなぁ、とも思いましたが。
あとはrequireを使ったのが便利で感動したからそれと
今回の作業の反省についてまとめて、
とりあえず当面はアンドロイドに専念します。
セキュリティの勉強とかしたくなったり、
また追加でphp勉強したいことが出てきたらいじるかもです。
ちょっとした追加の情報を表示させるために小窓を出す
phpのほう、レポートやら実験やらでなかなか書けなくて、あと数日分の時間が取れない。
レポート来年取ればよくない?と一瞬思ったけど、後半がボスのレポートなので気まずいなぁ、とそれなりに諦めつつ書いてました。
でもまぁ、そろそろ、そろそろ……。
まあ、Androidも少しずつ進めたいので週の後半はそっちです。
先週&今日までで書いていた部分は、
- 50音順/カテゴリ別で科目を選ぶフォームの作成
- 上のフォームで選んだ科目で検索ボタンを押すと、その科目が得意教科と登録している人で予定が空いている人がいるかどうか、記入された時間割表が出る。(今週分と来週分の二つ出る)
- 時間割表の■(予定が空いている人がいる)のあるマスをクリックすると、画面が切り替わって、そのマスの人にお願いするための画面へ切り替わる
- お願いするための画面で、お願いする相手を選ぶselect要素の横にもっと知りたいボタンがあって、それを押すと選択した人のプロフィールを確認するウィンドウを開く
です。
4個目の実装について、これでいいのかなーと思うところもあったので、
今回の実装方法をメモしておきます。
- 開く&cookieにユーザーのidがセットされていたら自動的にユーザーのプロフィールを表示するphpを作成する
- select要素中のoption要素のvalueでプロフィールを表示したいユーザーのidを指定する
- Javascriptで指定したユーザーのidを取得。それをcookieで保存してからwindow.open()で1のphpを開く
- window.open()の後でユーザーのidが入ったcookie変数の値を空にする
4とかきちんと時間指定して消しませう、という話ですが←
window.open()で小さいウィンドウが表示される(参考:)ことがわかったとき、ちょっと興奮した勢いで書いてました。楽しいなぁ。
元のページの中でdivの要素.innerHTML="ユーザーの情報"みたいに実装したほうがおなじみの手なのですが、自分のパソコンの画面が小さいのでスクロールが必要になって確認が面倒くさいな(しいては使いにくそうだな)……ということで別窓にしました。
ページ遷移させちゃうとフォームの間で行ったりきたりして変なことが起こりそうですし……。
たくさん情報載せすぎるとページがややこしくなるし(私のパソコンではスクロールなしでは表示しきれなくなるし(笑))、
自分でもちょっとこの部分詳しく見たいけど画面は切り替わってほしく無いなー(そういう意味で最近のニコ動のタグで検索で切り替わらなくなったのうれしいと思います)って思ったので今回は新しくウィンドウ出すことにしました。
まあ、コレかいてて肝心のお願い、ないしそのお知らせ通知ができてないんかい!って感じですが、もう少しで完成しそうだなー(月の初めから言ってる)。
これだから納期がない学生は←
今週から選考でインターンに参加させていただけることになったので、こっちも復習がてら気合入れて書きましょう……。
formのmethodが空白の場合
最近は見たらびっくりするうっかりミス辞典になってないだろうか、このブログ……。
・button要素を押したら投稿せずにJavaScriptで表示を切り替える
というのを書いていたら、フォームに入っていたデータが全部GETの形式でURLに入っていたのでちょっと驚いた。
間違えてボタンのtype="submit"にしたかと思ったけどtype="button"で間違ってなかったので、formのところを見たら、methodが指定してなかった。
method="POST"としたら直った。
また、1つの画面に2つのform要素があって、一つがmethodを指定してなくてもう一つのmethodがPOSTだったんだけど、指定してあるほうのmethodの指定を消してもパラメータがアドレスに渡されなかった。
そういうことがあるのかー、という感じ。
そのフォーム、パスワードも入力してある画面だったもので、ほんと驚いた。
formのmethodはsubmitボタンで投稿しない場合もきちんと指定しよう。
(そもそも他の人は忘れない←)
PHPの関数でセレクト要素を出力しようと思ったら <select></select><option>..</option><option>..</option><option>..</option> ... となる場合
正月全部潰しても、もう書き終わらない感じだから、
明日あたりに今の段階でコーディングに関して反省書いてしまおう。
これで、反省書いたあと同じことをやったらとっても面白いですよね。
<select>タグの中に<select>タグが入っているとそうなります。
この場合は、おかしいことが起こるのは当たり前でしょ、といわれたらそれはそうなんだけど、こういう形で、とかいうのは分からなかったので30分くらい右往左往。
おかしくなったときのコード
function get_options($ids){
for($i =0;$i>count($ids);$i++){
$options = @$options . <option value="'.$ids[$i].'">選択肢$i</option>';
}
//ここにうっかり<select>で挟む部分まで入れていた
$select = '<select>'. $options .'</select>';
return $select;
}
htmlを表示する用のPHP
$ids = array(0,1,2,3,4);
echo '<select>'.get_options($ids).'</select>';
出力結果
<select></select>
<option value="0">選択肢0</option>
<option value="1">選択肢1</option>
<option value="2">選択肢2</option>
<option value="3">選択肢3</option>
<option value="4">選択肢4</option>
訂正
function get_options($ids){
for($i =0;$i>count($ids);$i++){
$options = @$options . <option value="'.$ids[$i].'">選択肢$i</option>';
}
return $options;
}
出力結果
<select>
<option value="0">選択肢0</option>
<option value="1">選択肢1</option>
<option value="2">選択肢2</option>
<option value="3">選択肢3</option>
<option value="4">選択肢4</option>
</select>