対象
- MVCって何?という人
- MVCという言葉は知ってるけど,実装がいまいちわからない人
Laravelのバージョン
$ php artisan -V
Laravel Framework 9.26.1
MVCの導入
まずは,MVCをそもそも聞いたことがない人のために,導入としてMVCを簡単に説明したいと思います.
MVCとは,UI(ユーザーインタフェース)を持つソフトウェアに適用されるアーキテクチャ(構造)の一種で,M(モデル),V(ビュー),C(コントローラ)に処理を分割して,ソフトウェアの内部データをユーザーが直接参照・編集できる情報から分離する構造のことを言います.
わかりづらいですね・・・
ということで,事前にいくつかの疑問を解消したうえで,実装していこうと思います.
-
UIって何?
ユーザーが直接参照・編集できる部分のことです(Webページなど). -
MVCのそれぞれの役割は?
M(Model:モデル)
内部データ(サーバ内のデータ)にアクセスし,データの抽出・挿入などを行います.
V(View:ビュー)
渡されたデータをもとに,ユーザー側にUI(Webページなど)を提供します.
C(Controller:コントローラ)
モデルとビューに指示を送ります.
モデルへの指示:「どのデータにどのような操作をするのか」を指示する
ビューへの指示:「どのデータをどのビューへ渡し,表示するのか」を指示する -
なぜ役割を分割するの?
ソフトウェアの規模が大きくなっても,保守性と可読性を高く保つためです.ここについては実装を見ていくとわかってくると思います.
簡単な実装例を通して考える
ここからは実際に簡単な実装例を通して,MVCの流れを見ていきます.
以下のように,投稿(Post)のタイトル(title)と本文(body)をWebページに一覧表示させます.
title1
body1
title2
body2
title3
body3
...(省略)...
また,Postテーブルは以下のような構成になっています(詳細は省略しています).
id・・・・・・・・投稿ID(PK)
title・・・・・・ 投稿タイトル
body・・・・・・・投稿本文
crated_at・・・・ 作成日時
updated_at・・・・更新日時
ルーティング(web.php)
まずはルーティングから実装していきます.
ルーティングとは,UI(Webページ)で受け取ったリクエスト(URLなど)に対してどのような処理を行うかを定義する部分
のことです.
Laravelではweb.php
というファイルにその定義を記述します.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
Route::get('/index', [PostController::class, 'index']);
ここでRoute::get('/index', [PostController::class, 'index']);
は,上部でuse
しているRoute
を用いて受け取った,/index
というURLのget
メソッドのリクエスト(図中➀)に対して,上部でuse
しているPostController
クラス内の,index
メソッドを実行する(図中➁)ということを定義しています.
Controller
ということで,次は呼び出されるPostController
クラスと,その中にあるindex
メソッドを定義しましょう.
<?php
namespace App\Http\Controllers;
use App\Models\Post;
class PostController extends Controller
{
public function index(Post $post)
{
return view('posts/index')->with(['posts' => $post->getOrderBy()]);
}
}
こちらも一つずつ説明していきます.
まず,namespace
についてですが,なぜこれを記述する必要があると思いますか?
・・・
先ほどweb.php
内で,use App\Http\Controllers\PostController;
という記述があったと思います.これはApp\Http\Controllers
というディレクトリ構造の中にあるPostController
クラスをこのファイルで利用しますという宣言です.
ここで,人間であればエディターからディレクトリ構造を確認し,App\Http\Controllers
というディレクトリ構造の中にあるPostController.php
を簡単に見つけることができ,その中にPostController
クラスがあることを突き止めることが出来ます.
ただ,これと同じことをコンピュータにやらせようと思ったら,あなたならどうするでしょうか?
・・・
このアプローチの一つがnamespace
です.日本語だと名前空間とも呼ばれますが,ファイル内に自身のディレクトリの相対位置を記述しておくことで,コンピュータはファイルを見つけることが出来るようになります.
続いて,Post
クラスをuse
しています.これは後ほど実装するモデルに処理をお願いするための宣言です.
その後,Controller
クラスをextends
(継承)したPostController
クラスを定義します.このextends
をすることで,Laravelが用意してくれたController
クラスのメソッドを継承したうえでPostController
クラスを拡張していくことが出来ます.
ここからやっとindex
メソッドの説明に入ります.
まず引数に注目しましょう.(Post $post)
となっています.これは上部でuse
したPost
クラスを$post
にインスタンス化しています.
ここで,インスタンス化とは「実際にたい焼きをつくること」だと考えてください.
クラスとは「たい焼き」を作る型です.たい焼きそのものではないので,中にあんこをいれたり,カスタードを入れたりすることはできません.
実際に食べる(使用する)ためには,その型を使って「実際にたい焼きをつくること」が必要です.これがインスタンス化の意味するところになります.
ということで,返り値(return
)の部分で実際に使用していきます.
まずは,「どのビューを表示させるのか」を定義しています.今回の場合だと,/resources/views/posts
内にあるindex.blade.php
を表示させています(図中➂).
続いて,with
以降の部分で「どのデータを渡すのか」を定義しています.今回の場合だと,$post
のgetOrderBy
メソッド(図中➃)を使用した結果をposts
という名前をつけて,index.blade.php
に渡しています.
このgetOrderBy
メソッドとは何でしょう?
これは,次のモデルで定義することになります.
Model
ということで,index
メソッド内で呼び出されたgetOrderBy
メソッドを実装していきましょう.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function getOrderBy()
{
return $this->orderBy('updated_at', 'DESC')->get();
}
}
ここでの説明は,これまで説明してきたものと被る部分があるので,すこし省略して説明します.まず,namespace
を定義し,Model
クラスをuse
します.
そして,そのModel
クラスを継承した,Post
クラスを定義し,その中で,getOrderBy
メソッドを定義しています.
まず,$this
について説明したいと思います.これは「このメソッドが所属するクラス(親クラス)のインスタンス」を表します.
つまり,この場合はPostクラスのインスタンス,PostController
内で言うところの$post
にあたります.
そして,その中にある,orderBy
メソッドと,get
メソッドを順に使用しています(図中➄).
このorderBy
メソッドと,get
メソッドはModel
クラスに標準装備されているメソッドで,Post
クラスがModel
クラスをextends
しているため,使用することが出来ます.
ここで,orderBy('updated_at', 'DESC')
は$this
内のデータを更新日時(updated_at
)を基準に,降順(DESC
)に並び替えるメソッドです.
そして,get
メソッドはそのデータを抽出するメソッドです.
つまり,返り値の$this
には,更新日時を基準に,降順に並び替えられたPostモデル内のデータが全て格納されていることになります.
View
最後はindex
メソッドの返り値で指定されていたindex.blade.php
を実装していきましょう.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
...(省略)...
</head>
<body>
<div class='posts'>
@foreach ($posts as $post)
<div class='post'>
<h1 class='title'>{{ $post->title }}</h1>
<p class='body'>{{ $post->body }}</p>
</div>
@endforeach
</div>
</body>
</html>
ここで注目すべきはposts
というクラス名が付けられたdiv
タグの中身です.@foreach ($posts as $post)
の部分で,PostController
で渡したposts
を$posts
として使用しているのが分かると思います(※bladeファイル内では渡された名前(posts
)に$
をつけることで取り扱えるようになります).
$posts
はforeach
ディレクティブというBladeファイルの機能により$post
に一つずつ順番に格納され,post
というクラス名が付けられたdiv
タグの中身が$posts
内の投稿の個数分だけ繰り返されます(今回はメインのトピックではないのでこれ以上の説明は省略します).
ここまでの実装により,最初に示したWebページを表示することが出来ます.
まとめ(MVCとは)
以上の実装を通して,なぜこのようなアーキテクチャを採用しているかが,なんとなくわかっていただけたでしょうか?
この記事の実装はごく簡単なものでしたが,規模が大きくなるにつれて,コードの量は増えていきます.その際に,これまでのMVCの役割を全て一つのファイルが担っていたとしたらどうでしょう?
とても読みづらいし,メンテナンスなんて無理
ですよね.
また,ユーザーが簡単にサーバのデータにアクセスできてしまうと,セキュリティ面で危険
です.
以上のような理由から,UIを持つソフトウェア開発では,MVCの役割分担を守って実装していきましょう.
ただ,このMVC構造が最善の手段というわけではありません.人によってこだわりがあったり,そもそもMVC以外のアーキテクチャも存在します.また,今後よりよいアーキテクチャが開発される可能性もあります.
そのため,まずは基本を守ったうえで,様々な人・本・経験から学んで,自分自身で本当に良い実装というものを考え続けていく
のが最も大切だと思います.