Laravelで簡単なメモアプリを作る（3）~データの更新と削除~

# はじめに
[Laravel](http://laravel.jp/)とは、Webに特化したPHPフレームワーク。セキュリティ対策(CSRF対策など)もこれでできます。MVCアーキテクチャを採用しています。今回は、データの更新と削除をしていきたいと思います。

[Laravelで簡単なメモアプリを作る（1）~Viewの作成と表示~](https://qiita.com/Alesion30/items/95222f62904ef46f6f45)
[Laravelで簡単なメモアプリを作る（2）~データの取得と保存~](https://qiita.com/Alesion30/items/97c3bd16c4e20d4bdb72)

# 今回の目標物
<img width="723" alt="スクリーンショット 2019-12-31 11.25.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/677f6e21-1da3-ad96-4ac5-116fb6e8c391.png">

<img width="703" alt="スクリーンショット 2019-12-31 11.25.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/88b43946-ed4d-5ab1-56c6-d95b4d8f011b.png">

# データの更新

### 更新するデータの取得
id=1のデータを取得する時は、このように書く。

```php
$memo = Memo::find(1);
```

whereメソッドでも、取得することができる。ただ、whereメソッドは、findメソッドと異なって、条件に合うものを全て取ってくるため、たとえ条件に合うものが一つであったとしても**配列**として取得するので注意。なのでfirstメソッドで、要素として取得する必要がある。

```php
$memo = Memo::where('id', 1)->first();
```

### データの更新
**update**メソッドで、データベース上のデータを書き換えることができる。

```php
$memo->update([
    'title' => $title,
    'content' => $content
]);
```

MemoControllerを以下のように編集する。

```app/Http/Controllers/MemoController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Memo;

class MemoController extends Controller
{
    public function showHome()
    {
        $memos = Memo::get();
        return view("home", ['memos' => $memos]);
    }

    public function showSubmit($id = 0)
    {
        if ($id != 0) {
            $memo = Memo::where('id', $id)->get()->first();
        } else {
            $memo = (object) ["id" => 0, "title" => "", "content" => ""];
        }
        return view("submit", ['memo' => $memo]);
    }

    public function postSubmit(Request $request, $id = 0)
    {
        $title = $request->input('title');
        $content = $request->input('content');
        if ($id == 0) {
            Memo::create([
                'title' => $title,
                'content' => $content
            ]);
        } else {
            $memo = Memo::find($id);
            $memo->update([
                'title' => $title,
                'content' => $content
            ]);
        }
        return redirect()->route('home');
    }
}
```

# データの削除

### メモを削除する関数の作成
**destroy**メソッドを使うと、引数に指定したidのデータだけが削除される。下のコードをMemoControllerに追加する。

```php
public function deleteMemo($id)
{
    Memo::destroy($id);
    return redirect()->route('home');
}
```

### web.phpにdeleteMemoメソッドの登録
`/delete/{id}`というURLを受け取ったら、メモが削除されるようにする。下のコードをweb.phpに追加する。

```php
Route::get('/delete/{id}', 'MemoController@deleteMemo')->name('delete');
```

### 削除ボタンを押したら削除されるようにする
resources/views/home.blade.phpの削除ボタンを以下のように修正する。

```html
<td><a href="{{ route('delete', ['id' => $memo->id])}}">削除</a></td>
```

# おまけ

### モーダルを使って、削除の確認をする
bootstrap4のモーダルを使用する。
https://getbootstrap.com/docs/4.3/components/modal/

```html:resources/views/home.blade.php
@extends('layouts.app')

@section('css')
<style>
    header {
        height: 50px;
        background-color: #000;
        color: white;
        padding-left: 20px;
        font-size: large;
        color: #ddd;
    }

    .title {
        position: absolute;
        top: 10px;
    }

    .card {
        margin-top: 40px;
    }

    .left {
        width: 70%;
    }

    .submit {
        position: absolute;
        top: 10px;
        right: 20px;
    }
</style>
@endsection

@section('content')
<div class="card" style="width: 100%;">
    <div class="card-header">
        メモ一覧
        <a href="{{ route('submit')}}" class="submit">メモを追加</a>
    </div>

    <table class="table">
        <tbody>
            @foreach ($memos as $memo)
            <tr>
                <td class="left">{{$memo->title}}</td>
                <td><a href="{{ route('submit', ['id' => $memo->id])}}">編集</a></td>
                <td><a type="button" data-toggle="modal" data-target="#modal{{$memo->id}}">削除</a></td>
            </tr>
            <!-- Modal -->
            <div class="modal fade" id="modal{{$memo->id}}" tabindex="-1" role="dialog" aria-hidden="true">
                <div class="modal-dialog" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">以下のメモを本当に削除しますか？</h5>
                            <button type="button" class="close" data-dismiss="modal">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            {{$memo->title}}
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">閉じる</button>
                            <button type="button" class="btn btn-primary"
                                onclick="location.href='{{ route('delete', ['id' => $memo->id])}}'">
                                削除
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            @endforeach
        </tbody>
    </table>
</div>
@endsection
```

### submitページのボタンを新規作成の時と編集の時で変える

新規作成の時は`$memo->id == 0`で、編集の時は`$memo->id != 0`なので、if文で条件分岐する。
resources/views/submit.blade.phpの追加ボタンを以下のように書き換える。

```html
@if($memo->id == 0)
<button type="submit" class="btn btn-success">追加</button>
@else
<button type="submit" class="btn btn-success">適用</button>
@endif
```

### テーブルの列をクリックしたら、詳細画面に飛べるようにする

下のコードを、app.blade.phpのbodyタグの一番下に追加する。

```javascript:resources/views/layouts/app.blade.php
<script>
    jQuery(function($) {
                $('tbody tr[data-href]').addClass('clickable').click(function() {
                    window.location = $(this).attr('data-href');
                }).find('a').hover(function() {
                    $(this).parents('tr').unbind('click');
                }, function() {
                    $(this).parents('tr').click( function() {
                        window.location = $(this).attr('data-href');
                    });
                });
            });
</script>
```

home.blade.phpの編集ボタンを消して、trタグにdata-href属性を追加する。

```html:resources/views/home.blade.php
<tr data-href="{{route('submit', ['id' => $memo->id])}}">
       <td class="left">{{$memo->title}}</td>
       <td><a type="button" data-toggle="modal" data-target="#modal{{$memo->id}}">削除</a></td>
</tr>
```

### テーブルの列をホバーすると色が変わるようにする

trタグに`class="table-row"`を追加する。

```html:resources/views/home.blade.php
<tr data-href="{{route('submit', ['id' => $memo->id])}}" class="table-row">
       <td class="left">{{$memo->title}}</td>
       <td><a type="button" data-toggle="modal" data-target="#modal{{$memo->id}}">削除</a></td>
</tr>
```

```css:resources/views/home.blade.php
.table-row {
    background-color: #fff;
}

.table-row:hover {
    background-color: #ddd;
}
```

### 追加ボタンをいい感じにする。ヘッダーも固定する。

app.blade.phpでFont Awesomeを読み込む。

```html:resources/views/layouts/app.blade.php
<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
```

app.blade.phpを以下のように編集する。

```html:resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MemoApp</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/css/bootstrap.min.css"
        integrity="sha384-SI27wrMjH3ZZ89r4o+fGIJtnzkAnFs3E4qz9DIYioCQ5l9Rd/7UAa8DHcaL8jkWt" crossorigin="anonymous">
    <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
        integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous">
    </script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous">
    </script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js"
        integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous">
    </script>
</head>

<style>
    header {
        background-color: #000;
        background-position: 0px 50px;
        padding-left: 20px;
        font-size: large;
        width: 100%;
        height: 50px;
        clear: both;
        position: fixed;
        top: 0;
        margin-top: 0px;
        z-index: 1;
    }

    .title {
        position: absolute;
        top: 10px;
        color: #fff;
    }

    .container {
        margin-top: 70px;
    }
</style>

@yield('css')

<body>
    <header>
        <span class="title">MemoApp</span>
    </header>

    <div class="container">
        @yield('content')
    </div>

    <script>
        jQuery(function($) {
                $('tbody tr[data-href]').addClass('clickable').click(function() {
                    window.location = $(this).attr('data-href');
                }).find('a').hover(function() {
                    $(this).parents('tr').unbind('click');
                }, function() {
                    $(this).parents('tr').click( function() {
                        window.location = $(this).attr('data-href');
                    });
                });
            });
    </script>
</body>

</html>
```

home.blade.phpを以下のように編集する。

```html:resources/views/home.blade.php
@extends('layouts.app')

@section('css')
<style>
    .table-row {
        background-color: #fff;
    }

    .table-row:hover {
        background-color: #ddd;
    }

    .card {
        margin-top: 40px;
    }

    .circle-button {
        position: fixed;
        display: inline-block;
        text-decoration: none;
        color: rgba(152, 152, 152, 0.43);
        width: 80px;
        height: 80px;
        line-height: 80px;
        font-size: 40px;
        border-radius: 50%;
        text-align: center;
        overflow: hidden;
        font-weight: bold;
        background-image: linear-gradient(#e8e8e8 0%, #d6d6d6 100%);
        text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.66);
        box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.5), 0 2px 2px rgba(0, 0, 0, 0.19);
        border-bottom: solid 2px #b5b5b5;
    }

    .circle-button i {
        line-height: 80px;
    }

    .circle-button:active {
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 2px 2px rgba(0, 0, 0, 0.19);
        border-bottom: none;
    }

    @media screen and (min-width:0px) {
        .circle-button {
            bottom: 20px;
            right: 30px;
        }
    }

    @media screen and (min-width:768px) and (max-width:1024px) {
        .circle-button {
            bottom: 20px;
            right: 50px;
        }
    }

    @media screen and (min-width:1024px) {
        .circle-button {
            bottom: 30px;
            right: 150px;
        }
    }

    .delete-button {
        display: inline-block;
        text-decoration: none;
        width: 20px;
        height: 20px;
        line-height: 20px;
        font-size: 7px;
        border-radius: 50%;
        text-align: center;
        overflow: hidden;
        background-color: #cd5c5c;
        margin-right: 10px;
        margin-top: 5px;
    }

    .delete-button i {
        line-height: 20px;
        color: #fff;
    }

    .td-left {
        width: 10px;
    }
</style>
@endsection

@section('content')
<div class="card" style="width: 100%;">
    <div class="card-header">
        メモ一覧
    </div>

    <table class="table">
        <tbody>
            @foreach ($memos as $memo)
            <tr data-href="{{route('submit', ['id' => $memo->id])}}" class="table-row">
                <td class="td-left">
                    <a class="delete-button" type="button" data-toggle="modal" data-target="#modal{{$memo->id}}">
                        <i class="fa fa-minus"></i>
                    </a>
                </td>
                <td class="td-right">
                    {{$memo->title}}
                </td>
            </tr>
            <!-- Modal -->
            <div class="modal fade" id="modal{{$memo->id}}" tabindex="-1" role="dialog" aria-hidden="true">
                <div class="modal-dialog" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h5 class="modal-title">以下のメモを本当に削除しますか？</h5>
                            <button type="button" class="close" data-dismiss="modal">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            {{$memo->title}}
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-secondary" data-dismiss="modal">閉じる</button>
                            <button type="button" class="btn btn-primary"
                                onclick="location.href='{{ route('delete', ['id' => $memo->id])}}'">
                                削除
                            </button>
                        </div>
                    </div>
                </div>
            </div>
            @endforeach
        </tbody>
    </table>
</div>

<a href="{{route('submit')}}" class="circle-button">
    <i class="fa fa-plus"></i>
</a>

@endsection
```

submit.blade.phpのcss部分を消しておく。inputもついでに文字数制限をかける。

```html:resources/views/submit.blade.php
@extends('layouts.app')

@section('content')
<form method="POST" action="{{ route('submit', ['id' => $memo->id])}}">
    @csrf
    <div class="form-group">
        <label for="title">タイトル</label>
        <input type="text" class="form-control" id="title" name="title" value="{{$memo->title}}" maxlength="24"
            required>
    </div>
    <div class="form-group">
        <label for="content">内容</label>
        <input type="text" class="form-control" id="content" name="content" value="{{$memo->content}}" maxlength="60"
            required>
    </div>
    <a href="{{ route('home')}}" class="btn btn-primary">戻る</a>
    @if($memo->id == 0)
    <button type="submit" class="btn btn-success">追加</button>
    @else
    <button type="submit" class="btn btn-success">適用</button>
    @endif
</form>
@endsection
```

<img width="1387" alt="スクリーンショット 2020-01-04 23.29.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/8ab8a932-26d5-517b-09a7-07773aa132b7.png">