1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[KPI] laravel + react HP

Last updated at Posted at 2020-09-13

おおまかな流れ

1、laravelでreactが使えるようにターミナルから必要なものをインストールする。
2、ルーティングを設定する
3、予約機能の作成

laravelでreactを使えるようにする

ターミナルで以下のコマンド群を実行する

まずは、laravelのuiパッケージをインストールする
composer require laravel/ui

次に、reactを使えるようにする
php artisan ui react

最後にnpmをインストールし、自動的にファイルの変更を検知してコンパイルをしてもらう
npm install && npm run watch-poll

reactで開発できるようにするため、laravelのファイル群を変更する

一番最初にリクエストを投げた時にレスポンスで返ってくるbladeファイルを作成する。
下記のコードにある<div id="index"></div>の部分に注目する。
ここ部分にreactで作成する箇所が埋め込まれるイメージ。

top.blade.php
<body>
    <div id="index"></div>
</body>

次は、下記のコードにある

if (document.getElementById('index')) {
    ReactDOM.render(<Index />, document.getElementById('index'));
}```
の部分に注目する。
先ほど設定したid・indexの部分にこのIndexコンポーネントを埋め込む

```Index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Top from './Top';
import Greetings from './Greetings';
import Profile from './Profile';
import Fee from './Fee';
import Contact from './Contact';
import Access from './Access';
import Reservation from './Reservation';
import '../../../public/css/index.css';

export default class Index extends Component {
    render() {
        return (
            <div>
                    <BrowserRouter>
                        <Switch>
                            <Route exact path="/" component={Top} />
                            <Route exact path="/greetings" component={Greetings} />
                            <Route exact path="/profile" component={Profile} />
                            <Route exact path="/reservation" component={Reservation} />
                            <Route exact path="/fee" component={Fee} />
                            <Route exact path="/contact" component={Contact} />
                            <Route exact path="/access" component={Access} />
                        </Switch>
                    </BrowserRouter>
            </div>
        );
    }
}

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

SPAを作成するためにreact-router-domをインストールして設定する

ターミナルでnpm install react-router-domを実行すると、package.jsonにインストールしたモジュールが記述される。
これでモジュールを使用する準備が整った。

まずは、BrowserRouter、Route、Switchモジュールをimportする。
次に、BrowserRouter、Route、Switchの順に入れ子にする。

<Route path="/" component={Top} />の解説をする。
/にアクセスがあった場合に、Topコンポーネントを表示させるということである。

ここに遷移させるためのアンカー部分は、Linkモジュールを使用する。下記に例を示す。
Linkモジュールはレンダーされるとaタグになる。このLinkモジュールで生成したアンカーをクリックすると、
上記で設定した<Route path="/" component={Top} />の通り、Topコンポーネントが表示される。

ルーティングはこれで完成。

Header.js
<Link to="/"</Link>
Index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Top from './Top';

export default class Index extends Component {
    render() {
        return (
            <div>
                    <BrowserRouter>
                        <Switch>
                            <Route path="/" component={Top} />
                        </Switch>
                    </BrowserRouter>
            </div>
        );
    }
}

予約機能の作成

コードが長くてみにくいので必要な部分をピックアップして解説していく。

下記のコードにあるconstructor内では、stateを定義したり関数をバインドする。
これをしないとstate・関数が使えない。

        super(props)
this.state = {
            date: "",
}
this.onDateChange = this.onDateChange.bind(this);
}```

下記のコードは、stateであるdateにフォームで入力された値を格納している。
```onDateChange(e) {
        this.setState({ date: e.target.value })
}```

下記のコードのdisabled={this.state.isDisabled}はisDisabledのstateがtrueの場合はsubmitできないようにしている。
onClick={this.postReservation}は、submitした際にpostReervationメソッドが実行される。
postReservationメソッドについての説明はこの後に行う。
```<Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary">
                                送信する
                            </Button>

下記のコードの解説をする。
定数dataにフィールドに入力された値を格納する。
axiosを使用して非同期通信を行なっている。post送信で/reservationに対して先ほど定義した定数dataを送っている。
では。送信した先の/reservationを見ていく。

    const data = {
            date: this.state.date,
        }

            axios.post('/reservation', data)

先ほど解説した通りに非同期通信を行うと下記の処理が実行される。
先ほど定数dataで送った値が$requestに格納されている。
ReservationSendmailクラスをインスタンス化する際に$date渡す。

ReservationController.php
<?php

namespace App\Http\Controllers;

use App\Mail\ReservationSendmail;
use Illuminate\Support\Facades\Mail;
use App\Http\Requests\ReservationRequest;

class ReservationController extends Controller
{
    public function store(ReservationRequest $request)
    {
        $date = $request->date;
        $to = 'test@gmail.com';
        Mail::to($to)->send(new ReservationSendmail($date);

        return;
    }
}

次はReservationSendmailクラスをみる。

ReservationSendmail.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ReservationSendmail extends Mailable
{
    use Queueable, SerializesModels;

    private $date;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($date)
    {
        $this->date = $date; //ReservationControllerから送られてきた$dateをプロパティである$this->dateに格納している。
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this
            ->from('tatataabcd@gmail.com') //送信元のメールアドレス
            ->subject('自動送信メール') //メールのタイトル
            ->view('reservation.mail') //viewはreservation.mail.blade.phpを使用
            ->with([
                'date' => $this->date,  //先ほど格納したプロパティをreservation.mail.blade.phpでも使えるようにしている
            ]);
    }
}

次にreservation.mail.blade.phpを見る

mail.blade.php
お問い合わせ内容を受け付けました。<br>
<br>
■日時<br>
{!! $date !!}<br> この内容がReservationControllerのtoに指定したメールアドレスに送信される。
Reservation.js
import React, { Component } from 'react';
import moment from 'moment';
import Header from './Header';
import { Button } from '@material-ui/core';
import '../../../public/css/reservation.css';

class Reservation extends Component {
    constructor(props) {
        super(props)
        this.state = {
            date: "",
            name: "",
            phone: "",
            email: "",
            age: "",
            state: "",
            gender: "",
            isDisabled: false,
            errors: {
                date: [],
                name: [],
                phone: [],
                email: [],
                age: [],
                state: [],
                gender: [],
            }
        }
        this.onDateChange = this.onDateChange.bind(this);
        this.postReservation = this.postReservation.bind(this);
    }

    onDateChange(e) {
        this.setState({ date: e.target.value })
    }

    postReservation(e) {
        if (this.state.date !== "" && this.state.name !== "" && this.state.phone !== "" && this.state.email) {
            this.setState({
                isDisabled: true
            });
        }

        axios
            .post('/reservation', data)
            .then(response => {
                alert('予約を受け付けました。')
                this.setState({
                    isDisabled: false
                });

                this.setState({
                    errors: []
                });
            })
            .catch(error => {
                this.setState({
                    isDisabled: false
                });
                console.log(error.response.data.errors);
                const errors = this.state.errors;

                // 次の日から
                const nextDay = moment().add('1', 'd').format('YYYY-MM-DD');
                let reservationDay = this.state.date;
                reservationDay = reservationDay.slice(-16, -6);

                // 水木のみ
                const date = moment(this.state.date);
                const dayOfWeek = date.day();

                // 時間指定
                const hour = date.hour();
                const minute = date.minute();
                const hourAndMinute = hour + ':' + minute;

                if (this.state.date === "" ||
                    nextDay > reservationDay ||
                    dayOfWeek !== 3 &&
                    dayOfWeek !== 4 ||
                    hourAndMinute !== 10 + ':' + 0 &&
                    hourAndMinute !== 11 + ':' + 0 &&
                    hourAndMinute !== 13 + ':' + 0 &&
                    hourAndMinute !== 14 + ':' + 0 &&
                    hourAndMinute !== 15 + ':' + 0 &&
                    hourAndMinute !== 15 + ':' + 30 &&
                    hourAndMinute !== 16 + ':' + 0 &&
                    hourAndMinute !== 16 + ':' + 30
                ) {
                    errors.date = error.response.data.errors.date[0];
                } else {
                    errors.date = "";
                }
                this.setState({
                    errors: errors
                });

                if (this.state.name === "") {
                    errors.name = error.response.data.errors.name[0];
                } else {
                    errors.name = "";
                }
                this.setState({
                    errors: errors
                });

                if (this.state.phone === "" || isNaN(this.state.phone)) {
                    errors.phone = error.response.data.errors.phone[0];
                } else {
                    errors.phone = "";
                }
                this.setState({
                    errors: errors
                });

                if (this.state.email == "" || this.validateEmail(this.state.email)) {
                    errors.email = error.response.data.errors.email[0];
                } else {
                    errors.email = "";
                }
                this.setState({
                    errors: errors
                });
            });
    }

    render() {
        return (
            <React.Fragment>
                <Header />
                <div className="container">
                    <div className="row">
                        <div className="col-12">
                            <h1 class="h3 mb-5 mt-5 text-center">予約フォーム</h1>
                            <div className="form-group">
                                <span>予約日時</span>
                                <input className="form-control" type="datetime-local" name="date"
                                    value={this.state.date} onChange={this.onDateChange} />
                                <p className="err-msg">{this.state.errors.date}</p>
                            </div>

                          
                            <Button className="float-right" id="btn" variant="contained" disabled={this.state.isDisabled} onClick={this.postReservation} color="primary">
                                送信する
                            </Button>
                        </div>
                    </div>
                </div>
            </React.Fragment >
        );
    }
}

export default Reservation;

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?