Build a naïve Blog with Laravel

Build a naïve Blog with Laravel #

项目 #

基于springboot的个人博客系统

知识点 #

实训计划 #

时间

知识点

进度

day0

需求宣讲、分组、数据库设计

确认项目需求、完成数据库设计

day1

springboot通用开发流程

确认项目UI、完成登录与注册开发

day2

spring interceptor开发

完成登录的interceptor开发

day3-4

layui+simeditor

完成博客撰写页\完成首页及详情页

day5-6

图片上传组件 及解决方案

完成相册页

day7

其他知识点提示

集成各功能

For log propose, do not follow steps below in production project.

From 2019/01/04 ~ 2019/01/11 ChinaSofti software development, the final goal is to design a blog system.

The whole system can be describe as below:

User Consideration #

The Admin User will be the only person has the permission to Add/Edit/Delete Post, Add/Edit/Delete Post, the normal user has only the permission to post comment.

Comment Consideration #

The Comments has no in-line reply function as the comments for one post is just listed at date ascend.(Single foreign key referring the post’s primary key.)

Post Consideration #

For better editing the pages, and the support rich-content requisite, Markdown is used to store post content, on-the-fly Markdown to HTML rendering is needed for displaying the content for end user.

The primary key for post will be UUID (without slashes), so the post URL style will look like : /posts/ad65b02b1e6c4ea1ba7af94f69aeec1f/ which is way better than the /posts/2.

For this need, we need to import the UUID repo, as below:

composer require webpatser/laravel-uuid

Since our DB stores the Markdown data, we will need to use a Class to convert Markdown formatted text to HTML for front-end rendering.

composer require graham-campbell/markdown

Register them in config/app.php :

'Uuid' => Webpatser\\Uuid\\Uuid::class,
'Markdown' => GrahamCampbell\\Markdown\\Facades\\Markdown::class,

In related controllers(such as app/Http/Controllers/PostController.php), use the needed package:

use GrahamCampbell\\Markdown\\Facades\\Markdown;

Init the Laravel Project #

laravel new laralog

and fill it with default user authentication system.

php artisan make:auth

then use php artisan serve for open a dev server at localhost:8000, this should look like as below.

Routing #

Since this is only a blog, we don’t need specified routing system, just hard-code the page route in route.php and the rest of the posts can be accessed by /posts/{post_id} which should be controlled by PostController.

As CRUD for blog post, we define the following RESTful design pattern.

Method

URL

Action

GET

/posts/{post_uuid}

Display the specified post content.

GET

/posts/{post_uuid}/edit

Display the editor for specified post.

DELETE

/posts/{post_uuid}

Delete the specified post.(Soft Delete)

POST

/posts

Create a post.

PUT

/posts/{post_uuid}

As an interface for receiving JSON formatted post content.

We define the post route here, let’s create PostController first with php artisan make:controller PostController:

Route::post('/posts/{post\_uuid}','PostController@create\_post');
Route::get('/posts/{post\_uuid}','PostController@get\_post');
Route::get('/posts/{post\_uuid}/edit','PostController@get\_post\_edit');
Route::put('/posts/{post\_uuid}','PostController@update\_post');
Route::delete('/posts/{post\_uuid}','PostController@delete\_post');

Pages routing will be hard-coded (About page as example):

Route::get('/about','PageController@index');

In app/Http/Controllers/PostController.php:

    public function index()
    {
        return view('about');
    }

Build User Seed along with some DB tables #

User migration, user should login with email and password:

        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('role')->default("user");
            $table->string('email')->unique();
            $table->timestamp('email\_verified\_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

Create the Model with Post migration:

php artisan make:model Post
php artisan make:migration create\_blog\_post

Post Model (app/Post.php):

class Post extends Model
{
    protected $table = 'post';
}

Post migration(database/migrations/2019_01_05_014913_create_blog_post.php):

      Schema::create('posts', function (Blueprint $table) {
            $table->primary('post\_uuid');
            $table->uuid('post\_uuid');
            $table->string('post\_title');
            $table->string('post\_type')->default('post');
            $table->mediumText('post\_content');
            $table->timestamps();
        });

Create Post Seeder:

php artisan make:seeder PostSeeder

In database/seeds/PostSeeder.php:

    public function run()
    {
        for ($i=1; $i < 10; $i++) {
          DB::table('posts')->insert(\[
          'post\_uuid' => (string) Uuid::generate(4),
          'post\_title' => str\_random(20),
          'post\_type' => "post",
          'post\_content' => str\_random(300),
        \]);
        }
        DB::table('posts')->insert(\[
        'post\_uuid' => (string) Uuid::generate(4),
        'post\_title' => str\_random(20),
        'post\_type' => "gallery",
        'post\_content' => "!\[bird-3732867\_1920.jpg\](https://i.loli.net/2019/01/07/5c32efd9098c5.jpg)",
      \]);
    }

Since there is only one Admin, we will need to insert the Admin user on migration seed section:

php artisan make:seeder UserSeeder

in database/seeds/UserSeeder.php we define our Admin user here:

    public function run()
    {
        DB::table('users')->insert(\[
        'name' => "Nova Kwok",
        'role' => "admin",
        'email' => "[email protected]",
        'password' => bcrypt("opennova"),
      \]);
    }

Here comes the comment part, as I mentioned above, Post -> Comment has a One-To-Many relation, there is a need to declare the “foreign key” in the migration file, in database/migrations/2019_01_06_060322_create_comments.php.

        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->uuid('commented\_on');
            $table->string('commented\_by');
            $table->mediumText('comment\_content');
            $table->timestamps();
        });

Run the seeds to seed the DB:

php artisan db:seed --class=UserSeeder
php artisan db:seed --class=PostSeeder

Index Page Blade #

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">


          @foreach ($all\_posts as $key => $post)
            <div class="card">
                <div class="card-header"><a href="/posts/{{ $post->post\_uuid }}">{{ $post->post\_title }}</a></div>
                <div class="card-body">
                    {{ $post->post\_content }}
                </div>
            </div>
            <br>
          @endforeach

        </div>
        <div class="col-md-4">
          <div class="card">
              <div class="card-header">TukiBlog</div>
              <div class="card-body">
                  <ul>
                    <li><a href="/login">Login</a></li>
                    <li><a href="/register">Register</a></li>
                    <li><a href="https://nova.moe">Nova Kwok's Blog</a></li>
                    <li><a href="htt\[ps://keshane.moe">Keshane's Blog</a></li>
                  </ul>
              </div>
          </div>
          <br>
          <div class="card">
              <div class="card-header">Pages</div>
              <div class="card-body">
                  <ul>
                    <li><a href="/about">About</a></li>
                  </ul>
              </div>
          </div>
          <br>
          <div class="card">
              <div class="card-header">Pages</div>
              <div class="card-body">
                  <ul>
                    <li><a href="/about">About</a></li>
                  </ul>
              </div>
          </div>
        </div>
    </div>
</div>
@endsection

Adding the Post_edit Page with Editor #

HTML Part:

          <div class="input-group">
            <div class="input-group-prepend">
              <span class="input-group-text" id="">Post Title</span>
            </div>
            <input type="text" name="post\_title" class="form-control" value="{{ $post->post\_title }}">
          </div>
          <br>

            <div class="card">
                <div class="card-body">
                  <small>The Post URL will be: <a href="/posts/{{ $post->post\_uuid }}">/posts/{{ $post->post\_uuid }}</a> </small>

                  <form class="" action="/posts/{{ $post->post\_uuid }}" method="POST" enctype="multipart/form-data">
                      @csrf
                      @method('put')
                        <textarea id="md-text" name="post\_content" rows="8">{{ $post->post\_content }}</textarea>
                        <div class="row justify-content-center">
                            <div class="col-lg-6 col-sm-6 col-xs-12">
                                <button class="btn btn-block btn-raised btn-success pull-right" input type="submit">Edit</button>
                            </div>
                      </div>
                  </form>

                </div>
            </div>

Adding the SimpleMDE Editor:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.js"></script>
  <script type="text/javascript">
    var simplemde = new SimpleMDE({
      element: document.getElementById("md-text"),
      toolbar: \["table", "heading", "|", "preview"\]
    });
</script>

Defining the Middleware & Securing the Route #

Create a middleware as guard for guarding the Admin Panel.

php artisan make:middleware IsAdmin

In app/Http/Middleware/IsAdmin.php

    public function handle($request, Closure $next)
    {
        $role = auth()->user()->role;
        if($role != "admin")
        {
            return response()->view('errors.403');
        }
        return $next($request);
    }

And register it in the Kernel(app/Http/Kernel.php):

    protected $routeMiddleware = \[
        'auth' => \\App\\Http\\Middleware\\Authenticate::class,
        ...
        'admin' => \\App\\Http\\Middleware\\IsAdmin::class,
    \];

Applying Middleware to the protected route (routes/web.php) so when a non-admin user tries to visit protected page will meet an 403 error.

Route::get('/posts/{post\_uuid}/edit','PostController@get\_post\_edit')->middleware('auth')->middleware('admin');

Pagination #

Laravel has provide a simple pagination function, we define pagination in the index function:

    public function index($page = 1)
    {
        $all\_posts = Post::paginate(5);
        $post = Post::paginate(5);
        $pre\_url =  $post->previousPageUrl();
        $next\_url =  $post->nextPageUrl();
        return view('index')
        ->with('pre\_url',$pre\_url)
        ->with('next\_url',$next\_url)
        ->with('all\_posts',$all\_posts);
    }

And in the resources/views/index.blade.php, we define the following front-end.

          <nav>
            <ul class="pagination">
              <li class="page-item"><a class="page-link" href="{{ $pre\_url }}">Previous</a></li>
              <li class="page-item"><a class="page-link" href="{{ $next\_url }}">Next</a></li>
            </ul>
          </nav>