Crud setup for posts

This tutorial is part of this mini project on topic oriented user community. To see all the sections of this project Click Here

Now, it's time to give focus on following methods in controller CommunityTopicPostController.php to show all posts which are being saved under particular topics and communities

<?php
namespace App\Http\Controllers;

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

class CommunityTopicPostController extends Controller
{

    public function index(Community $community, Topic $topic)
    {
        $query = Post::with(['community','topic'])->where(['community_id' => $community->id , 'topic_id' => $topic->id]);

        if ( !empty(request('search')) ) {
            $query->where('title', 'like' ,'%'.request('search').'%')->orWhere('post_text', 'like' , '%'.request('search').'%');
        }

        $posts = $query->orderBy('id')->paginate(10);

        return view('community_topic_posts.index', compact( 'community' ,'topic' , 'posts'));
    }

    //
    public function create(Community $community, Topic $topic)
    {
        $community->load('topics');
        $communities = Community::with( 'topics')->get();
        return view('community_topic_posts.create', compact( 'community', 'topic' , 'communities' ));
    }

    public function store(Request $request, Community $community, Topic $topic)
    {
        $this->validate($request, [
            'title' => 'required',
            'post_text' => 'required',
            'community_id' => 'required',
            'topic_id' => 'required',
        ]);

        Post::create([
            'user_id' => 0,
            'community_id' => $request->community_id,
            'topic_id' => $request->topic_id,
            'title' => $request->title,
            'post_text' => $request->post_text ?? null,
        ]);

        return redirect()->route('communities.topics.posts.index', [$community, $topic]);
    }

    public function edit(Community $community, Topic $topic, Post $post)
    {
        $community->load('topics');
        $communities = Community::with( 'topics')->get();
        return view('community_topic_posts.edit', compact( 'community' ,'topic', 'post' , 'communities' ));
    }

    public function update(Request $request, Community $community, Topic $topic, Post $post)
    {
        $this->validate($request, [
            'title' => 'required',
            'post_text' => 'required',
            'community_id' => 'required',
            'topic_id' => 'required',
        ]);

        $post->update([
            'user_id' => 0,
            'community_id' => $request->community_id,
            'topic_id' => $request->topic_id,
            'title' => $request->title,
            'post_text' => $request->post_text ?? null,
        ]);

        return redirect()->route('communities.topics.posts.show', [ $community, $topic, $post]);
    }

    public function show(Community $community, Topic $topic, Post $post)
    {
        //
        $post->load('comments');
        $relatedPosts = Post::where('id', '<>', $post->id)->where('topic_id', $topic->id)->orderBy('id', 'asc')->get();
        return view('community_topic_posts.show', compact( 'community' ,'topic' , 'post', 'relatedPosts'));
    }

    public function destroy(Community $community, Topic $topic, Post $post)
    {
        if ($post->comments->count() > 0) {
            $post->comments->each->delete();
        }
        $post->delete();

        return redirect()->route('communities.topics.posts.index', [$community,$topic]);
    }

    public function getTopics($community_id)
    {
        $community = Community::find($community_id);
        if ($community) {
            $topics = $community->topics()->select('id', 'name', 'slug')->get();
        } else {
            $topics = [];
        }

        return response()->json($topics);
    }
}

Now, Let's turn our attention to the blade files for CommunityTopicPostController.php, specifically index.blade.php, create.blade.php, and show.blade.php

1. index.blade.php

@extends('layouts.app')
@section('content')
    <div class="container mt-5">
        <div class="row">
            <div class="col-md-10 col-lg-8 col-xl-12">
                <h2 class="float-start me-2">{{ $community->name }} - {{ $topic->name }}</h2>
                <div class="float-end mt-2"><a href="{{ route('communities.topics.posts.create', [$community, $topic])}}" class="btn btn-sm btn-primary">Create Post</a></div>
                <div class="clearfix"></div>
                <hr>
                <div class="mb-3">{!! $topic->description !!}</div>
                <!-- Another variation with a button -->
                <form method="GET" action="{{ route('communities.topics.posts.index', [$community, $topic] ) }}">
                <div class="input-group">
                    <input type="text" class="form-control" name="search" value="{{ request('search','') }}" placeholder="search posts"> &nbsp;
                    <div class="input-group-append">
                        <button class="btn btn-sm btn-secondary py-2" type="submit">
                            Search
                        </button>
                        <a class="btn btn-sm btn-danger py-2" href="{{ route('communities.topics.posts.index', [$community, $topic] ) }}">
                            Reset
                        </a>
                    </div>
                </div>
                </form>
            </div>
        </div>
    </div>
    <div class="container">
        @forelse ($posts as $post)
            @php
                //dd($post);
            @endphp
            <div class="row my-5 border bg-light shadow">
                <div class="col-md-12 align-self-center p-4 ">
                    <a class="text-dark" href="{{ route('communities.topics.posts.show', [ $post->community, $post->topic, $post]) }}">
                        <h5 >{{ $post->title }}</h5>
                    </a>
                    <h6>{{ $post->created_at->diffForHumans() }}</h6>
                    <p>{{ \Illuminate\Support\Str::words(strip_tags($post->post_text), 20) }}</p>
                    <a href="{{ route('communities.topics.posts.show', [ $post->community, $post->topic, $post]) }}" class="btn btn-outline-danger btn-sm">Learn More</a>
                </div>
            </div>
        @empty
            <div class="row my-5">
                <div class="col-md-12 align-self-center">
                No posts found.
                </div>
            </div>
        @endforelse
        <div class="text-center">
            {!! $posts->links() !!}
        </div>
    </div>
@endsection

@section('styles')

    <style>
        .has-search .form-control {
            padding-left: 2.375rem;
        }

        .has-search .form-control-feedback {
            position: absolute;
            z-index: 2;
            display: block;
            width: 2.375rem;
            height: 2.375rem;
            line-height: 2.375rem;
            text-align: center;
            pointer-events: none;
            color: #aaa;
        }
    </style>

@endsection

2. create.blade.php

@php
    $queryCommunityId = $community->id;
    $queryTopicId = $topic->id;
    $selectedCommunityId = old('community_id', $queryCommunityId);
    $selectedTopicId = old('topic_id', $queryTopicId);
@endphp

@extends('layouts.app')
@section('content')
    <div class="container mt-5">
        <div class="row align-center justify-content-center">
            <div class="col-lg-7">
                <h2>Create Post on {{ $community->name }} - {{ $topic->name }}</h2>
                <form action="{{ route('communities.topics.posts.store', [$community, $topic]) }}" method="POST" id="community-topic-form" data-community-id="{{ $selectedCommunityId }}" data-topic-id="{{ $selectedTopicId }}">
                    @csrf
                    <div class="mb-3">
                        <label class="form-label" for="first-name">Community<span class="required">*</span>
                        </label>
                        <select class="form-control" name="community_id" id="community_id">
                            <option value="">Choose community</option>
                            @foreach($communities as $db_community)
                                <option value="{{ $db_community->id }}" {{ $selectedCommunityId == $db_community->id ? 'selected' : ''  }}>{{ $db_community->name }}</option>
                            @endforeach
                        </select>
                        @if ($errors->has('community_id'))
                            <span class="text-danger">{{ $errors->first('community_id') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label class="form-label" for="first-name">Topic<span class="required">*</span>
                        </label>
                        <select class="form-control" name="topic_id" id="topic_id">
                            <option value="">Choose topic</option>
                        </select>
                        @if ($errors->has('topic_id'))
                            <span class="text-danger">{{ $errors->first('topic_id') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label for="exampleInputName" class="form-label">Title</label>
                        <input type="text" class="form-control" id="title" name="title">
                        @if ($errors->has('title'))
                            <span class="text-danger">{{ $errors->first('title') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label for="exampleInputDes" class="form-label">Description</label>
                        <input type="text" class="form-control" id="post_text" name="post_text">
                        @if ($errors->has('post_text'))
                            <span class="text-danger">{{ $errors->first('post_text') }}</span>
                        @endif
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                    &nbsp;<a href="{{ route('communities.topics.posts.index', [$community, $topic])  }}" class="btn btn-danger">Back</a>
                </form>
            </div>
        </div>
    </div>
@endsection

3. edit.blade.php

@php
    $queryCommunityId = $community->id;
    $queryTopicId = $topic->id;
    $selectedCommunityId = old('community_id', $queryCommunityId);
    $selectedTopicId = old('topic_id', $queryTopicId);
@endphp

@extends('layouts.app')
@section('content')
    <div class="container mt-5">
        <div class="row align-center justify-content-center">
            <div class="col-lg-7">
                <h2>Edit Post on {{ $community->name }} - {{ $topic->name }}</h2>
                <form action="{{ route('communities.topics.posts.update', [ $community ,$topic, $post ]) }}" method="POST" id="community-topic-form" data-community-id="{{ $selectedCommunityId }}" data-topic-id="{{ $selectedTopicId }}">
                    @csrf
                    @method('PUT')
                    <div class="mb-3">
                        <label class="form-label" for="first-name">Community<span class="required">*</span>
                        </label>
                        <select class="form-control" name="community_id" id="community_id">
                            <option value="">Choose community</option>
                            @foreach($communities as $db_community)
                                <option value="{{ $db_community->id }}" {{ $selectedCommunityId == $db_community->id ? 'selected' : ''  }}>{{ $db_community->name }}</option>
                            @endforeach
                        </select>
                        @if ($errors->has('community_id'))
                            <span class="text-danger">{{ $errors->first('community_id') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label class="form-label" for="first-name">Topic<span class="required">*</span>
                        </label>
                        <select class="form-control" name="topic_id" id="topic_id">
                            <option>Choose topic</option>
                        </select>
                        @if ($errors->has('topic_id'))
                            <span class="text-danger">{{ $errors->first('topic_id') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label for="exampleInputName" class="form-label">Title</label>
                        <input type="text" class="form-control" id="title" name="title" value="{{ $post->title }}">
                        @if ($errors->has('title'))
                            <span class="text-danger">{{ $errors->first('title') }}</span>
                        @endif
                    </div>
                    <div class="mb-3">
                        <label for="exampleInputDes" class="form-label">Description</label>
                        <input type="text" class="form-control" id="post_text" name="post_text" value="{{ $post->post_text }}">
                        @if ($errors->has('post_text'))
                            <span class="text-danger">{{ $errors->first('post_text') }}</span>
                        @endif
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                    &nbsp;<a href="{{ route('communities.topics.posts.index', [$community, $topic])  }}" class="btn btn-danger">Back</a>
                </form>
            </div>
        </div>
    </div>
@endsection

4. show.blade.php

@extends('layouts.app')

@section('content')

    <!-- Page Header-->
    <div class="container mt-5">
        <div class="row">
            <div class="col-xl-12">
                <h1 class="text-dark">{{ $post->title }}</h1>
            </div>
        </div>
    </div>

    <main class="my-3">
        <div class="container">
            <div class="row">
                <div class="col-xl-12">
                    <div class="post-preview">
                        <p class="post-meta">
                            Category: <strong><a href="{{ route('communities.show', $post->community) }}">{{ $post->community->name }}</a> </strong>
                            Topic: <strong><a href="{{ route('topics.show', $post->topic) }}">{{ $post->topic->name }}</a></strong>
                            Created: {{ $post->created_at->diffForHumans() }}
                        @if ($post->post_url != '')
                            <div class="my-3">
                                <a href="{{ $post->post_url }}" target="_blank">{{ $post->post_url }}</a>
                            </div>
                            @endif
                    </div>
                    {!! $post->post_text !!}
                    <div class="post-preview my-4">
                        <p class="post-meta">  Last Edited: {{ $post->updated_at->diffForHumans() }}  </p>
                    </div>

                    <a href="{{ route('communities.topics.posts.edit', [$community, $topic, $post]) }}"
                       class="btn btn-sm btn-primary">Edit post</a>
                    <a href="#" class="resource-delete-row btn btn-sm btn-danger"
                       data-resource-name="{{ $post->title  }}"
                       data-dellink="{{ route('communities.topics.posts.destroy', [$community, $topic, $post]) }}"
                       data-toggle="tooltip" title="Delete">
                        Delete
                    </a>

                    <hr/>
                    <h3>Comments</h3>
                    <div class="comments"></div>
                    @forelse ($post->comments as $comment)
                        <b>Demo User</b> says:
                        <br/>
                        <span style="color:grey;"><small>{{ $comment->created_at->diffForHumans() }}</small></span>
                        <p class="mt-2">{{ $comment->comment_text }}</p>
                    @empty
                        <span class="no-comments">No comments yet.</span>
                    @endforelse
                    <hr/>
                    <form method="POST" action="{{ route('posts.comments.store', $post) }}" id="post-comment-frm">
                        @csrf
                        Add a comment:
                        <br/>
                        <textarea class="form-control" name="comment_text" rows="3" required></textarea>
                        <br/>
                        <div id="error-message" class="text-danger"></div>
                        <button type="submit" class="btn btn-sm btn-primary">Add Comment</button>
                    </form>
                </div>
            </div>
        </div>
    </main>

    @if(count($relatedPosts))
        <div class="container mt-5">
            <h3>Related Posts</h3>
            <hr class="my-4" />
            <div class="row mt-4">
                @foreach($relatedPosts as $post)
                    <div class="col-sm-4 ">
                        <a href="{{ route('communities.topics.posts.show', [$post->community,$post->topic, $post]) }}"><h4>{{ $post->title }}</h4></a>
                        <div class="separator"></div>
                        <div class="post-preview">
                            <p class="post-meta">Created: {{ $post->created_at->diffForHumans() }}</p>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
    @endif

    <div id="resource-delete-confirm" class="modal fade">
        <div class="modal-dialog modal-md modal-dialog-centered" >
            <div class="modal-content" >
                <div class="modal-header">
                    <h4 class="modal-title">Confirm Delete!</h4>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <div id="resource-confirm-form">
                        <div class="text-center">
                            <p>Would you like to delete this post "<strong><span id="resource-delete-title"></span></strong>"? <br>Once deleted it cannot be reverted.</p>
                            <a id="resource-delete-link" class="btn btn-sm btn-danger">Yes</a> &nbsp;&nbsp;
                            <form id="delete-submit-form" action="" method="POST" class="d-none">
                                @csrf
                                @method('DELETE')
                            </form>
                            <a href="#" class="btn btn-sm btn-info white modal-close">No</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

@endsection

Lastly, setting up routes for CommunityTopicPostController.php and ajax loading of topics for particular communities in posts create and update form.

Route::get('/community/{community}/topics', [\App\Http\Controllers\CommunityTopicPostController::class, 'getTopics']);
Route::resource('communities.topics.posts', \App\Http\Controllers\CommunityTopicPostController::class);

So, CommunityTopicPostController.php and blade files for the controller have been setup. Now, we can move on to the next post.


Related Posts


Crud setup for communities

Crud setup for topics

Ajax setup for post comment