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

目次

公開した記事を表示して記事の中身を表示して記事を編集できるようにする

今回は公開にした記事を表示して中身を表示するのと編集ができるようにします。

前回の記事が理解できたら今回の記事の内容は簡単だと思います。

まずは公開している記事を表示します。


公開している記事の表示・中身の表示

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('/', function () {
    return view('welcome');
});
//ここまで削除


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');
});
//ここまで追加


require __DIR__.'/auth.php';

welcome.blade.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'));
    }
    // ここまで追加

    
}

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();
    }
    //ここまで追加

    
}

where句でstatusカラムの値が「公開」の記事のみを表示するようにしています。

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

<div class="wrap">
    @forelse ($posts as $post)
        <a href="" class="post-wrap">
            <img src="{{ asset('storage/eyecatchs/' . $post->eyecatch) }}" alt="">
            <h2>{{ $post->title }}</h2>
        </a>
    @empty
        <p>記事がまだありません。</p>
    @endforelse
</div>


<style>
    .wrap{
        width: 90%;
        margin-left:auto;
        margin-right:auto;
        display: flex;
        column-gap:14%;  
        flex-wrap:wrap;   
        padding-top: 50px;
        padding-bottom: 50px;
    }
    .post-wrap{
        width: 43%;
    }
    .post-wrap img{
        width: 100%;
    }
    .post-wrap h2{
        font-size: 14px;
    }
</style>

これで下記の見た目になります。

20250112_151224_blog-site7.jpg
記事の中身を表示するのはコードのみを掲載します。

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('/', function () {
    return view('welcome');
});
//ここまで削除


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');    //ここまで追加


});

require __DIR__.'/auth.php';

top.blade.phpを下記に修正します。

<div class="wrap">
    @forelse ($posts as $post)
        <a href="{{ route('post.view', ['post' => $post->id]) }}" class="post-wrap">     //←の行を修正
            <img src="{{ asset('storage/eyecatchs/' . $post->eyecatch) }}" alt="">
            <h2>{{ $post->title }}</h2>
        </a>
    @empty
        <p>記事がまだありません。</p>
    @endforelse
</div>

PostContrller.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'));
    }
    // ここまで追加


}

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

<div class="wrap">
    <h2>{{ $post->title }}</h2>
    <div>
        {!! $post->content !!}
    </div>
</div>


<style>
    .wrap{
        width: 90%;
        margin-left:auto;
        margin-right:auto;
        padding-top: 50px;
        padding-bottom: 50px;
    }
    .post-wrap h2{
        font-size: 20px;
    }
</style>

summernoteのエディタの内容を表示するときはカラムの値を表示する時と少し違い下記になります。

{!! $post->カラム !!}

通常の表記をしたとします。

{{ $post->content }}

この記述をするとエディタに書いた時にタグも含まれるのですが一緒に出力します。

次は記事の編集です。


記事の編集

管理画面に記事の一覧を表示して編集できるようにします。

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('/', function () {
    return view('welcome');
});
//ここまで削除


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');
    //ここまで追加


});

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'));
    }
    // ここまで追加


}

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="">編集</a>
                </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;
    }
    table{
        border:1px solid #000;
        width: 100%;
        border-collapse: collapse;
    }
    th, td{
        border:1px solid #000;
    }
    td{
        padding: 5px;
    }
</style>

管理画面にアクセスすると下記の表示になります。

20250112_161448_blog-site8.jpg
編集のリンクから記事の編集のページに遷移します。

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>    //←この行を修正
                </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>

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');
    //ここまで追加


});

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', '投稿を更新しました');
    }
    // ここまで追加


}

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,
        ]);
    }
    //ここまで追加


}

edit.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>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-bs4.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.20/src/lang/summernote-ja-JP.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.0/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <meta name="csrf-token" content="{{ csrf_token() }}">  
    <title>記事の編集</title>
</head>
<body>
    <div class="container pt-5 pb-5">
        @if(session('message'))
            <p class="pt-2 pb-2 text-center flash">{{ session('message') }}</p>
        @endif
        @if($errors->any())
            <div>
                <p>記事の登録ができません</p>
                <ul>
                    @foreach($errors->all() as $error)
                    <li>{{$error}}</li>
                    @endforeach
                </ul>
            </div>
        @endif
        <h2>問題の編集</h2>
        <form class="mt-2" action="{{ route('post.update', ['post' => $post->id]) }}" method="post" enctype="multipart/form-data">
            @csrf
            @method('PUT')
            <div>
                <label class="label">タイトル : </label>
                <input type="text" name="title" class="d-inline-block title" value="{{ old('title', $post->title) }}" required>
            </div>
            <div class="mt-3">
                <textarea name="content" id="summernote" cols="30" rows="10">{{ old('content', $post->content) }}</textarea>
            </div>
            <div class="eyecatch mt-3">
                <h2>アイキャッチ画像</h2>
                <div class="mt-2">
                    <input type="file" name="eyecatch" accept="image/jpg,image/png">
                    <img class="exist-eyecatch" src="{{ asset('storage/eyecatchs/' . $post->eyecatch) }}" alt="">
                </div>
            </div>
            <div class="mt-5">
                <button type="submit" name="status" value="公開" class="border-0">公開</button>
                <button type="submit" name="status" value="下書き" class="border-0">下書き保存</button>
            </div>
        </form>
    </div>
</body>
</html>

<script>
    $(document).ready(function() {
        $('#summernote').summernote({
            placeholder: 'ここにテキストを書きます。画像も入れる事ができます。',
            height: 500,
            lang: 'ja-JP',
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'underline', 'clear']],
                ['fontname', ['fontname']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'paragraph']],
                ['table', ['table']],
                ['insert', ['link', 'picture', 'video']],
                ['view', ['fullscreen', 'codeview', 'help']]
            ],
            callbacks: {
                onImageUpload: function(files) {
                    sendFile(files[0]);
                },
            }
        });

        function sendFile(file) {
            let form_data = new FormData();

            form_data.append('summernote-image', file);
            
            $.ajax({
                headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
                data: form_data,
                type: "POST",
                contentType: 'multipart/form-data',
                url: '/post/image',
                cache: false,
                contentType: false,
                processData: false,
            })
            .done(function(url){
                $('#summernote').summernote('insertImage', url);
            })
            .fail(function(XMLHttpRequest, textStatus, errorThrown){
                console.log(XMLHttpRequest.status);
                console.log(textStatus);
                console.log(errorThrown.message);
            })
            .always(function(url){
                console.log('画像を送りました');
            });
        }

    });
</script>

<style>
    .container{
        width: 70%;
        margin-left:auto;
        margin-right:auto;
        padding-top: 50px;
    }
    .title{
        width: 300px;
    }
    .flash{
        border:1px solid red;
    }
    .exist-eyecatch{
        width: 500px;
        margin-top: 20px;
    }
    h2{
        font-size: 18px;
        font-weight: bold;
    }
</style>

これで記事の更新ができます。


戻る