がるの健忘録 このページをアンテナに追加 RSSフィード

2017-11-08

[][]Model、どうすっかねぇ? 的な

直近思案しているのはLaravel5.5案件なのですが。

まぁ割と「あちこちのPHP MVCフレームワークで言える(ような気がする)」ので、あちこちに疑問を投げかける的な想定で。

端的には「データの入力やvalidateの処理、Modelに書きますか? Controllerに書きますか?」的な内容です。


さて。いわゆるMVCの「Model」ですが。

本来的には

アプリケーションデータ、ビジネスルール、ロジック:システムの本体

をつかさどる、とされています。

言い方を変えると「どのModelを使うかの制御(Controller)、表示や出力(View)」以外全部、をつかさどる、と言い伝えられています。


ただまぁ、どこのRoRが元凶とかActive Record以下検閲削除とかは言いませんが、半歩間違えると、Modelって

・ORマッパーのことでしょ?

的なお話になることも、少なからずあるように思われます。


いや別に「Model === ORM」だ、ってんならそれはそれでよいのですが。

その場合はまぁ「Controllerがfatになるのは避けられないよねぇ」とか思うわけです。

いやもちろん「一旦処理を外に書き出して"Controllerというファイル名のファイルの中身は細くする"」ってことも可能ですが、それって本当に「not Fat Controllerなの?」とか思うわけです。

「書き出したクラス」って、所属はController? Model?

「ControllerでもModelでもないそれ以外」だとしたら、それは本当にMVC? とかとか。


あぁ念のため「おいちゃん的には、トップハム・ハット卿 *1 を否定している」わけではありません。

いやまぁ肯定もとくにせんのですが。

その辺はぶっちゃけると「どっちでもいいんじゃないかなぁ一貫性があれば」。


正直、ある程度の規模のシステムを組めば

・太った子が出てくるか

・やたら大量の子だくさんになるか

のどちらかはどちらにしても不可避なので。ゆえに「ある程度納得できる理由と芯の通った哲学」があればよいのではないかなぁ、と思うのですます。


んで。「どっちでもよい」ので、一旦、仮定として「例えば、トップハム・ハット卿を避ける方向」で考えた場合。

Modelですが。「ビジネスロジック全部まるっと」はちぃと難しそうな気がせんでもないのですが、「あるデータの塊に対する責務全般」くらいは、お願いしてもよいのではないかなぁ? とか、おいちゃん個人としては思うのでございます。

ほら「Tell, Don't Ask」とか言うじゃないですかまぁ一般的なModelのコードもそんな感じだとは思うのですが。とはいえ、もうちょっと「いろいろ、Modelに持ち込んでもよいのかなぁ」とか、なんとか。


で、ちょいとちょうど良い感じがあったので、さっそくの実例

ちと書いてもらったコードがあったので、微妙に細部を変えながら、転記。

    public function postTweet(Request $request)
    {
        $userId = auth()->id();
        if (null === $userId) {
            return redirect('/');
        }
        // ここはもうちょっと「$request->validate」つかうのが本当なんだろうと思う
        $tweetPost = $request->input('tweet');
        if (144 < strlen($tweetPost)) {
            return redirect('/');
        }

        $tweetModel = new Tweet();

        $tweetModel->user_id = $userId;
        $tweetModel->tweet = $tweetPost;
        $tweetModel->save();

        Session::flash('message', 'tweet した');
        return redirect('/');
    }

うんたぶん、LaravelでなくほかのPHP MVCフレームワークであっても、大体こんな感じだろうなぁと思うですサンプルコード見ている限り。

別の場所をグぐってみても。

http://libro.tuyano.com/index3?id=7896003&page=3

public function postNew(Request $request)
{
    $name = $request->input('name');
    $mail = $request->input('mail');
    $age = $request->input('age');
    $data = array(
        'name' => $name,
        'mail' => $mail,
        'age' => $age
    );
    MyTable::create($data);
    return redirect()->action('HeloController@getIndex');
}

https://qiita.com/yagi21/items/eea131ef0d3bc20be59a

  public function res(Request $request){
    //フォームから受け取る
    $フォームのname = $request->input('フォームのname');
      .
      .
      .
    //DB保存
  }

などなど。

公式も

https://readouble.com/laravel/5.5/ja/eloquent.html

class FlightController extends Controller
{
    /**
     * 新しいflightインスタンスの生成
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // リクエストのバリデート処理…

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

なので、Laravel公式的にも「処理はControllerに書く」のほうに倒れてるんだろうなぁ、とは思うです。

さて上述に共通するのは「Controllerにデータ取得とvalidateの処理が書いてある」あたり。


んで、おいちゃんが気になっているのが

・入力データの取得

・validate

の処理を「Controllerでやっている」あたり。

その辺「どうなんだろう??」と思うですだす。


で、一案っつか二案なんだが。

上述の処理を「model側に移す」のって、みんなの印象値的にどうなんだろう? という、質問ですます。

1つは「全部まとめて1メソッド」、もう1つは「入力、validate、保存の3メソッド」の提案。

まぁModel内部的にはどちらにしても切り分けるんだろうけど。


1つまとめの場合は身もふたもなくて

    public function postTweet(Request $request)
    {
        $tweetModel = new Tweet();
        $r = $tweetModel->insert用に入力してvalidateして保存();
        if (true === $r) {
            Session::flash('message', 'tweet した');
        }
        return redirect('/');
    }

3つの場合は

    public function postTweet(Request $request)
    {
        $tweetModel = new Tweet();
        $r = $tweetModel->insert用の入力();
        if (false === $r) {
            return redirect('/');
        }
        $r = $tweetModel->insert用のvalidate();
        if (false === $r) {
            return redirect('/');
        }
        $r = $tweetModel->insert保存();
        if (true === $r) {
            Session::flash('message', 'tweet した');
        }
        return redirect('/');
    }

なんかifがうざったいから、例外投げるほうが楽かもしんまい。

    public function postTweet(Request $request)
    {
        try {
            $tweetModel = new Tweet();
            $tweetModel->insert用の入力();
            $tweetModel->insert用のvalidate();
            $tweetModel->insert保存();
        } catch(Throwable $e) {
            return redirect('/');
        }

        Session::flash('message', 'tweet した');
        return redirect('/');
    }

こんな感じ。

細かい話をすると「insertとupdate」って、似てるけど処理が違うので。データ取り込みもvalidateも保存する時も、微妙に入り口を分けていたい感じではある。

ので、全体的に「insert用の」ってつけてるの感じ。


Modelのほうはまぁだいたいイメージがつくと思うので適宜オミット。

ちな、これの実装を愚直にやると「formのnameアトリビュート値が固定」になるんだけど。「いやまぁ固定でもいいじゃない」ってのと「可変にしたきゃ、デフォルト引数とかうまく使ってよ」とか、まぁ解決策はいくつか。

面倒なんで書かないけど、質問がきたら書くかも。


あと、細かいところでエラー処理。

まぁ例外投げる時は「例外のメッセージの中に、それこそjson文字列とかででも細かい情報突き返せばよくね?」とかアバウトにアバウトに。いやまぁModelインスタンスん中にエラー情報入れてgetってもいいだろうし。

戻り値でreturnであれば、別途「error_detail」とかってメソッド使って詳細吐き出せばよいと思うですます。


ってなわけで、これやると「Controllerでやることが減る」変わりに「Modelでやることが増える」んだよねぇ当たり前だよ動かしただけだもん。

ただ、Modelって「実のところ、なんなの?」って考えた時に。

もしModelが「ある程度のビジネス処理をつかさどるところ」なのであれば、一つの提案としては上述のようなコード「も」想定できるかなぁ、とか思うわけですます。


一方で「LaravelのController(の1メソッド)って、必ずしも"外部から呼ばれる"前提とは限らない」と思われるので(web.phpとかにルーティング書かなければ呼ばれないし)。

例えば「このControllerのこのメソッドは"このデータの塊を、入力受け取ってvalidateして保存する"用のメソッドなんだ!!」ってなるのであれば、それはそれで「そーゆー方向性と指針なんだなぁ」とも思うのです。「それって、ビジネスロジックって言わね?」とかって疑問もありますが、別にそれはそれで一貫性があれば。「Controllerにビジネスロジックを書いたら死ぬ」ってわけでもないだろうし。

いやまぁ結果的に「Controllerに書く内容が増えて、太りやすくなるよなぁ」とは思うのですが「それはそれでいいじゃん」であれば、それはそれでよいんじゃないかなぁ、とも思いますです。はい。


ってなわけで。

上述みたいなコードで「データに紐づく責務」をModel側に移すのって、世間的にはどーゆー反応なのかしらん? ってのが、おいちゃんの疑問。いや上述のような感じのコードって、見る見ないでいうと「ほとんど見かけない」ので。

個人的見解と感想と好みと雑感と偏見」ざぶざぶでよいので、コメントなどいただけると、おいちゃんが喜んだりすると思われますので、ぜひ ノ

*1: 「ふとっちょのきょくちょう」(Fat Controller)、って、説明しなきゃわからないギャグを書くな

ndxbnndxbn 2017/11/08 21:17 Laravel 的には、 入力値のバリデーションは Controller に到達する前に行ってしましますねー。
Request クラスを作って、そこにバリデーションルールを列挙しておく。Controller では、各種メソッド(index とか store とか)の引数に、そのRequestクラスを指定しておく。
Request クラスは、フレームワークが Controller に渡す前にバリデーションして、だめだったら「アクセス元のページに $error にエラーメッセージを添えて、戻す」っていう挙動するです。

https://readouble.com/laravel/5.5/ja/validation.html


Laravel の考え方を普通の MVC に持ち込むなら、「Validation は Controller に書く。実装的には、 Validation 部分だけ切り出して、再利用可能な感じにする。」になるのかなぁって思うです。

増田増田 2017/12/14 14:56 記事に関係無い内容ですみません。
ブログタイトルですが、「備忘録」を「健忘録」と誤字しているので問題なければ修正していただければと思います。
(フレーバーとしてわざとなのかもしれませんがその文脈を読みきれませんでした…)

gallugallu 2018/01/18 18:59 To 増田さん
すみません誤字ではなくて、意図的になります(苦笑

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/gallu/20171108/p1