Laravel6ではフロントエンドの開発を行う際にvue.js、Reactのどちらを利用するか選択できるように事前に設定が行われています。本文書ではvue.jsではなくReactに注目してReact Routerの設定方法とReactとLaravelとの連携方法の基礎を確認します。

本文書を読み終えると下記のことを理解することができます。

  • Laravel環境におけるReactの初期設定
  • React Routerによるページリロードなしのページ遷移
  • Reactをフロントエンド、Laravelをバックエンドとして、Laravelから取得したデータをReactを経由してブラウザに表示

LaravelとVue Router、vue.jsを利用したシングルページアプリケーションの基礎については下記の文書で公開しています。

環境の構築

ReactとLaravelの連携を前提にしておりLaravelの知識がある程度あることを想定しているためインストールなどの詳細については説明を行なっていません。

Laravelのインストール

composerコマンドを利用してLaravelのインストールを行います。任意のディレクトリで実行してください。ここではlaravel_reactという名前のディレクトリ下にLaravelに関連するファイルが保存されます。


$ composer create-project --prefer-dist laravel/laravel laravel_react

インストール後、Laravelのバージョンを確認しておきます。実行はLaravelのインストールディレクトリのlaravel_reactの中でphp artisanコマンドを利用します。実行するとバージョンが6.16.0であることがわかります。


$ cd laravel_react
$ php artisan -V
Laravel Framework 6.16.0

Reactのインストール

Reactのインストールを行う前にlaravel/uiパッケージをインストールする必要があります。インストールはcomposerコマンドで行います。Laravelのインストールディレクトリで実行してください。


$ cd laravel_react
$ composer require laravel/ui

laravel/uiのパッケージのインストールが完了したら、Reactのインストールを行います。下記のコマンドを実行するとLaravelのログイン機能とReactをインストールすることができます。


 $ php artisan ui react --auth
React scaffolding installed successfully.
Please run "npm install && npm run dev" to compile your fresh scaffolding.
Authentication scaffolding generated successfully.
vue.jsを利用する場合はphp artisan ui vue –authを実行します。
fukidashi

インストールメッセージに表示されている通りにnpmコマンドを実行して、JavaScriptライブラリのインストールとコンパイルを行います。


$ npm install && npm run dev

ここまでの処理が完了するとユーザのログイン後に表示される画面のresource¥js¥components¥Example.jsファイルの中身がReactで記述されていることを確認することができます。


import React from 'react';
import ReactDOM from 'react-dom';

function Example() {
    return (
        <div className="container">
 //中略
        </div>
    );
}

export default Example;

if (document.getElementById('example')) {
    ReactDOM.render(<Example />, document.getElementById('example'));
}

Laravelの動作確認

Laravelのインストールが正常に行われたか確認するために開発サーバを起動してブラウザでアクセスします。


 $ php artisan serve
Laravel development server started: http://127.0.0.1:8000

ブラウザhttp://127.0.0.1:8000にアクセスして、下記の画面が表示されたらインストールは正常に行われています。ログイン機能もインストールしているので、右上にはLOGINとREGISTERのリンク文字列も表示されます。

login, Registerリンクが表示
login, Registerリンクが表示

Reactの設定

Hello React!の表示

Reactを利用して画面上にHello Reactを表示させるために既存のファイルを変更していきます。

http://127.0.0.1:8000にアクセスすると表示される内容はweb.phpファイル内のルーティング設定で指定されているwelocome.blade.phpに記述されています。


Route::get('/', function () {
    return view('welcome');
});

まずwelcome.blade.phpファイルを以下のように書き換えます。


<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app"></div>
</body>
</html>
Reactで設定する内容を表示させるためのidの値はappに設定しています。このidの値は後ほどApp.jsファイル内で利用します。
fukidashi

次にresource¥jsディレクトリのapp.jsファイルを以下のように書き換えます。


require('./bootstrap');

require('./components/App');
app.jsにはコメントが実際には記述されていますが、上部では省略しています。
fukidashi

最後に先程更新したapp.jsファイルでrequireしているcomponents/App.jsファイルを作成します。デフォルトではExample.jsが設定されています。


import React from 'react';
import ReactDOM from 'react-dom';

function App() {
    return <h1>Hello React!</h1>;
}

if (document.getElementById('app')) {
    ReactDOM.render(<App />, document.getElementById('app'));
}

ブラウザで確認するためにnpm run watchコマンドを実行しておきます。npm run watchコマンドを実行しておくとファイルを更新するその更新を検知し自動でコンパイルを実行してくれます。


 $ npm run watch

npm run watchコマンド実行後、ブラウザでアクセスを行うとHello React!が表示されます。Hello React!がブラウザ上に表示されたらReactの初期の動作確認は完了です。

Hello Reactの表示
Hello Reactの表示
ブラウザで動作確認を行う際は必ずphp artisan serveとnpm run watchコマンドを実行しておいてください。
fukidashi

React Routerの設定

Reactの初期設定を行なった結果、Hello Reactを表示することができました。アプリケーションを作成した場合は”/”ルートだけではなく/aboutへアクセスすると/aboutページの内容、/userへアクセスすると/userページといったようにURLによって異なる内容を表示させます。React上でURL毎に異なるページを表示させるためにはReact Routerの設定を行う必要があります。

react-router-domのインストール

React上でルーティングを行うためにreact-router-domライブラリのインストールを行います。インストールはLaravelのインストールディレクトリでnpmコマンドを利用して行います。


$ npm install react-router-dom
+ react-router-dom@5.1.2

ページとナビゲーションメニューの作成

今回はUserとAboutページを作成するので各ページの内容を記述するUserとAboutコンポーネントをresource¥js¥componentsディレクトリの下に作成します。


import React from 'react';

function User() {
    return <h1>Userページ</h1>;
}

export default User;

import React from 'react';

function About() {
    return <h1>Aboutページ</h1>;
}

export default About;

各ページの上部にリンクを表示させるためコンポーネントNavBarを作成します。NavBar.jsファイルはresource¥js¥componentsディレクトリの下作成します。NavBar.jsはUser.jsとAbout.jsの両方のコンポーネントから利用するコンポーネントです。


import React from 'react';

function NavBar() {
    return (
        <nav>
            <ul className="nav">
                <li>About</li>
                <li>User</li>
            </ul>
        </nav>
    )
}

export default NavBar;

3つのファイルが作成できたら、resource¥js¥componentsディレクトリの下にApp.jsファイルを作成した3つのコンポートをimportし、importしたコンポーネントをdivタグの中に追加します。

document.getElementByIdでappを指定していますが、このappはwelcome.blade.phpに記述しているdivタグのidのappに対応します。


import React from 'react';
import ReactDOM from 'react-dom';

import NavBar from './NavBar'
import About from './About'
import User from './User'

function App() {
    return (
    <div>
        <NavBar />
        <About />
        <User />
    </div>
    )
}

if (document.getElementById('app')) {
    ReactDOM.render(<App />, document.getElementById('app'));
}

作成が完了したらブラウザで更新した内容を確認します。App.jsで設定した通り、NavBar, About ,Userに記述した内容が順番に表示されます。

3つのコンポーネントを表示
3つのコンポーネントを表示

すべてのページが一つの画面に表示されるのではなく/aboutにアクセスしたらAboutページ、/userにアクセスしたらUserページのみ表示されるようにルーティングの設定を行なっていきます。

ルーティングの設定

App.jsファイルにインストールしたreact-router-domからBrowserRouter, Route, Switchをimportします。


import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
BrowserRouterでエイリアス(別名)をRouterにしていますがエイリアスの設定は必須ではありません。
fukidashi

先程追加したコンポーネントを使ってルーティングを行います。

Routerタグで全体をはさみます。Routeタグを使って各ページへのURLをpathで設定し、そのパスへのアクセスがあった場合に表示させたいコンポーネントをcomponentで設定します。NavBarはすべてのページで表示させたいのでRouteタグの設定は必要ありません。


function App() {
    return (
    <Router>
        <div>
            <NavBar />
            <Route path="/about" component={About} />
            <Route path="/user" component={User} />
        </div>
    </Router>
    )
}

先程とは異なり、設定を行なったあとでブラウザでアクセスするとNavBar以外は何も表示されません。

NavBarのみ表示
NavBarのみ表示

ルーティングに設定したパス/aboutへのアクセスを行います。しかし、404 Not Foundが表示され、Aboutページの内容は表示されません。

404 Not Foundページ
404 Not Foundページ

Laravelのルーティングの設定

Laravel側のルーティングweb.phpファイルでは”/”へのアクセスがあった場合のみwelcome.blade.phpが表示される設定となっているため、/aboutにアクセスしてもルーティングが設定されていないため404 NotFoundが表示されます。どのURLにアクセスが来てもwelcome.blade.phpファイルが表示されるように以下の設定に変更します。


Route::get('/{any}', function () {
    return view('welcome');
})->where('any','.*');

{any}を設定することで”/”(ルート)以下へのアクセスを行うとwelcome.blade.phpファイルが表示される設定を行っています。しかし{any}だけでは”/”(ルート)へのアクセスがあるとNot Foundエラーが発生します。そのため、where(‘any’,’.*’)も必要になります。

.*は正規表現で0文字以上の任意の文字列を意味します。0文字なので”/”(ルート)や”/”(ルート)以下のどの場所にアクセスしてもwelcome.blade.phpの中身が表示されます。
fukidashi

web.phpの設定後、/aboutや/userにアクセスすると各ページの内容が表示されます。React上でルーティングが行われています。

Aboutページの表示
Aboutページの表示

“/”ルートへのアクセスがあった場合に表示させるコンポーネントTop.jsファイルを追加します。


import React from 'react';

function Top() {
    return <h1>Topページ</h1>;
}

export default Top;

App.jsファイルにTopをimportして、ルーティングを追加します。これでルーティングが3つになりました。


import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import NavBar from './NavBar'
import About from './About'
import User from './User'
import Top from './Top'

function App() {
    return (
    <Router>
        <div>
            <NavBar />
            <Route path="/" component={Top} />
            <Route path="/about" component={About} />
            <Route path="/user" component={User} />
        </div>
    </Router>
    )
}

if (document.getElementById('app')) {
    ReactDOM.render(<App />, document.getElementById('app'));
}

ブラウザで”/”にアクセスするとTopページが表示されます。

Topページ表示
Topページ表示

しかし/aboutページにアクセスするとTopページの内容とAboutページの内容両方が表示されます。

Top、Aboutページが表示
Top、Aboutページが表示

Switchタグの設定

/aboutにアクセスすると”/”ルートと/aboutページが表示されることがわかりました。URLへのアクセスに対して一致する1つのコンポーネントのみ戻すためにSwitchタグを利用します。


function App() {
    return (
    <Router>
        <div>
            <NavBar />
            <Switch>
                <Route path="/" component={Top} />
                <Route path="/about" component={About} />
                <Route path="/user" component={User} />
            </Switch>
        </div>
    </Router>
    )
}

Switchを設定すると”/about”ページにアクセスすると”/”ルートが一致してしまうため、Topページが表示されます。Switchタグの中で上から順番にチェックを行い、/aboutへのアクセスだと”/”が一致するため処理が終了し下の/aboutの設定を確認することなくTopページが表示されることになります。

/aboutへのアクセスでTOPページが表示
/aboutへのアクセスでTOPページが表示

“/”(ルート)の場合は完全に”/”のみへのアクセスだけTopページを表示させるためにexactを設定します。

exactの設定


function App() {
    return (
    <Router>
        <div>
            <NavBar />
            <Switch>
                <Route path="/" exact component={Top} />
                <Route path="/about" component={About} />
                <Route path="/user" component={User} />
            </Switch>
        </div>
    </Router>
    )
}

これで各URLへのルーティングが正常に行われ、各URLに設定されたコンポーネントが表示されます。

リンクの設定

各ページにアクセスすることができたのでNavBarのリンク設定を行います。

react-router-domからLinkをimportして各ページへのリンクをLinkタグを利用して設定します。


import React from 'react';
import { Link } from 'react-router-dom';

function NavBar() {
    return (
        <nav>
            <ul className="nav">
                <Link to="/about">
                    <li>About</li>
                </Link>
                <Link to="/user">
                    <li>User</li>
                </Link>
            </ul>
        </nav>
    )
}

export default NavBar;

ブラウザでページを確認するとリンクのaタグが設定され色が変わっていることがわかります。About, Userをクリックするとページをリロードすることなくページの遷移が行われることが確認できます。

Linkタグの設定
Linkタグの設定

AboutUserの文字の間隔がほしい場合はbootstrapがrequireされている(app.js)ので利用することができます。ml-2で左側にmarginを設定しています。


<li className="ml-2">About</li>
Reactでclassを設定する場合はclassNameになります。
fukidashi
NavBarのliタグにCSS設定
NavBarのliタグにCSS設定

ここまでの設定で最も基本的なReactのルーティングの確認ができました。

データベースの設定

作成するアプリケーションのデータを保存するためにデータベースの作成を行います。簡易的に利用できるSQLiteデータベースを利用します。

SQLiteデータベースの作成

Laravelのインストールディレクトリでtouchコマンドを利用してSQLiteデータベースのファイルを作成します。


 $ touch database/database.sqlite

.envファイルでデータベースの設定を行います。デフォルトではMySQLの設定が行われていますが、データベースに関する設定はDB_CONNECTIONのみ残し、値はsqliteに変更します。


DB_CONNECTION=sqlite

#変更前の設定は下記
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

設定完了後、php artisan migrateコマンドを実行してテーブルの作成を行います。


 $ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0 seconds)

ダミーデータの作成

ダミーデータを作成するためにSeedingを利用します。Seedingを利用するとUserテーブルにダミデータを一括で挿入することができます。Seedingの詳しい内容を下記の文書を参考にしてください。

ここからUserテーブルに50件のダミデータを挿入する設定をおこなっていきます。Seedingを理解していなくても下記の流れに沿って行なっていけばダミデータを挿入することができます。

php artisan make:seederコマンドでUsersTableSeederファイルを作成します。


 $ php artisan make:seeder UsersTableSeeder
Seeder created successfully.

実行するとdatabase¥seedsディレクトリの下にUsersTableSeeder.phpファイルが作成されます。50件のダミーデータを作成するので以下を設定してください。50の数字を変更するとダミデータの数が変わります。


use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $users = factory(App\User::class, 50)->create();
    }
}

ダミーデータに入る値の設定はUserFactory.phpで行います。UserFactory.phpはdatabase/factoriesの下に事前に作成されています。今回は初期設定のままで更新を行わないので何もする必要がありません。

次にdatabase/seedsの下にあるDatabaseSeeder.phpファイルのrunのコメントアウトを外します。


    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }

設定は完了したので、php artisan db:seedを実行してください。


 $ php artisan db:seed
Seeding: UsersTableSeeder
Seeded:  UsersTableSeeder (0.32 seconds)
Database seeding completed successfully.

usersテーブルへのデミーデータの作成は完了です。

APIによるデータ取得

React上からAPIを利用してデータを取得し、Userコンポーネントに取得したデータを表示させてみましょう。UserコンポーネントのUser.jsファイルはresource¥js¥componentsディレクトリの下に作成済みです。

データのリスト表示

APIによるデータの取得の前にオブジェクトのデータを利用してUserコンポーネントにユーザの一覧を表示してみましょう。Laravelではデータをリスト表示する場合にforeach、vue.jsではv-forなどを利用して描写しますが、Reactではmap関数を利用します。


import React from 'react';

function User() {

    const users = [
        {
            id: 1,
            name: 'John',
            email: 'john@example.com'
        },
        {
            id: 2,
            name: 'Kevin',
            email: 'kevin@test.com'
        },
        {
            id: 3,
            name: 'Joshua',
            email: 'joshua@example.com'
        }
    ]

    return (
        <div>
            <h1>Userページ</h1>
            <ul>
                {users.map((user) => <li key={user.id}>{user.name}</li>)}
            </ul>
        </div>
    );
}

export default User;

ブラウザで確認するとリスト表示されていることがわかります。

ユーザ情報のリスト表示
ユーザ情報のリスト表示

データベースからのデータ取得

axiosを利用して、usersテーブルからユーザ情報の取得を行います。

usersテーブルへのデータの挿入は完了しているので、Laravelのapi.phpファイルにユーザ情報を取得するためのルーティングを設定します。


Route::get('/user',function (Request $request) {
	
	$users = App\User::all();
	
	return response()->json(['users' => $users]);

});

設定が完了すると/api/userにアクセスするとユーザ一覧を取得することができます。

User.jsファイルに/api/userからユーザ一覧を取得するためのコードを記述します。


import React, { useEffect, useState } from 'react';
import axios from 'axios';

function User() {

    const [users, setUsers] = useState([]);

    useEffect(() => {
        getUsers()
    },[])

    const getUsers = async () => {
        const response = await axios.get('/api/user');
        setUsers(response.data.users)
    }

    return (
        <div>
            <h1>Userページ</h1>
            <ul>
                {users.map((user) => <li key="{user.id}">{user.name}</li>)}
            </ul>
        </div>
    );
}

export default User;

Laravelではaxiosはインストール済みなのでaxiosをimportして利用します。

useStateで値を保持して、useEffectでコンポーネント描写時のみLaravelからユーザ一覧の情報を取得します。

usersの初期値は空の配列を設定し、useEffectによりコンポーネントが描写される時にgetUsers()が実行されます。getUsersではaxiosを利用して/api/userからユーザ一覧を取得し、setUsersを使ってusersに取得したデータを保存しています。

ブラウザで/userにアクセスすると取得したユーザ一覧が表示されます。

Laravelから取得したユーザデータを表示
Laravelから取得したユーザデータを表示

詳細ページの表示

これまではuserやaboutページなので固定ページのルーティングの設定を行なってきましたが、ここではユーザ情報の詳細情報を表示するページへのルーティング設定を行なっていきます。

詳細ページへのルーティングは/user/1, /user/2…のようにユーザのIDによって変わるため固定ページのルーティングとは異なる設定が必要になります。

User.jsファイルにLinkをインポート、各userの行にLinkタグを設定しリンク先は`/user/${user.id}`とします。

`/user/${user.id}`ではなく{/user/ + user.id}と記述することもできます。
fukidashi

import { Link } from 'react-router-dom';

<ul>
    {users.map((user) => 
    <li key={user.id}>
        {user.name}
        <Link to={`/user/${user.id}`}>
            詳細
        </Link>
    </li>)}
</ul>

Linkの設定により詳細にリンクが貼られます。各リンクにはURLが/user/1, /user/2と設定されますがクリックしても画面には変化はありません。

詳細ボタンを表示
詳細ボタンを表示

詳細ページのコンポーネントの作成とApp.jsへの/user/{id}へのリンクの設定を行います。詳細ページはresources¥js¥componentsの下にUserDetail.jsという名前で保存します。


import React from 'react';

function UserDetail() {
    return (
        <div>
            <h1>User詳細ページ</h1>
        </div>
    );
}

export default UserDetail;

App.jsファイルではUserDetail.jsファイルをimportします。


import UserDetail from './UserDetail'

Switchタグの中に新たにRouteタグを追加し、pathとcomponentを設定します。pathはidが動的に変更するので:idとします。/user/1, /user/2でアクセスがあった場合/userのURLが先に一致されるためUserコンポーネントが表示されます。それを回避するために/userのRouteタグにexactを設定します。


<Switch>
    <Route path="/" exact component={Top} />
    <Route path="/about" component={About} />
    <Route path="/user" exact component={User} />
    <Route path="/user/:id" component={UserDetail} />
</Switch>

ここまでの設定で再度詳細をクリックすると以下の画面が表示されます。

ユーザ詳細ページ表示
ユーザ詳細ページ表示

詳細ページにユーザ情報を表示させるためには、userのidを利用して再度Laravelへアクセスを行い情報を取得する必要があります。

idにより個別のユーザデータが取得できるようにapi.phpファイルに新たにルーティングを追加します。


Route::get('/user/{user}', function(App\User $user){

	return response()->json(['user' => $user]);

});

UserDetail.jsにはUser.jsでのデータ取得のコードを参考に以下を追加します。


import React, { useEffect, useState } from 'react';
import axios from 'axios';

function UserDetail(props) {

    const [user, setUser] = useState([]);

    useEffect(() => {
        getUser()
    },[])

    const getUser = async () => {
        // console.log(props.match)
        const response = await axios.get(`/api/user/${props.match.params.id}`);
        setUser(response.data.user)
    }
    return (
        <div>
            <h1>User詳細ページ</h1>
            <p>{user.id}</p>
            <p>{user.name}</p>
            <p>{user.email}</p>
        </div>
    );
}

export default UserDetail;

userのidはpropsのmatchの中に保存されています。console.log(props.match)で一度どのような値が入っているかを確認してください。

props.match.params.idでuserのidを取得することができます。この値とaxiosを設定してLaravelからユーザの個別情報を取得します。

ブラウザでアクセスするとユーザの詳細情報を表示することができます。

ユーザの詳細画面
ユーザの詳細画面

ここまでの設定でReactをフロントエンド、Laravelをバックエンドとして、Laravelから取得したデータをReactを経由してブラウザに表示することができました。