Laravel Tutorial #7: Create Routes, Controllers and Views


More Tutorials on Web Development


Registering Routes

use App\Http\Controllers\CategoryController;
use App\Http\Controllers\IndexController;
use App\Http\Controllers\PostController;
use App\Http\Controllers\TagController;

Route::get('/', IndexController::class);
Route::get('/category/{slug}', CategoryController::class);
Route::get('/tag/{slug}', TagController::class);
Route::get('/post/{slug}', [PostController::class, 'show']);

We didn’t use the single-action controller for the post controller because we are going to add a search function here in the next article.

For now, your IDE will probably give you an error, because we haven’t created these controllers yet.

Controllers

If you are using Voyager, you can delete the line use App\Models\General;. It’s not necessary here.

Index Controller

<?php

namespace App\Http\Controllers;

use App\Models\General; //Delete this line if you are using Voyager
use App\Models\Post;
use App\Models\Tag;
use App\Models\Category;
use Illuminate\Http\Request;

class IndexController extends Controller
{
    public function __invoke()
    {
        //get the general information about the website
        $website = General::query()->firstOrFail();

        //get the posts that are published, sort by decreasing order of "id".
        $posts = Post::query()
            ->where('is_published',true)
            ->orderBy('id','desc')
            ->get();

        //get the featured posts
        $featured_posts = Post::query()
            ->where('is_published',true)
            ->where('is_featured',true)
            ->orderBy('id','desc')
            ->take(5)
            ->get();

        //get all the categories
        $categories = Category::all();

        //get all the tags
        $tags = Tag::all();

        //get the recent 5 posts
        $recent_posts = Post::query()
            ->where('is_published',true)
            ->orderBy('created_at','desc')
            ->take(5)
            ->get();

        //return the data to the corresponding view
        return view('home', [
            'website' => $website,
            'posts' => $posts,
            'featured_posts' => $featured_posts,
            'categories' => $categories,
            'tags' => $tags,
            'recent_posts' => $recent_posts
        ]);
    }
}

Category Controller

<?php

namespace App\Http\Controllers;

use App\Models\General; //Delete this line if you are using Voyager
use App\Models\Post;
use App\Models\Tag;
use App\Models\Category;
use Illuminate\Http\Request;

class CategoryController extends Controller
{
    public function __invoke($slug)
    {
        //get the general information about the website
        $website = General::query()->firstOrFail();

        //get the requested category
        $category = Category::query()
            ->where('slug', $slug)
            ->firstOrFail();

        //get the posts in that category
        $posts = $category->posts()
            ->where('is_published',true)
            ->orderBy('id','desc')
            ->get();

        //get all the categories
        $categories = Category::all();

        //get all the tags
        $tags = Tag::all();

        //get the recent 5 posts
        $recent_posts = Post::query()
            ->where('is_published',true)
            ->orderBy('created_at','desc')
            ->take(5)
            ->get();

        //return the data to the corresponding view
        return view('category', [
            'website' => $website,
            'category' => $category,
            'posts' => $posts,
            'categories' => $categories,
            'tags' => $tags,
            'recent_posts' => $recent_posts
        ]);
    }
}

Tag Controller

<?php

namespace App\Http\Controllers;

use App\Models\General; //Delete this line if you are using Voyager
use App\Models\Post;
use App\Models\Tag;
use App\Models\Category;
use Illuminate\Http\Request;

class TagController extends Controller
{
    public function __invoke($slug)
    {
        //get the general information about the website
        $website = General::query()->firstOrFail();

        //get the requested tag
        $tag = Tag::query()
            ->where('slug', $slug)
            ->firstOrFail();

        //get the posts with that tag
        $posts = $tag->posts()
            ->where('is_published',true)
            ->orderBy('id','desc')
            ->get();

        //get all the categories
        $categories = Category::all();

        //get all the tags
        $tags = Tag::all();

        //get the recent 5 posts
        $recent_posts = Post::query()
            ->where('is_published',true)
            ->orderBy('created_at','desc')
            ->take(5)
            ->get();

        //return the data to the corresponding view
        return view('tag', [
            'website' => $website,
            'tag' => $tag,
            'posts' => $posts,
            'categories' => $categories,
            'tags' => $tags,
            'recent_posts' => $recent_posts
        ]);
    }
}

Post Controller

<?php

namespace App\Http\Controllers;

use App\Models\General; //Delete this line if you are using Voyager
use App\Models\Post;
use App\Models\Tag;
use App\Models\Category;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function show($slug)
    {
        //get the general information about the website
        $website = General::query()->firstOrFail();

        //get the requested post, if it is published
        $post = Post::query()
            ->where('is_published', true)
            ->where('slug', $slug)
            ->firstOrFail();

        //get all the categories
        $categories = Category::all();

        //get all the tags
        $tags = Tag::all();

        //get the recent 5 posts
        $recent_posts = Post::query()
            ->where('is_published', true)
            ->orderBy('created_at', 'desc')
            ->take(5)
            ->get();

        //return the data to the corresponding view
        return view('post', [
            'website' => $website,
            'post' => $post,
            'categories' => $categories,
            'tags' => $tags,
            'recent_posts' => $recent_posts,
        ]);
    }
}

Views

In this tutorial, we will be using this Bootstrap template for our views.

img

You can download the source code here:

Assuming you understand how HTML, CSS and JS work. This is the view structure I designed:

Layout

  • views
    • vendor
      • posts-list.blade.php
      • sidebar.blade.php
    • master.blade.php
    • home.blade.php
    • category.blade.php
    • tag.blade.php
    • post.blade.php

Master

<!DOCTYPE html>
<html lang="en">

<head>

@yield('meta')

@yield('title')

<!-- Bootstrap core CSS -->
    <link href="{{asset('vendor/bootstrap/css/bootstrap.css')}}" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="{{asset('vendor/css/mystyle.css')}}" rel="stylesheet">
</head>

<body>

<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container">
        <a class="navbar-brand" href="/">{{$website['website_title']}}</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="/">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="https://www.techjblog.com/index.php/laravel-tutorial-for-beginners/">Laravel
                        Tutorial</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/nova">Admin</a>
                </li>
            </ul>
        </div>
    </div>
</nav>

<!-- Page Content -->
<div class="container">

    @yield('content')

</div>
<!-- /.container -->

<!-- Footer -->
<footer class="py-5 bg-dark">
    <div class="container">
        <p class="m-0 text-center text-white">Copyright
            © {{$website['website_title']}} <?php echo date("Y"); ?></p>
    </div>
    <!-- /.container -->
</footer>

<!-- Bootstrap core JavaScript -->
<script src="{{asset('vendor/bootstrap/js/jquery/jquery.min.js')}}"></script>
<script src="{{asset('vendor/bootstrap/js/bootstrap.bundle.min.js')}}"></script>

</body>
</html>

Line 11, {{asset('vendor/css/bootstrap.min.css')}} generates the URL /public/vendor/css/bootstrap.min.css.

Post List

@foreach($posts as $post)
    <!-- Blog Post -->
    <div class="card mb-4">
        <img class="card-img-top" src="{{\Illuminate\Support\Facades\Storage::url($post['featured_image'])}}" alt="Card image cap">
        <div class="card-body">
            <h2 class="card-title">{{$post['title']}}</h2>
            <p class="card-text">{{\Illuminate\Support\Str::limit(strip_tags($post['content']), 200, '...')}}</p>
            <a href="/post/{{$post['slug']}}" class="btn btn-primary">Read More →</a>
        </div>
        <div class="card-footer text-muted">
            Posted on {{$post->created_at->format('M d Y')}} by
            <a href="#">{{$post->user['name']}}</a>
        </div>
    </div>
@endforeach

Line 7, strip_tags() will eliminate the HTML tags, \Illuminate\Support\Str::limit() can limit the number of characters that are shown, in this case, limit the string to 200 char and end with “...

Line 11, format('M d Y') changes the format of the date.

Sidebar

<!-- Sidebar Widgets Column -->
<div class="col-md-4">

    <!-- Search Widget -->
    <div class="card my-4">
        <h5 class="card-header">Search</h5>
        <form class="card-body" action="" method="GET" role="search">
            <div class="input-group">
                <input type="text" class="form-control" placeholder="Search for..." name="q">
                <span class="input-group-btn">
                <button class="btn btn-secondary" type="submit">Go!</button>
              </span>
            </div>
        </form>
    </div>
    <a href="https://www.techjblog.com/index.php/tutorials/laravel-tutorial-9-search-pagination-related-posts-and-other-functions/#0-search/">How
        to Add Search Function for Your Blog</a>


    <!-- Categories Widget -->
    <div class="card my-4">
        <h5 class="card-header">Categories</h5>
        <div class="card-body">
            <div class="row">
                <div class="col-lg-6">
                    <ul class="list-unstyled mb-0">
                        @foreach($categories as $category)
                            <li>
                                <a href="/category/{{$category['slug']}}">{{$category['name']}}</a>
                            </li>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>

    <!-- Tags Widget -->
    <div class="card my-4">
        <h5 class="card-header">Tags</h5>
        <div class="card-body">
            <div class="row">
                <div class="col-lg-6">
                    <ul class="list-unstyled mb-0">
                        @foreach($tags as $tag)
                            <li>
                                <a href="/tag/{{$tag['slug']}}">{{$tag['name']}}</a>
                            </li>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>

    <!-- Recent Posts Widget -->
    <div class="card my-4">
        <h5 class="card-header">Recent Posts</h5>
        <div class="card-body">
            <div class="row">
                <div class="col-lg-12">
                    <ul class="list-unstyled mb-0">
                        @foreach($recent_posts as $post)
                            <li>
                                <a href="/post/{{$post['slug']}}">{{$post['title']}}</a>
                            </li>
                            <hr>
                        @endforeach
                    </ul>
                </div>
            </div>
        </div>
    </div>

    <!-- Side Widget -->
    <div class="card my-4">
        <h5 class="card-header">Tutorial</h5>
        <div class="card-body">
            <ul class="list-unstyled mb-0">
                <li>
                    <a href="https://www.techjblog.com/index.php/laravel-tutorial-for-beginners/">Laravel Tutorial For
                        Beginners</a>
                </li>
            </ul>
        </div>
    </div>

</div>

Home Page

@extends('master')

@section('meta')
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="{{$website['description']}}">
    <meta name="author" content="Eric Hu">
@endsection

@section('title')
    <title>{{$website['website_title']}}</title>
@endsection

@section('content')
    <div class="row">
        <!-- Blog Entries Column -->
        <div class="col-md-8">

            <h1 class="my-4">Laravel Blog
                <small>Home Page</small>
            </h1>

            @include('vendor.posts-list')

        </div>
        @include('vendor.sidebar')
    </div>
@endsection

Category Page

@extends('master')

@section('meta')
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="{{$website['description']}}">
    <meta name="author" content="Eric Hu">
@endsection

@section('title')
    <title>{{$website['website_title']}} - Category:{{$category['name']}}</title>
@endsection

@section('content')
    <div class="row">
        <!-- Blog Entries Column -->
        <div class="col-md-8">

            <h1 class="my-4">Category:
                <small>{{$category['name']}}</small>
            </h1>

            @include('vendor.posts-list')

        </div>
        @include('vendor.sidebar')
    </div>
@endsection

Tag Page

@extends('master')

@section('meta')
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="{{$website['description']}}">
    <meta name="author" content="Eric Hu">
@endsection

@section('title')
    <title>{{$website['website_title']}} - Tag:{{$tag['name']}}</title>
@endsection

@section('content')
    <div class="row">
        <!-- Blog Entries Column -->
        <div class="col-md-8">

            <h1 class="my-4">Tag:
                <small>{{$tag['name']}}</small>
            </h1>

            @include('vendor.posts-list')

        </div>
        @include('vendor.sidebar')
    </div>
@endsection

Post Page

@extends('master')

@section('meta')
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="{{$website['description']}}">
    <meta name="author" content="Eric Hu">
@endsection

@section('title')
    <title>{{$post['title']}}{{$website['website_title']}}</title>
@endsection

@section('content')
    <div class="row">

        <!-- Post Content Column -->
        <div class="col-lg-8">

            <!-- Title -->
            <h1 class="mt-4">{{$post['title']}}</h1>

            <!-- Author -->
            <p class="lead">
                by
                <a href="#">{{$post->user['name']}}</a>
            </p>
            <hr>

            <!-- Date/Time -->
            <p>Posted on {{$post->created_at->format('M d, Y')}}</p>
            <hr>

            <!-- Preview Image -->
            <img class="img-fluid rounded" src="{{\Illuminate\Support\Facades\Storage::url($post->featured_image)}}" alt="">
            <hr>

            <!-- Post Content -->
            {!! $post->content !!}
            <hr>

        </div>
        @include('vendor.sidebar')

    </div>
@endsection

Line 39, {!! $post->content !!} get the content for the post. {!! !!} must be used or the HTML tags will not work.

By default, Blade {{ }} statements are automatically sent through PHP’s htmlspecialchars function to prevent XSS attacks.

For Voyager Users

Voyager has a built-in section for basic information of our website, and that is why the General model is not necessary. Go to settings and this is what you will see:

To access this information:

{{ setting('site.title') }}

13 thoughts on “Laravel Tutorial #7: Create Routes, Controllers and Views”

  1. Thanks for a very goog tutorial.

    Everything went smoothly, but now I’m getting the following error:

    Class ‘App\Models\General’ not found

    I’m using Voyager and therefore I didn’t do anything in lesson 5.

    I have tried suubsequently to make the missing model “General”, but then I’m missing the GeneralController.

    Please help!

    regards/Leif

    1. Where did the error appear? Please send me the error massage and I’ll take a look. I don’t think there is a GeneralController, there is only an IndexController for the home page.

      1. I use Voyager and not Nova. It looks like all controllers are meant for use by Nova.
        Eg. in Lesson 7 – IndexController:

        use App \ Models \ General;

        and

        public function __invoke ($ slug)
        {
        // get the general information about the website
        $ website = General :: query () -> firstOrFail ();

        Reference is made to the model General, but model General was created in lesson 5, where users of Voyager do not have to do anything and therefore model General is not created!

        Error message:

        Error
        Class ‘App \ Models \ General’ not found

        I hope I have expressed myself fairly understandably.

        Thanks for the help!

        1. I think I forgot to mention that you can delete the line use App\Models\General; if you are using Voyager. It’s not necessary because there is a built-in section for basic website information. Thank you for noticing.

          1. I’m sorry, but I’m a complete noob.

            The variable $website=General::query()->firstOrFail(), but as I’m using Voyager “General” is not defined, and I get the error:

            Class ‘App\Models\General’ not found

            How can I solve this?

            1. Yes, it has an individual view. You can refer to the “layout” section of this article. In this project, I think I put the post-list view under the “vendor” folder.

Leave a Reply

Your email address will not be published. Required fields are marked *