カテゴリー: Laravel

  • MVC とは?を理解するために簡単なアプリをつくる with Laravel

    MVC とは?を理解するために簡単なアプリをつくる with Laravel

    ❓ これは何

    ソフトウェアを構築するには、様々な設計モデルがあります。

    今回はバックエンドの開発で基礎的で代表的なMVCモデルを少しわかったつもりになるために、
    簡単なメモアプリを作ります。

    この記事では Laravel を使っています。
    まだ Hello World ができていない方は、先に済ませておきましょう。
    M1 mac でLaravel のHelloWorldする方法

    このページでの🐏はコラムです。読み飛ばしてOKです。

    今回完成すると次のようなものになります。
    この記事ではスタイリングなどの解説はしていません。
    1時間内でとりあえず作ることを目標にしています。

    🪴 環境

    この記事では Apple Silicon に合わせた書き方になっている箇所があります。(Docker関係)
    ご注意ください。

    • Apple m1 MacBook Air
    • 14.5(23F79)

    🔧 やり方

    MVC(Model-View-Controller)は、ソフトウェア設計の基本原則の一つです。

    アプリケーションの構造を

    • Model
    • View
    • Controller

    の3つの部分に分けることで、保守性と可読性を向上させます。

    🐳 初期設定

    ここではApple m1 での設定となっています。
    主にDockerに関する操作についてです。

    docker-compose.yml は次のようにしました。

    version: '3.8'
    services:
      app:
        build:
          context: .
          dockerfile: Dockerfile
        image: laravel-app
        container_name: laravel-app
        restart: unless-stopped
        working_dir: /var/www
        volumes:
          - .:/var/www
        networks:
          - laravel
        environment:
          - DB_CONNECTION=mysql
          - DB_HOST=db
          - DB_PORT=3306
          - DB_DATABASE=your_database_name
          - DB_USERNAME=your_database_user
          - DB_PASSWORD=your_database_password
    
      webserver:
        image: arm64v8/nginx:alpine
        container_name: webserver
        restart: unless-stopped
        ports:
          - "8000:80"
        volumes:
          - .:/var/www
          - ./nginx.conf:/etc/nginx/conf.d/default.conf
        networks:
          - laravel
    
      db:
        image: arm64v8/mariadb:10.5
        container_name: db
        restart: unless-stopped
        environment:
          - MYSQL_ROOT_PASSWORD=root_password
          - MYSQL_DATABASE=your_database_name
          - MYSQL_USER=your_database_user
          - MYSQL_PASSWORD=your_database_password
        volumes:
          - dbdata:/var/lib/mysql
        ports:
          - "3306:3306"
        networks:
          - laravel
    
    volumes:
      dbdata:
    
    networks:
      laravel:
        driver: bridge

    .env ファイルに次のコードを追記します。

    DB_CONNECTION=mysql
    DB_HOST=mysql
    DB_PORT=3306
    DB_DATABASE=your_database_name
    DB_USERNAME=your_database_user
    DB_PASSWORD=your_database_password

    コンテナを起動し、コンテナにアクセスします。

    docker-compose up -d
    docker-compose exec app bash

    migrate したら exit します。

    php artisan migrate

    マイグレーションファイルの作成

    Laravelでは、マイグレーションファイルを使ってデータベーススキーマ(スキーマとは簡単にいうと構造のこと)の変更を定義します。
    マイグレーションファイルは、PHPコードでデータベースのテーブルやカラムの追加、削除、変更を記述します。

    database/migrationsディレクトリに新しいマイグレーションファイルを生成します。

    notes というテーブルが作成されます。

    実行後、database/migrations/ホゲホゲcreate_notes_table.php というファイルが作成されていると思います。(ホゲホゲには日付などが入っているはず)

    php artisan make:migration create_notes_table --create=notes

    🐏 php artisan とは?

    • command: 実行するコマンド(例: make:migration, serve, migrateなど)
    • options: オプションとして指定するもの(例: –force, –quietなど)
    • arguments: コマンドに渡す引数(例: マイグレーションの名前など)
    php artisan [command] [options] [arguments]

    create_notes_table.php

    function up()の中身を次のように追記します。

    upメソッド
    マイグレーションを適用する際に実行されます。
    例では、notesテーブルを作成し、
    id, title, content, timestamps
    のカラムを定義しています。

    downメソッド
    マイグレーションをロールバックする際に実行されます。
    例では、notesテーブルを削除しています。
    ロールバックとはデータベースのマイグレーションを元に戻す操作を指します。具体的には、適用されたマイグレーションを取り消し、以前の状態に戻すことです。

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    return new class extends Migration
    {
        /**
         * Run the migrations.
         */
        public function up(): void
        {
            Schema::create('notes', function (Blueprint $table) {
                $table->id();
                $table->string('title');
                $table->text('content');
                $table->timestamps();
            });
        }
    
        /**
         * Reverse the migrations.
         */
        public function down(): void
        {
            Schema::dropIfExists('notes');
        }
    };
    

    マイグレーションを実行します。
    このコマンドで上記コードのupが実行されることになります。。

    php artisan migrate

    📦 MODEL

    メモモデルの作成

    モデルはデータベースとのやり取りや、データの検証の実装を担当します。

    メモアプリケーションでは、Noteモデルがこれに該当します。Noteモデルはメモのデータ(タイトルと内容)を管理します

    メモモデルの作成

    php artisan make:model Note

    modelsの中の Note.php に次のコードを記述します。

    use HasFactory;
    Laravelのファクトリ機能を使用します。

    protected $fillable = [‘title’, ‘content’];:
    マスアサインメントを許可する属性を指定します。

    マスアサインメント(Mass Assignment)とは、配列やオブジェクトからモデルの複数の属性に一括で値を割り当てることです。

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class Note extends Model
    {
        use HasFactory;
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = [
            'title',
            'content',
        ];
    }

    🐏 use HasFactory; とは?

    ファクトリは、テストデータを作成するクラスであるFactoryを使用して、データベーステーブルにランダムなデータを生成する機能です。

    🎮 CONTROLLER

    controller の作成

    コントローラーはユーザーの入力に応じてモデルとビューを調整します。
    リクエストを受け取り、適切なモデルとビューを呼び出してレスポンスを返します。

    メモアプリケーションでは、NoteControllerがコントローラーに該当します。
    NoteControllerはメモの作成、表示、編集、削除のロジックを担当します。

    メモコントローラの作成

    php artisan make:controller NoteController --resource

    app/Http/Controllers/NoteController.php に次のコードを追加します。

    各メソッドは、特定のアクション(一覧表示、作成、保存、表示、編集、更新、削除)を担当します。

    リクエストを処理し、適切なビューを返します。

    $request->validate([
           'title' => 'required',  // タイトルは必須
           'content' => 'required', // 内容も必須
    ]);

    こういった書き方で、バリデーションを実装できます。

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Models\Note;
    use Illuminate\Http\Request;
    
    class NoteController extends Controller
    {
        // メモの一覧を表示するメソッド
        public function index()
        {
            // データベースからすべてのメモを取得
            $notes = Note::all();
            
            // 'notes.index'ビューを表示し、取得したメモをビューに渡す
            return view('notes.index', compact('notes'));
        }
    
        // 新しいメモを作成するフォームを表示するメソッド
        public function create()
        {
            // 'notes.create'ビューを表示
            return view('notes.create');
        }
    
        // 新しいメモを保存するメソッド
        public function store(Request $request)
        {
            // リクエストデータをバリデート
            $request->validate([
                'title' => 'required',  // タイトルは必須
                'content' => 'required', // 内容も必須
            ]);
    
            // リクエストデータを使って新しいメモを作成
            Note::create($request->all());
    
            // メモの一覧ページにリダイレクトし、成功メッセージを表示
            return redirect()->route('notes.index')
                            ->with('success', 'Note created successfully.');
        }
    
        // 特定のメモを表示するメソッド
        public function show(Note $note)
        {
            // 'notes.show'ビューを表示し、特定のメモをビューに渡す
            return view('notes.show', compact('note'));
        }
    
        // 特定のメモを編集するフォームを表示するメソッド
        public function edit(Note $note)
        {
            // 'notes.edit'ビューを表示し、特定のメモをビューに渡す
            return view('notes.edit', compact('note'));
        }
    
        // 特定のメモを更新するメソッド
        public function update(Request $request, Note $note)
        {
            // リクエストデータをバリデート
            $request->validate([
                'title' => 'required',  // タイトルは必須
                'content' => 'required', // 内容も必須
            ]);
    
            // 特定のメモを更新
            $note->update($request->all());
    
            // メモの一覧ページにリダイレクトし、成功メッセージを表示
            return redirect()->route('notes.index')
                            ->with('success', 'Note updated successfully');
        }
    
        // 特定のメモを削除するメソッド
        public function destroy(Note $note)
        {
            // 特定のメモを削除
            $note->delete();
    
            // メモの一覧ページにリダイレクトし、成功メッセージを表示
            return redirect()->route('notes.index')
                            ->with('success', 'Note deleted successfully');
        }
    }

    ルートの設定

    ルートの設定

    routes/web.php に次のコードを追記します。

    <?php
    
    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\NoteController;
    
    Route::get('/', [NoteController::class, 'index']);
    
    Route::resource('notes', NoteController::class);

    👁️ VIEW

    Viewの作成

    ビューはユーザーインターフェースを表示します。
    データの表示やユーザーの入力を受け付けます。

    メモアプリケーションでは、index.blade.php、create.blade.php、edit.blade.php、show.blade.phpがビューに該当します。
    これらのビューは、メモのリスト表示、作成フォーム、編集フォーム、詳細表示を担当します。

    まずは resources/views に layout.blade.php を作ります。

    共通レイアウトを定義し、各ビューで使用します。
    ヘッダーやフッターなど、どのページでも使うものもここに書いておくと、このファイルの役割がわかりやすいでしょう。

    <!DOCTYPE html>
    <html>
    <head>
        <title>Memo App</title>
    </head>
    <body>
        @yield('content')
    </body>
    </html>

    Resources/views フォルダに notes というディレクトリを作り、下記ファイルを作成します。

    • index.blade.php
    • create.blade.php
    • edit.blade.php
    • show.blade.php

    index.blade.php

    メモの一覧を表示し、各メモに対して詳細表示、編集、削除のリンクがあります。

    @extends('layout')
    
    @section('content')
        <h1>Notes</h1>
        <a href="{{ route('notes.create') }}">Create a new note</a>
        <ul>
            @foreach ($notes as $note)
                <li>
                    <a href="{{ route('notes.show', $note->id) }}">{{ $note->title }}</a>
                    <a href="{{ route('notes.edit', $note->id) }}">Edit</a>
                    <form action="{{ route('notes.destroy', $note->id) }}" method="POST">
                        @csrf
                        @method('DELETE')
                        <button type="submit">Delete</button>
                    </form>
                </li>
            @endforeach
        </ul>
    @endsection

    🐏 hoge.blade.php とは?

    hoge.blade.php とかくと、Laravelが提供するテンプレートエンジンの「Blade」が使用できます。
    BladeはPHPのテンプレートエンジンで、HTML, PHP をより見やすく、書きやすくすることが出来ます。

    • @extends(‘layout’)は、layout.blade.phpを継承することを示しています。
    • @section(‘content’)と@endsectionの間に、レイアウトに挿入するコンテンツを記述します。
    • @foreachループを使って、$notesコレクションの各メモを表示します。
    • @csrfはCSRFトークンを生成し、フォームの送信を保護します。
    • @method(‘DELETE’)はHTMLフォームでDELETEメソッドを使うためのBladeディレクティブです。

    🐏 CSRFトークンとは?

    CSRF(Cross-Site Request Forgery、クロスサイトリクエストフォージェリ)は、
    ユーザーが意図しないリクエストをWebアプリケーションに送信する攻撃です。
    CSRFトークンは、この攻撃を防ぐために使用されるセキュリティ機構です。

    基本的にPOSTの時には @csrf と書いておけばいいでしょう()

    create.blade.php

    メモの作成フォームです。

    @extends('layout')
    
    @section('content')
        <h1>Create Note</h1>
        <form action="{{ route('notes.store') }}" method="POST">
            @csrf
            <label>Title:</label>
            <input type="text" name="title">
            <label>Content:</label>
            <textarea name="content"></textarea>
            <button type="submit">Create</button>
        </form>
    @endsection

    edit.blade.php

    メモの編集フォームです。

    @extends('layout')
    
    @section('content')
        <h1>Edit Note</h1>
        <form action="{{ route('notes.update', $note->id) }}" method="POST">
            @csrf
            @method('PUT')
            <label>Title:</label>
            <input type="text" name="title" value="{{ $note->title }}">
            <label>Content:</label>
            <textarea name="content">{{ $note->content }}</textarea>
            <button type="submit">Update</button>
        </form>
    @endsection

    show.blade.php

    メモの詳細画面です。

    @extends('layout')
    
    @section('content')
        <h1>{{ $note->title }}</h1>
        <p>{{ $note->content }}</p>
        <a href="{{ route('notes.index') }}">Back to Notes</a>
    @endsection

    これで完成です!
    実行し、確認してみましょう。

Home
About
Blog
Works
Contact