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

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

ただいまの
回答率

88.91%

MVCアーキテクチャでViewからDBを参照するのは良しとするか

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 503

XNXSXMXR

score 236

MVCアーキテクチャのオリジナルフレームワークを使用した案件開発ですが、他のMVCフレームワークでも当てはまるかと思います。
日頃からループの回数を減らすべく意識しているのですが、表題とおりViewからDBを参照する是非について頭を悩ましています。

コーディングというよりも、施策についての質問です。

作成しようとする画面

stdClassのオブジェクト変数「$orderList」があり、中身は以下のようなイメージです。
その中の「lists」プロパティをループさせて、tableタグによる一覧を作成します。

またその中の「order_number」をキーにDBテーブル「order_status」をSELECTし、値を表示させます。

// $orderList
stdClass Object
(
    [customer_no] => 1
    [lists] => Array
        (
            [0] => stdClass Object
                (
                    [order_number] => 10000
                    [kubun] => 見積
                )
            [1] => stdClass Object
                (
                    [order_number] => 10001
                    [kubun] => 見積
                )
        )
)

↓ order_statusテーブル

order_number status
10000 了承
10001 査定中

↓こんな一覧を画面で表示させる

注文番号 区分 ステータス
10000 見積 了承
10001 見積 査定中

※$orderList変数の中にstatusを含めたいところですが、仕様のため含められません。

MVCアーキテクチャですので、データ処理などビジネスロジックはモデルに、ビューとモデルの連携をコントローラが行うルールで開発していますが、やろうと思えばビューからモデルのクラスを呼び出すことは可能です。

悩んでいる箇所

以下の案①か案②で行うか悩んでおります。

案①:DBへの問合せを1回にする

DBの問合せを1回で済ますには以下のようになるかと思いますが、ループが3回発生します。

1. $orderList->lists をループさせ、order_numberを取り出す

foreach ($orderList->lists as $key => $val) {
  $orderNumbers[] = $val->order_number;
}

2. $orderNumbers配列を使ってクエリを組み立て、order_statusテーブルの値を得る

// ------------------------
// Modelにてデータを生成
// ------------------------
$sql = 'SELECT '
    . ' order_number '
    . ',status '
    . ' FROM '
    . ' order_sutatus '
    . ' WHERE '
    . ' order_number in (' . implode(',', $orderNumbers) . ')';

// DBクラスでSQLを実行し、結果を配列で取得する。
$buff = Db::fetchAll($sql);

if (!empty($buff)) {
  foreach ($buff as $key => $val) {
    // order_number を配列のキーとする
    $orderStatus[ $val['order_number'] ] = $val['status'];
  }
}

// 作られる $orderStatus 配列はこんなイメージ
// $orderStatus[10000] ... "了承"
// $orderStatus[10001] ... "査定中"

3. tableタグの一覧を生成する

// ------------------------
// Viewにて画面を生成
// ------------------------
$html = '<table>'
    . '<tr>'
    . '  <td>注文番号</td>'
    . '  <td>区分</td>'
    . '  <td>ステータス</td>'
    . '</tr>';

foreach ($orderList->lists as $key => $val) {
    $html .= '<tr>'
        . '<td>' . $val->order_number . '</td>'                  // 注文番号
        . '<td>' . $val->kubun . '</td>'                         // 区分
        . '<td>' . $orderStatus[ $val->order_number ] . '</td>'  // ステータス
        . '</tr>';
}

$html .= '</table>';

案②:tableを作成するループ内でDBに問合せする

ループは1回だけで済みますが、DBへの問合せが件数ぶん発生しますし、ViewからModelを呼び出すのでMVCアーキテクチャの意味から外れる感じがします。

// ------------------------
// Viewにて画面を生成
// ------------------------
$html = '<table>'
    . '<tr>'
    . '  <td>注文番号</td>'
    . '  <td>区分</td>'
    . '  <td>ステータス</td>'
    . '</tr>';

foreach ($orderList->lists as $key => $val) {
    // OrderStatusクラス(model)のメソッドを使って、statusを取得
    $status = OrderStatus::getStatus($val->order_number);

    $html .= '<tr>'
        . '<td>' . $val->order_number . '</td>' // 注文番号
        . '<td>' . $val->kubun . '</td>'        // 区分
        . '<td>' . $status . '</td>'            // ステータス
        . '</tr>';
}

$html .= '</table>';

長々となりましたが、「うちの開発ルールではこうしている」とか「気にしていない」など、ご教示いただけますと助かります。

以上、よろしくお願いいたします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • asahina1979

    2020/01/23 19:58

    あからさまに設計が誤ってるんだから設計通り作るか、
    指摘して仕様を治すかどっちか

    キャンセル

  • m.ts10806

    2020/01/23 21:08

    PHP限定にするより「オブジェクト指向」とか「アルゴリズム」とかをタグに入れた方が良いかと思います。

    キャンセル

  • XNXSXMXR

    2020/01/23 21:13

    asahina1979さん

    あからさまな設計の誤りというのは、基底としているオブジェクト変数「$orderList」に、欲しい情報がないことでしょうか?

    キャンセル

  • XNXSXMXR

    2020/01/23 21:14

    m.ts10806さん

    タグ追加いたしました。

    キャンセル

回答 4

+2

ループとDB問い合わせ(インデックスが正しく効いている前提)って速度への影響が桁違いに違うで、案2を取るメリットはほぼ無いと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/26 20:21

    ご回答ありがとうございます。
    いかにループ回数を減らすか、パズル感覚で攻略する感じが好きで意識はしておりましたが、皆さんご指摘のとおりDBへの問い合わせコストを意識しておりませんでした。

    キャンセル

+2

PHPは門外漢ですが、案2の見た目のループの回数よりDBアクセスの回数の方が全体のコストに影響しそうであるし、案1の方がMVCに沿っていてかつDBアクセスは1回で済むのですから、本件のようなものであれば個人的にはあまり迷うことなく案1を採用すると思います。本当にパフォーマンスに疑義が生じるのであれば、別途、プロファイリングを提案します。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/26 20:25

    ご回答ありがとうございます。
    コスト的にDBアクセスとループとでは比べるまでもない、という感じでしょうか。感覚ではなく計測しておけばよかったと思います。

    キャンセル

checkベストアンサー

+1

ループの回数を減らす目的は@XNXSXMXRさんの中ではっきりしていますでしょうか?
そこを示さないと、意にかなった回答を皆さん出来かねるかと思います

パフォーマンスの観点を重視するなら、一般に、DBに対して複数回の問い合わせが発生した場合と、プログラミング言語で同数のループが発生した場合、後者の方がパフォーマンスが良いため案1の方が良いです
それは例えるなら、同じビル内の部屋を訪れるのと、別の駅にあるビルの部屋を訪れるぐらいの差があります

マシンスペックにもよりますが、プログラミング言語のループ処理は数十万回程度であれば一瞬で終わります(稀に例外はあるものの)
グーグルなどで検索すればループ回数毎の処理時間の目安がヒットする(はず)ので、一度確認することをお勧めします

ViewがDB(のアクセスコード)に直接依存することの是非は結構意見が割れそうな気もしますが、
個人的には、変更の多い不安定なものが、変更の少ない安定したものに依存するのが良い、逆は悪い、というお約束を守って設計することをお勧めします

実際の開発において、ViewはViewの都合で、DBはDBの都合で、頻繁に変更が入ることが多いと思いますので、合間に緩衝材としてControllerを挟むのが無難かと思います

また、後々のメンテナンスのことを考えると、Viewのソース内にModelにあるようなコードが存在するのは見る人を混乱させる可能性が高いかな、と思いますし、テストの類もやり難くなりそうだな、とも思いました

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/26 20:31

    ご回答ありがとうございます。
    質問を投稿したあと、皆さんのご意見を読ませていただいたときに、憶測ではなく計測すればよかったかも、と思いました。
    また、ループ回数にばかり意識せず、もう少し考慮を広げます。

    指針についても「変更の少ない安定したものに依存するのが良い」というのをチーム内で共有させたいと思います。

    キャンセル

+1

ルールをどうされてるかじゃないでしょうか?
うちはMVCに準拠ってルールがあるので、ViewからModelは駄目ですね。
後からの改修にて、後者が理解し辛くなるからです。

個人的には、最短でレスポンスが得られるなら、例外は有りだと思いますが…。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/26 20:15

    回答ありがとうございます。まぁ確かにルールをどうするか、ですよね。
    NGにする理由がいまいち理解せずにルール化するのも・・・と悩んでおりましたが、みなさんの意見をうかがうことで腹落ちできそうです。

    キャンセル

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

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る