Laravel QUEST #2【Laravelとデータベースを繋ぐ方法〜モデルの構築】

今回の講義では、Laravelとデータベースを繋ぐ方法〜モデルの構築まで行っていきます。

今回の講義内容が一通り実践できるようになると、下記のようなことができるようになります!

  • アプリケーションのデータ保存機能がつくれるようになる!
  • アプリケーションのユーザ登録機能の根幹部分が完成できる!
  • アプリケーションのサンプルユーザを一括して大量に増やすことができる!

つまり、アプリケーションの要である「ユーザ登録機能」について深く知ることができ、
実際のアプリのユーザ登録機構の一部を機能化することができるということです。

●今回のキーポイント

  • MySQLとLaravelの接続設定
  • 環境変数の理解
  • tinkerの操作を学ぶ
  • Modelの構築(Userモデル)
  • マイグレーションの実行
  • シーディングでユーザを一括登録

前回の内容はこちら

https://prog.quest-academia.com/laravel-quest-1/

MySQLとLaravelを接続しよう!

Laravelの使い方を学んでアプリケーションを動かすのが、この講座のメインなのですが


データベースがなければ、ユーザの名前などのアプリケーション上の情報を保存できません。

アプリケーション上で非常に重要となる、Laravelとデータベース(MySQL)の接続方法を学んでいきましょう!

データベース設定を確認しよう

今回のLaravelプロジェクトのconfigフォルダにある、database.phpというファイルに接続設定が記されています。

Laravel-quest > config > database.php

database.phpファイルに下記のような記述があることを確認して下さい。

database.php(一部抜粋)

'default' => env('DB_CONNECTION', 'mysql'),

このコードは、
「通常は、このようなデータベースを呼び出します」
という内容が書かれています。

例えば、

※サンプルコードにつき入力不要です

'default' => env(' ① ', ' ② '),

なら、
原則’①’という「環境変数(※後ほど解説)」を呼び出すけれども
もし’①’が設定されていなかったら、’②’が呼び出されるという仕組みです。

つまり、上記コードでは

「通常、’DB_CONNECTION’を呼び出しますが、
万一’DB_CONNECTION’が定義されていなかったら、
‘mysql’=MySQLというデータベースを呼び出しますよ」

という内容が書かれているということです。

なお、env()というのは、前述の「環境変数」を呼び出す関数です。
この「環境変数」について少し解説します。

環境変数って何?

環境変数とは、一言でいうと
「ユーザの環境によって変えることができる変数」
です。

上記のデータベース設定においても、「環境変数」を変更する事によって
ユーザが「自分の好きな(MySQL以外の)データベース」を設定することも可能になります。

更に、通常のプログラミングでは、プログラミングの停止と同時に変数は消えてしまうのですが、この環境変数は保存することができます。
そのため、毎回システムを利用するたびに
「利用するデータベースを指定しないといけない」ということにもなりません。

では、あなたのLaravelプロジェクトの環境変数はどこで決められているのでしょう?
環境変数を決めているのは、
プロジェクト内の「.env」ファイルになります。

.envファイルの編集

早速、env.ファイルを開いてみましょう。
プロジェクトの直下にあるファイルです。

Laravel-quest > .env

.env(一部抜粋)

DB_CONNECTION=mysql

ファイルの中を見ると、そのうちの1行におそらく DB_CONNECTION の記載があり、
上記のように最初から設定されていると思います。
記述が異なれば、上記のように値が「mysql」となるよう書き換えて下さい。

今回はデータベースをMySQLと設定して話を進めますが、
もしもMySQLでないデータベースを使いたい場合、.env ファイルの DB_CONNECTION=mysql の mysql 部分を使いたいデータベース名に変更することになります。

ここで、先ほどのdatabase.phpに戻ってMySQLの他の設定はどのようになっているか、見ていきましょう。

database.php(一部抜粋)

        'mysql' => [
            'driver'      => 'mysql',
            'host'        => env('DB_HOST', '127.0.0.1'),
            'port'        => env('DB_PORT', '3306'),
            'database'    => env('DB_DATABASE', 'forge'),
            'username'    => env('DB_USERNAME', 'forge'),
            'password'    => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset'     => 'utf8mb4',
            'collation'   => 'utf8mb4_unicode_ci',
            'prefix'      => '',
            'strict'      => true,
            'engine'      => null,
        ],

env.ファイルのDB_CONNECTION に’mysql’と値の記載がある場合、上記のコードが適用されます。

上記のコードのうち、

database.php(一部抜粋)

            'database'    => env('DB_DATABASE', 'forge'),
            'username'    => env('DB_USERNAME', 'forge'),
            'password'    => env('DB_PASSWORD', ''),

これら3行のコードがカギとなります。

それぞれ、env()という「環境変数」を呼び出す関数が当てられていますので、
それぞれに代入したい値を.envファイルに記載していきましょう。

初期設定では下記のようになっているかと思います。

.env(一部抜粋)

DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

これを、下記のように書き換えてみましょう。

DB_DATABASE=laravel-quest
DB_USERNAME=root
DB_PASSWORD=

このように書き換えると、

  • データベース名:laravel-quest
  • ユーザ名 :root
  • パスワード :(なし:パスワード入力なしでログイン可能)

という様に設定したことになります。

laravel-questデータベースをつくろう

では早速、作ったデータベースにログインしてみましょう!

まず、データベースサーバが起動しているかどうかを確認して、

ターミナル

$ sudo service mysqld status

データベースサーバがまだ起動していない場合は起動して下さい。

ターミナル

$ sudo service mysqld start

データベースサーバの自動起動設定をしておきましょう。

ターミナル

sudo chkconfig mysqld on

そして、下記コマンドでMySQLにログインします。
(rootは先ほど設定したユーザ名ですね)

ターミナル

$ mysql -u root

ログインできたら、下記の「データベース作成」コマンドを打ち込みましょう。

ターミナル

mysql> CREATE DATABASE `laravel-quest`;

「laravel-quest」の様に、ハイフン「-」 が含まれる文字列をデータベース名とする場合は、バッククオート「`」で前後を閉じないといけません。
(通常のアルファベット等の文字列のみをタイトルとする場合は、バッククオートで囲う必要はありません)

これで、LaravelとMySQLを接続するための準備完了です。
一旦、MySQLから下記コマンドでログアウトしましょう。

control(Ctrl)キー + Cキー を押す

tinkerとは?

ここまでできたかを確認するために、Laravelの対話型のPHP実行環境「tinker」で、
データベースの接続を確認してみましょう。

(Laravelのtinkerは、railsのconsoleに対応します。)

「tinker」は、このプロジェクトで今後よく使うことになりますので、
後々分かってくると思いますが、ここで簡単に解説します。

tinkerとは、ターミナル上にコマンドを打ち込むことで、

Laravelプロジェクトの「処理→実行→実行結果の表示」まで行ってくれる環境だと
大まかに捉えておいて下さい。

実際のウェブアプリ上で処理を実行しなくても、データベースにログインして直接操作しなくても
簡単に処理を行い、その結果の成否を確認することができます。

tinkerを利用するには、下記のコマンドを実行して下さい。

ターミナル

$ php artisan tinker

tinkerの環境に入ることができたら
tinkerを利用して、簡単なデータベース接続確認を行います。
下記コマンドを実行して下さい。

ターミナル

>>> DB::connection();

実行後、下記の様な表示となれば、データベースとの接続は成功です。

ターミナル

=> Illuminate\Database\MySqlConnection {#xxx}

データの保存時間を日本時間にしよう!

さらに、タイムゾーンを変更しましょう。
下記の通り、タイムゾーンを「東京」にすると、
データベースに記載されるレコードの保存時間などが日本時間で記録されます。

laravel-quest > config > app.php

app.php(一部抜粋)

    'timezone' => 'Asia/Tokyo',

モデルの構築

モデル(Model)とは、MVCにおける「データベース」関連の処理を担当する部分です。

例えば、今回のアプリケーションでは、主に下記の2種類のモデルが登場します。

  • ユーザ(利用者)モデル
  • ムービー(動画)モデル

今回構築するのはユーザーモデルとなります。

このアプリはいろいろなユーザさんが利用されることを想定して作りますので、
1人だけでなく、各ユーザの名前・メールアドレスなどをデータベース上に保存する必要があります。

そのユーザ情報を保存するに当たって肝心なのが、ユーザモデルです。
このユーザモデルがしっかり作れていないと、ユーザはアプリケーションの利用以前にログイン(ユーザ登録)できないということになりかねません。

早速、重要な項目であるモデル構築について、アプリケーションを作成しながらみていきましょう。

最大文字数の制限設定

モデル・テーブル作成前の準備として、下記のファイルを変更します。

laravel-quest > app > Providers > AppServiceProvider.php

AppServiceProvider.php(一部抜粋)

public function boot()
{
    \URL::forceScheme('https');  //←1章で追記したコード
}

上記のbootという関数の中身を以下のように追記しましょう。
データベースにMySQL(バージョンにもよる)を使う場合、文字列の最大文字数を191文字までに制限する必要があります。
(これがないと、次のマイグレーション実行時にエラーとなってしまいます)

AppServiceProvider.php(一部抜粋)

public function boot()
{
    \URL::forceScheme('https');
    \Schema::defaultStringLength(191);
}

マイグレーションでテーブル管理

上の項で、データベースとあなたのLaravelプロジェクトは連携が可能となりました。
しかし、データベースとあなたのプロジェクトを全く同じ構造で作成しないと、それぞれが上手く動作しなくなります。なので、この連携が非常に重要と言えます。

そこで登場するのが、マイグレーション機能です。
マイグレーション機能では、テーブルを新規に作成するためのマイグレーションファイルと呼ばれるファイルを作成してマイグレーションを実行することで、
マイグレーションファイルに書かれたテーブルの定義がMySQLなどのデータベースに間接的に反映されるのです。

また、マイグレーション機能を使ってテーブルを変更すれば、履歴がマイグレーションファイルとして積み上がっていくので、

「○日前の状態まで戻したい」

といった希望があった場合に、それを叶えることも可能なわけです。

ユーザテーブル設計のマイグレーション

早速マイグレーションを利用して、ユーザ(あなたのLaravelアプリケーションの利用者)を登録するためのテーブルを作っていきたいと思います。

ですが、Laravelプロジェクト開始と同時に、ユーザテーブル作成のためのマイグレーションファイルが既に準備されています。

これを利用すれば、ユーザを登録するためのテーブルが簡単に作成できるわけです。
では、users テーブルの中身をみていきましょう。
下記のファイルを探して、開いてみて下さい。

laravel-quest > database > migrations > 2014_10_12_000000_create_users_table.php

ちなみに、(データの作成日時)create(テーブル名)_table.php という命名ルールでマイグレーションファイルの名前が決められています。

2014_10_12_000000_create_users_table.php(一部抜粋)

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

上記コードを解説します。

ユーザテーブルには、最初からいくつかのカラムが与えられており
中でも下記の4つのカラムが重要です。

・id ID:ユーザに原則自動で割り振られる番号です。
・name 名前:ユーザの名前
・email Eメール:ユーザのメールアドレス
・password パスワード:ログイン時に利用できるパスワード

また、up() と down() 2つのメソッドが記載されているかと思います。

●upメソッド
up() はテーブルを生成するときに実行されるものです。

●downメソッド
down() は生成を戻すとき(例えば、php artisan migrate:resetなど)に実行されるものです。

つまり、Schema::dropIfExists(‘users’);というのは、
「もし’users’テーブルが存在したら、削除を実行する」
という意味です。

上記のマイグレーションファイルを実際に動かして、 usersテーブルをつくっていきましょう。

ターミナル

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

ユーザテーブルの実行と同時に実行される、create_password_resets_tableというのは
パスワードリセット用のマイグレーションファイルです。

Userモデルについて

Userモデルも、プロジェクトを作成した時点で自動で作られています。

laravel-quest > app > User.php

User.php(一部抜粋)

class User extends Authenticatable
{
    use Notifiable;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

ここで注目して欲しいのが、
「fillable」 「hidden」
という2つの配列です。

少し話が飛びますが、通常、Laravelプロジェクトからデータベースにレコードを保存する場合には、save()関数を利用します。

試しにtinkerにて、以下のようなsave()関数を利用して、ユーザを作成してみましょう。

作成前に、MySQLが起動しているかどうかを必ず確認するようにして下さい。

ターミナル

>>> use App\User
>>> $user=new User;
=> App\User {#xxx}
>>> $user->name='sample-name';
=> "sample-name"
>>> $user->email='sample@sample.com';
=> "sample@sample.com"
>>> $user->password=bcrypt('sample-password')
=> "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
>>> $user->save();
=> true
>>> $user;
=> App\User {#xxx
     id: 1,
     name: "sample-name",
     email: "sample@sample.com",
     updated_at: "2020-04-01 09:25:07",
     created_at: "2020-04-01 09:25:07",
   }

(注釈※bcrypt()は、暗号化する関数で、tinker上には結果として暗号化された値が表示されます)

このように、save()関数を利用するときは、インスタンスを作ってそのインスタンスにレコードを代入して保存するしかありません。
多くのレコードを保存したい場合、インスタンスを作るという作業は手間が掛かってしまいます。

ここで登場するのが、create()関数です。

create()はsave()と近く、データベースに情報をINSERTする役割があると同時に、
インスタンスを作成せずに多くのレコードを一括してデータベースに書き込むことができる利点があります。

ここで話が元に戻るのですが
$fillableに、「一括で保存したいパラメータ」を入れておきます。
(usersテーブルの場合、’name’, ‘email’, ‘password’の3つが既に$fillableに入っているかと思います)

User.php(一部抜粋)

protected $fillable = [
        'name', 'email', 'password',
    ];

すると、create()関数を使って、下記の様に一気に複数のレコードを保存できる様になります。

※サンプルコードにつき入力不要です

User::create([
            'name' => 'sample',
            'email' => 'sample@sample.com',
            'password' => bcrypt('sample'),
        ]);

また、hiddenについても少し触れておきます。

User.php(一部抜粋)

protected $hidden = [
        'password', 'remember_token',
    ];

この様に指定すると、パスワードなど、開発時においても原則表示すべきでないデータが隠されます。
以下のtinkerでのユーザ登録の際も、passwordは表示されていないのが分かると思います。

tinker上でcreate()関数を用いた、ユーザの登録をやってみましょう。

ターミナル

>>> use App\User
>>> User::create([
... 'name' => 'sample',
... 'email' => 'sample2@sample2.com',
... 'password' => bcrypt('sample') ])
>>> $user=User::find(2)
=> App\User {#xxx
     id: 2,
     name: "sample",
     email: "sample2@sample2.com",
     created_at: "2020-04-01 09:25:07",
     updated_at: "2020-04-01 09:25:07",
   }

$user=User::find(2)は、「2番目に登録されたUserのレコードを探して取得する」という意味です。
$user=User::find(2)で取得された値には、passwordは表示されていません。

次に、作ったデータを削除する方法を試してみましょう。
ユーザを代入した変数を宣言し、delete()すれば削除されます。

ターミナル

>>> $user->delete()
=> true
>>> $user=User::find(2)
=> null

$user=User::find(2)でもう一度確認すると、「null」となって
削除されたのが確認できると思います。

delete()は、一つ一つデータを消去する方法ですが、
作ったデータを「全て一度に削除」してしまいたい場合もあるかと思います。

そんな時には、tinker上で下記コマンドを実行してみてください。

ターミナル

>>> use App\User;
>>> User::truncate();

ユーザのレコードを全て削除するコードとなります。

もしくは、レコード消去には別の方法もあります。migrate:refresh のArtisanコマンドを使用する方法です。

上記と同じsampleデータで構いませんので、もう一度新規Userを作成し、
一旦、tinkerから下記コマンドで離脱しましょう。

control(Ctrl)キー + Cキー を押す

その後、ターミナルでArtisanコマンドを実行します。

ターミナル

$ php artisan migrate:refresh

上記いずれかのコマンド実行すると、データベースは初期化され、マイグレーションも1から実行されて、レコードは全て消えます。

上記いずれかのコマンド実行後、今一度tinkerに入って、
$user=User::find(2)で確認すると、作成したUserが全て消えていると思います。

なお、作成したいユーザを一度に全て表示したい場合は、tinkerで下記コマンドを打ち込めば、一括して全てのUserを表示できます。

ターミナル

>>> use App\User
>>> User::all()

シーディングで一括データ登録を行おう!

「1つ1つデータを登録するのは手前だし、一括でデータ登録をしたい」

ということもあると思います。

テストデータやダミーデータを自動生成するためのプログラムをシード、
それらのデータを自動生成する機能をシーディングと呼びます。

シーディングを行う方法を説明していきましょう。
ここでは、usersテーブルにシーディングにて新規ユーザを一括して作成していきます。
下記コマンドを実行してみましょう。

ターミナル

$ php artisan make:seeder UsersTableSeeder

実行すると下記のフォルダに UsersTableSeeder.php が生成されます。

laravel-quest > database > seeds > UsersTableSeeder.php

UsersTableSeeder.php(一部抜粋)

<?php
use Illuminate\Database\Seeder;
class MessagesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //
    }
}

run()にusersテーブルとして、レコードをinsertする処理を書き加えます。
試しに、5件一括して登録してみましょう。

UsersTableSeeder.php(一部抜粋)

<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->insert([
            'name' => 'sample1',
            'email' => 'sample1@sample.com',
            'password' => bcrypt('sample1')
        ]);
        DB::table('users')->insert([
            'name' => 'sample2',
            'email' => 'sample2@sample.com',
            'password' => bcrypt('sample2')
        ]);
        DB::table('users')->insert([
            'name' => 'sample3',
            'email' => 'sample3@sample.com',
            'password' => bcrypt('sample3')
        ]);
        DB::table('users')->insert([
            'name' => 'sample4',
            'email' => 'sample4@sample.com',
            'password' => bcrypt('sample4')
        ]);
        DB::table('users')->insert([
            'name' => 'sample5',
            'email' => 'sample5@sample.com',
            'password' => bcrypt('sample5')
        ]);
    }
}

またここで、下記ファイルを開きましょう。

laravel-quest > database > seeds >  DatabaseSeeder.php

DatabaseSeeder.phpの run()関数には、既に$this->call(UsersTableSeeder::class)がコメントとして記述されているかと思います。
丁寧にUsersTableSeederを実行する準備がなされています。
こちらのコメントアウトを解除してください。

DatabaseSeeder.php

<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
}

では、準備は完了しましたので、ユーザ情報の一括登録を実行していきましょう。

ターミナル

$ php artisan db:seed --class=UsersTableSeeder

こちらのコマンドが問題なく実行されれば、5名分のサンプルユーザ情報がテーブルに保存されているはずです。

tinkerに入って、レコードが保存されているか確認してみましょう。

ターミナル

>>> use App\User
>>> User::all()
=> Illuminate\Database\Eloquent\Collection {#2895
     all: [
       App\User {#xxx
         id: 1,
         name: "sample1",
         email: "sample1@sample.com",
         created_at: null,
         updated_at: null,
       },
       App\User {#xxx
         id: 2,
         name: "sample2",
         email: "sample2@sample.com",
         created_at: null,
         updated_at: null,
       },
       App\User {#xxx
         id: 3,
         name: "sample3",
         email: "sample3@sample.com",
         created_at: null,
         updated_at: null,
       },
       App\User {#xxx
         id: 4,
         name: "sample4",
         email: "sample4@sample.com",
         created_at: null,
         updated_at: null,
       },
       App\User {#xxx
         id: 5,
         name: "sample5",
         email: "sample5@sample.com",
         created_at: null,
         updated_at: null,
       },
     ],
   }

このように5件のユーザ情報が登録されていれば、実行完了です。

ちなみに、–class=UsersTableSeederは、当該シーダーを実行する場合のみに必要なコマンドです。
DatabaseSeederの中で記述したシーダーを全て一括で実行する場合は、記述しなくてOKです。

補足:エラーをクリアするためのコマンド

マイグレーションを実行する際に、下記のようなエラーが出たのではないでしょうか。

could not find driver (SQL: select * from information_schema.tables where table_schema = larave
 l-quest and table_name = migrations)

以下で、データベースを操作する仕組み:PDO (PHP Data Objects)のドライバがインストールされているかどうかを確認しましょう。

ちなみに、PDOを使うと統一された方法でデータベースにアクセスができるので、便利なのです。
さらにPDOを扱うには、各データベースに応じたドライバ(部品)をインストールしないといけません。
下記コマンドでドライバが存在するか確認できます。

ターミナル

php -m | grep pdo

上記を入力しても、pdo_mysql が表示されなければ、MySQLのドライバのインストールが必要です。

原因は、PHP7.2にバージョンをアップデートしたのに
PDOのドライバが PHP7.2バージョンに追いついていないからです。
以下のように、ドライバをインストールしましょう。

sudo yum -y install php72 php72-mbstring php72-pdo php72-mysqlnd

もう一度以下のコマンドを実行して、

php -m | grep pdo

pdo_mysql が表示されるのを確認できたら、マイグレーションが実行できるようになっているはずです。

最後に

いかがだったでしょうか?

データベースとの接続〜モデルの構築まで、今回はデータベースと関わる部分を重点的に学習しました。
データベースとの関わりが、PHP/Laravelなどのサーバサイド言語の肝といっても過言ではありません。

MVCの流れを理解する上でも、重要な項目ですので、特に「テーブル作成→レコード保存」の流れを繰り返し行ってみて下さい。

次回はこちら

https://prog.quest-academia.com/laravel-quest-3/