トップページ
Laravel学習サイトLaravelやるばい

目次

Ajaxで記事を削除する

今回がブログサイト作りの最後の解説です。

Ajaxでj削除する機能が一番のメインでブログサイト作成で私が一番解説したかった内容です。

ひとまず削除できるようにしてからAjaxを使って削除時に画面の読み込みをしないようにします。


ひとまず削除できるようにする

管理画面に削除ボタンを設置する為にadmin.blade.phpを修正します。

<div class="table-wrap">
    <table>
        <tr>
            <th>タイトル</th>
            <th>ステータス</th>
        </tr>
        @forelse($posts as $post)
            <tr>
                <td>
                    <p class="title">{{ $post->title }}</p>
                    <a class="edit-link" href="{{ route('post.edit', ['post' => $post->id]) }}">編集</a>


                    //ここから追加
                    <form method="post" action="{{ route('post.delete', ['post' => $post->id]) }}">
                        @csrf
                        @method('DELETE')
                        <button class="delete-button">削除</button>
                    </form>
                    //ここまで追加


                </td>
                <td>
                    @if (($post->status == '公開' && $post->delete_flag == 0) || ($post->status == '更新' && $post->delete_flag == 0))
                        公開中
                    @elseif ($post->status == '下書き')
                        下書き
                    @elseif ($post->status == '公開' && $post->delete_flag == 1)
                        公開停止
                    @endif
                </td>
            </tr>
        @empty
            <tr>
                <td>記事はまだありません</td>
            </tr>
        @endforelse
    </table>
</div>


<style>
    .table-wrap{
        width: 80%;
        margin-left:auto;
        margin-right:auto;
        padding-top: 50px;
        padding-bottom: 50px;
    }
    .title{
        margin-bottom: 5px;
    }
    .edit-link{
        font-size: 13px;
    }


    //ここから追加
    .delete-button{
        font-size: 13px;
        border:none;
        background:none;
        padding: 0;
    }
    //ここまで追加


    table{
        border:1px solid #000;
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border:1px solid #000;
    }
    td{
        padding: 5px;
    }
</style>

今のままでは削除ボタンに対するweb.phpの設定がないので設定します。

web.phpに下記の記述をします。

<?php

use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

Route::middleware('auth')->group(function () {
    Route::controller(PostController::class)->group(function() {
        Route::get('/post/create', 'create')->name('post.create');

        Route::post('/post/store', 'store')->name('post.store');

        Route::post('/post/image', 'uploadImage')->name('post.image');
    });
});

Route::controller(PostController::class)->group(function() {
    Route::get('/', 'top')->name('post.top');

    Route::get('/post/{post}/view', 'view')->name('post.view');

    Route::get('/post/admin', 'admin')->name('post.admin');

    Route::get('/post/{post}/edit', 'edit')->name('post.edit');

    Route::put('/post/{post}/update', 'update')->name('post.update');


    //ここから追加
    Route::delete('/post/{post}/delete', 'delete')->name('post.delete');
    //ここまで追加


});

require __DIR__.'/auth.php';

PostController.phpに下記の記述をします。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
    public function create()
    {
        return view('post.create');
    }

    public function store(Post $post, Request $request)
    {
        $post->postStore($request);

        return back()->with('message', '記事を作成しました');
    }

    public function uploadImage(Request $request)
    {
        if ($request->file('summernote-image')->isValid()) {

            $original_file_name = request()->file('summernote-image')->getClientOriginalName();

            $file_name = date('Ymd_His') . '_' . $original_file_name;

            request()->file('summernote-image')->move('storage/images', $file_name);

            echo '/storage/images/' . $file_name;
        }
    }

    public function top()
    {
        $posts = Post::publicPost();

        return view('post.top', compact('posts'));
    }

    public function view(Post $post)
    {
        return view('post.view', compact('post'));
    }

    public function admin()
    {
        $posts = Post::publicPost();

        return view('post.admin', compact('posts'));
    }

    public function edit(Post $post)
    {
        return view('post.edit', compact('post'));
    }

    public function update(Post $post, Request $request) {
        $post->postUpdate($post, $request);

        return back()->with('message', '投稿を更新しました');
    }


    // ここから追加
    public function delete(Post $post, Request $request) {
        $post->postDelete($post, $request);

        return back();
    }
    // ここまで追加


}

Post.phpに下記の記述をします。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'content',
        'eyecatch',
        'delete_flag',
        'status'
    ];

    protected $attributes = [
        'delete_flag' => 0
    ];

    public function postStore($request) {
        $inputs_validation = $request->validate([
            'title' => 'required',
            'content' => 'required',
            'eyecatch' => 'required',
            'status' => 'required',
        ]);

        $inputs_validation['eyecatch'] = $this->uploadEyecatch($request);

        $post = Post::create([
            'title' => $inputs_validation['title'],
            'content' => $inputs_validation['content'],
            'eyecatch' => $inputs_validation['eyecatch'],
            'status' => $inputs_validation['status'],
        ]);

        return $post;
    }

    private function uploadEyecatch($request) {
        if ($request->file('eyecatch')) {
            $original_file_name = $request->file('eyecatch')->getClientOriginalName();

            $store_file_name = date('Ymd_His') . '_' . $original_file_name;

            $request->file('eyecatch')->move('storage/eyecatchs', $store_file_name);

            return $store_file_name;
        }
    }

    public static function publicPost() {
        return Post::where('delete_flag', 0)->where('status', '公開')->get();
    }

    public function postUpdate($post, $request) {
        $inputs_validation = $request->validate([
            'title' => 'required',
            'content' => 'required',
            'eyecatch' => 'required',
            'status' => 'required',
        ]);

        $delete_flag = $request->has('delete_flag') ? 1 : 0;

        $inputs_validation['eyecatch'] = $this->uploadEyecatch($request);

        $post->update([
            'title' => $inputs_validation['title'],
            'content' => $inputs_validation['content'],
            'eyecatch' => $inputs_validation['eyecatch'],
            'status' => $inputs_validation['status'],
            'delete_flag' => $delete_flag,
        ]);
    }


    //ここから追加
    public function postDelete($post, $request) {
        $post->update([
            'delete_flag' => 1
        ]);
    }
    //ここまで追加


}

本当に削除するのではなくpostsテーブルのdelete_flagカラムの値を1に変更することで削除扱いにしています。

これでとりあえず削除機能が動作します。

次はAjaxを使って削除時に画面の読み込みをしないようにします。


Ajaxを使って削除

admin.blade.phpを修正・追記します。

//ここから追加
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <meta name="csrf-token" content="{{ csrf_token() }}">  
    <title>記事の作成</title>
</head>
//ここまで追加


<div class="table-wrap">
    <table>
        <tr>
            <th>タイトル</th>
            <th>ステータス</th>
        </tr>
        @forelse($posts as $post)
            <tr data-post-id="{{ $post->id }}">      //この行を修正
                <td>
                    <p class="title">{{ $post->title }}</p>
                    <a class="edit-link" href="{{ route('post.edit', ['post' => $post->id]) }}">編集</a>


                    //ここから削除
                    <form method="post" action="{{ route('post.delete', ['post' => $post->id]) }}">
                        @csrf
                        @method('DELETE')
                        <button class="delete-button">削除</button>
                    </form>
                    //ここまで削除


                    <button class="delete-button" data-id="{{ $post->id }}">削除</button>     //この行を追加
                </td>
                <td>
                    @if (($post->status == '公開' && $post->delete_flag == 0) || ($post->status == '更新' && $post->delete_flag == 0))
                        公開中
                    @elseif ($post->status == '下書き')
                        下書き
                    @elseif ($post->status == '公開' && $post->delete_flag == 1)
                        公開停止
                    @endif
                </td>
            </tr>
        @empty
            <tr>
                <td>記事はまだありません</td>
            </tr>
        @endforelse
    </table>
</div>

<style>
    .table-wrap{
        width: 80%;
        margin-left:auto;
        margin-right:auto;
        padding-top: 50px;
        padding-bottom: 50px;
    }
    .title{
        margin-bottom: 5px;
    }
    .edit-link{
        font-size: 13px;
    }
    .delete-button{
        font-size: 13px;
        border:none;
        background:none;
        padding: 0;
    }

    table{
        border:1px solid #000;
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border:1px solid #000;
    }
    td{
        padding: 5px;
    }
</style>


//ここから追加
<script>
    $(document).ready(function () {
        $('.delete-button').click(function (e) {
            e.preventDefault();

            const postId = $(this).data('id');

            if (!confirm('本当に削除しますか?')) {
                return;
            }

            $.ajax({
                url: `/post/${postId}/delete`,
                type: 'DELETE',
                data: {
                    _token: '{{ csrf_token() }}'
                },
                success: function (response) {
                    alert(response.message);
                    $(`tr[data-post-id="${postId}"]`).remove();
                },
            });
        });
    });
</script>
//ここまで追加

headタグの中の「<metaname="csrf-token"content="{{ csrf_token() }}">」はAjaxをする時に必要なので記述しています。

<trdata-post-id="{{ $post->id }}">」は削除する時にどの記事を削除するかを指定するのに使います。

動的に表示している場合に有効です。

削除ボタンに設定している「data-id="{{ $post->id }}」はweb.phpのURLに使う為の記述です。(下記参照)

Route::delete('/post/{post}/delete', 'delete')->name('post.delete');     //←の「{post}」のpostが{{ $post->id }}に該当

それではjQueryのコードの解説をします。

下記の記述は削除をした時に画面の読み込みを発生させない為にあります。

e.preventDefault();

「confirm」はいきなり削除ではなく本当に削除するかの確認をしてから削除をする為の記述です。

Ajaxの動作はコントローラーに送信する為の記述とコントローラーから受け取る記述の2つに分かれています。(下記参照)

$.ajax({


    //コントローラーに送信
    url: `/post/${postId}/delete`,
    type: 'DELETE',
    data: {
        _token: '{{ csrf_token() }}'
    },
    //コントローラーに送信


    //コントローラーから受け取る
    success: function (response) {
        alert(response.message);
        $(`tr[data-post-id="${postId}"]`).remove();
    },
    //コントローラーから受け取る

    
});

「url」の項目はweb.phpに合わせます。(下記参照

Route::delete('/post/{post}/delete', 'delete')->name('post.delete');     //←の「/post/{post}/delete」

「type」の項目はweb.phpの「Route::〜」の「〜」に合わせます。(下記参照)

Route::delete('/post/{post}/delete', 'delete')->name('post.delete');     //←のdelete

「data」の項目はheadタグの記述(下記参照)をコントローラーに送ります、この記述がないとエラーになります。

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

データの項目はそのまま書いていいです。

ここまでの動作が成功するとPostコントローラーにデータが送信されます。

コントローラーからAjaxにデータを送る為の記述をします。

public function delete(Post $post, Request $request) {
    $post->postDelete($post, $request);

    return back();      //この行を削除
    return response()->json(['message' => '記事を削除しました']);      //この行を追加
}

追記した記述でコントローラーからAjaxにデータを送ることができます。

「message」はAjaxで使うことができる変数名で「’記事を削除しました'」がmessageの値です。(連想配列の形になっています)

Ajaxに戻ります。

success: function (response)」のresponseがコントローラーから送られたデータです。

コントローラーから送られるデータはオブジェクトになります。

「console.log(response)」をすると下記の表示になります。

{message: '記事を削除しました'}

だからデータにアクセする時の記述は「response.message」になり「alert('response.message')」で「記事を削除しました」を表示します。

この時点で削除ボタンに該当する記事のdelete_flagの値は1になり削除扱いになっていますが画面上では削除されていないので削除します。

それが「$(`tr[data-post-id="${postId}"]`).remove();」でビューの「<trdata-post-id="{{ $post->id }}"></tr>」の部分が対応しています。

これでAjaxを使って削除ができます。

これでブログサイト作りは完成です。

戻る