Vue Tutorial: Setup Laravel Side Application

Please note that this post may contain affiliate links, and for every purchase you make, at no extra cost to you, a commission fee will be rewarded to me.

The first thing we need to do is to quickly go through the Laravel side of our application, and we will use the same setup in our entire tutorial.

Model & Migration Files

Site Info Model

Remember, if you are using Voyager as the Admin Panel like we talked about in the Laravel Tutorial For Beginners, you do not need to make a model for the website’s general information. So you can skip to the next part.

Migration File:

public function up()
{
    Schema::create('sites', function (Blueprint $table) {
        $table->id();
        $table->char('name', 150);
        $table->string('favicon');
        $table->text('description');
        $table->timestamps();
    });
}

Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class General extends Model
{
    protected $fillable = [
        'name',
        'description',
        'favicon',
    ];
}

Tool Model

Migration File:

public function up()
{
    Schema::create('tools', function (Blueprint $table) {
        $table->id();
        $table->char('name', 200);
        $table->char('slug', 200)->unique();
        $table->string('icon');
        $table->text('introduction')->nullable();
        $table->text('tutorial')->nullable();
        $table->boolean('is_featured');
        $table->timestamps();
    });
}

Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tool extends Model
{
    protected $fillable = [
        'name',
        'slug',
        'icon',
        'introduction',
        'tutorial',
        'is_featured',
    ];
}

Controllers

Index Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Site;
use App\Tool;

class IndexController extends Controller
{
    public function home(){

        $site = Site::query()->find(1);
        $featured_tool = Tool::query()->where('is_featured', true)->inRandomOrder()->first();
        $tools = Tool::all();

        return view('home', [
            'site' => $site,
            'featured_tool' => $featured_tool,
            'tools' => $tools,
        ]);

    }
}

Tool Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Site;
use App\Tool;

class ToolController extends Controller
{
    public function show($slug)
    {
        $site = Site::query()->find(1);
        $tool = Tool::query()->where('slug', $slug)->first();

        return view('tool', [
            'site' => $site,
            'tool' => $tool,
        ]);
    }
}

Views

Layout

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="author" content="Eric Hu">
    <link rel="icon" type="image/png" href="{{\Illuminate\Support\Facades\Storage::url($site->favicon)}}"/>

@yield('meta')

<!--
    <meta name="description" content="">
    <title>Starter Template · Bootstrap</title>
    -->

    <!-- CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
          integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

    <style>
        .display-4 {
            font-size: 2.5rem;
        }

        @media (min-width: 768px) {
            .display-4 {
                font-size: 3rem;
            }
        }

        main {
            margin-top: 90px;
        }

        h1, h2, h3, h4, h5, h6 {
            font-family: "Playfair Display", Georgia, "Times New Roman", serif;
        }

        .tutorial-content {
            font-family: Lucida Console, Courier, monospace;
        }

        .tool-link:link, .tool-link:visited, .tool-link:hover, .tool-link:active {
            color: black;
        }

        .tool-name {
            font-family: "Playfair Display", Georgia, "Times New Roman", serif;
            font-size: 1.3em;
            max-width: 50%;
            position: absolute;
            top: 50%;
            transform: translate(0, -50%);
            word-wrap: normal;
        }

        .tool-category {
            margin-bottom: 20px;
            text-decoration: underline;
        }

        .footer {
            padding: 2.5rem 0;
            color: #999;
            text-align: center;
            background-color: #f9f9f9;
            border-top: .05rem solid #e5e5e5;
        }

        .footer p:last-child {
            margin-bottom: 0;
        }

    </style>
</head>
<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container">
        <a class="navbar-brand" href="/">{{$site->name}}</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExample07"
                aria-controls="navbarsExample07" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarsExample07">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="#">Blog</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
            </ul>

            <form class="form-inline my-2 my-md-0">
                <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-light my-2 my-sm-0" type="submit">Search</button>
            </form>
        </div>
    </div>
</nav>

<main role="main" class="container">

    @yield('content')

</main><!-- /.container -->

<footer class="footer">
    <p>eToolbox built by <a href="#">Eric Hu</a>.</p>
    <p>
        <a href="#">Back to top</a>
    </p>
</footer>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
        integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
        crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
        integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
        crossorigin="anonymous"></script>
<script src="https://kit.fontawesome.com/1b4c11d9c2.js" crossorigin="anonymous"></script>
<script src="{{asset('js/app.js')}}"></script>
</body>

</html>

Index View

@extends('layout')

@section('meta')
    <meta name="description" content="{{$site->description}}">
    <title>{{$site->name}}</title>
@endsection

@section('content')

    <div class="jumbotron p-4 p-md-5 text-white rounded bg-dark">
        <div class="col-md-6 px-0">
            <h1 class="display-4 font-italic">{{$featured_tool->name}}</h1>
            <p class="lead my-3">{{\Illuminate\Support\Str::words($featured_tool->introduction, 25, '...')}}</p>
            <p class="lead mb-0"><a href="/tools/{{$featured_tool->slug}}"
                                    class="text-white font-weight-bold">Visit {{$featured_tool->name}}</a></p>
        </div>
    </div>

    <div class="row mb-2">
        @foreach($tools as $tool)
            <div class="col-md-4">
                <a href="/tools/{{$tool->slug}}" class="tool-link">
                    <div
                        class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
                        <div class="col p-4 d-flex flex-column position-static">
                            <div class="mb-0 tool-name">{{$tool->name}}</div>
                        </div>
                        <div class="col-auto d-none d-lg-block">
                            <img class="bd-placeholder-img"
                                 src="{{\Illuminate\Support\Facades\Storage::url($tool->icon)}}" width="150"
                                 height="150" style="object-fit: cover">
                        </div>
                    </div>
                </a>
            </div>
        @endforeach
    </div>
@endsection

Tool View

@extends('layout')

@section('meta')
    <meta name="description" content="{{$tool->introduction}}">
    <title>{{$tool->name}} | {{$site->name}}</title>
@endsection

@section('content')
    <div class="row">
        <div class="col-lg-3">
            <h1 class="my-4">{{$site->name}}</h1>
            <div class="card my-4">
                <h5 class="card-header">Sidebar Widget</h5>
                <div class="card-body">

                </div>
            </div>
        </div>
        <!-- /.col-lg-3 -->

        <div class="col-lg-9">
            <div class="card mt-4">
                <div class="card-body">
                    <h2 class="card-title">{{$tool->name}}</h2>
                    @if($tool->slug === 'unit-converter')
                        <div class="mx-auto" id="app">

                        </div>
                    @endif
                </div>
            </div>
            <!-- /.card -->
            <div class="card card-outline-secondary my-4">
                <h2 class="card-header">
                    How to use the {{\Illuminate\Support\Str::lower($tool->name)}}.
                </h2>
                <div class="card-body">
                    <div class="tutorial-content">
                        {!! $tool->tutorial !!}
                    </div>
                </div>
            </div>
            <!-- /.card -->
        </div>
        <!-- /.col-lg-9 -->
    </div>
@endsection

URL Structure

Route::get('/', 'IndexController@home');
Route::get('/tools/{slug}', 'ToolController@show');

I did not explain the specifics of the code here, but if you are interested, check out the Laravel Tutorials For Beginners for details.

Leave a Reply

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