用 10 分鐘在 Cloud9 開發一個 Laravel 5 的 Blog

Cloud9是一個雲端Web開發平台,它提供了一些開發環境給我們(例如安裝了PHP、Ruby、Python等),讓我們省卻配置伺服器及開發環境的時間,可以立即開發Web App。Cloud9 是免費的,每個開發環境有1個CPU、512MB RAM 及 2GB HDD,對小型 project 是夠用的,很適合用來學習各種 Web App 的開發。
Cloud9 IDE是一個免費的在線集成開發環境,支持四十多種程式語言,例如 PHP、Ruby、Python、Go、JavaScript(包括Node.js)等。它可以使開發者在一個事先安裝好的環境中快速進入開發、與其他人協作並且在Web開發中做到實時預覽和瀏覽器兼容性測試等。

- Wiki

我們會用一個熱門的 PHP 框架 - Laravel(版本:5.2),在 Cloud9 建立一個簡單的 Blog 網站應用程式,所以請先到 Cloud9 註冊戶口。 開完戶口就可以立即建立開發環境

選擇開發環境

等待 Cloud9 建立開發環境

建立完畢便進入了我們的開發環境


安裝 Laravel 5

接下來會安裝最熱門的 PHP Framework - Laravel 5

在 Terminal 輸入以下指令(可以一次過複製以下指令並在 Terminal 貼上)
rm README.md php.ini hello-world.php
sudo composer self-update
composer create-project laravel/laravel ./laravel --prefer-dist
shopt -s dotglob
mv laravel/* ./
rm -rf laravel
因為 Laravel 會提供 public 資料夾 的檔案給 Client,所以我們要(透過一個文字編緝器nano)修改 Apache Config
sudo nano /etc/apache2/sites-enabled/001-cloud9.conf
DocumentRoot /home/ubuntu/workspace
改為
DocumentRoot /home/ubuntu/workspace/public
改完請按 F2 儲存,然後 'Y' 及 'Enter'。

最後是取得最新版本的 Laravel(筆者安裝的版本是 5.2)
sudo composer update


至此 Laravel 5 已成功安裝,看看左邊已經多了 Laravel framework 的檔案


終於可以正式開發網誌網頁應用程式了。
請注意,本系列教學只會以最精簡的方式列出完成開發時必要的步驟,並在適當的地方加插圖片輔助,希望讀者能在最短時間親自完成教學。因此,當中的說明或解釋將會省略,如欲了解,請參考原文 Simple blog application in laravel 5。 這個日誌會有以下功能:
1. 任何人都可登入/註冊
2. 用戶可分類為「admin」、「author」或「subscriber」
3. 「author」可以撰寫/更新/刪除帖子
4. 「admin」擁有網站全部存取權限及可以閱讀/撰寫/更新/刪除任何帖子
5. 任何人都可閱讀帖子
6. 用戶可以(登入後)在帖子內留言

完成品如下圖所示,可到示範網站參看:


這個教學將分作 5 部份:

1) Setup database
- Connect with mysql database
- Create posts and comments tables

2) Make routes and models
- Routes
- Create post model
- Create comment model
- Create User model

3) Controllers
- PostController
- CommentController

4) Build front end
- Customize app.blade.php
- make home view
- Create posts
- Show posts
- Edit posts

5) Add tinymce and Make user profile
- Add tinymce to posts
- Make profile backend
- View for profile

Part 1: 建立數據庫

首先我們會建立數據庫表格用作儲存資料,當中會用到 migrations 及 schema builder。 建立數據庫表格前要先配置好連接數據庫的資訊,請在應用程式的根目錄(root)找到 .env (.env是一個隱藏檔案),並更新連接數據庫的資訊
DB_HOST=localhost
DB_DATABASE=your_database_name
DB_USERNAME=database_username
DB_PASSWORD=database_password
Cloud9用戶可參考以下配置,只需把 DB_USERNAME 改為你的登入名稱就可以:
DB_HOST=localhost
DB_DATABASE=c9
DB_USERNAME=amzfree
DB_PASSWORD=
到 blog >> config >> database.php , 找到 default, 改為
'default' => env('DB_CONNECTION', 'mysql'),
改完請儲存所有檔案 在 Terminal 輸入以下程式碼安裝 MySQL:
mysql-ctl install
或者輸入以下程式碼安裝 MySQL:
mysql-ctl cli
安裝完畢後退出 MySQL
exit
現在用 migrations
php artisan make:migration posts
php artisan make:migration comments
留意 blog >> database >> migrations 多了 2 個檔案

建立 posts 表格

打開 建立時間_posts.php,清除所有程式碼並貼上以下(用作在數據庫建立一個名為 posts 的表格的)程式碼
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Posts extends Migration {
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    // blog table
    Schema::create('posts', function(Blueprint $table)
    {
      $table->increments('id');
      $table -> integer('author_id') -> unsigned() -> default(0);
      $table->foreign('author_id')
          ->references('id')->on('users')
          ->onDelete('cascade');
      $table->string('title')->unique();
      $table->text('body');
      $table->string('slug')->unique();
      $table->boolean('active');
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    // drop blog table
    Schema::drop('posts');
  }
}
Migration 的作用是數據庫版本控制(database version control),原理與程式碼版本控制一樣,Migration 有 up() 和 down() 2 個 function,migration 執行的時候會執行up(),down() 則是 up() 的相反,還原 up() 所做過的。例如這裡的 up() 我們 create 了 posts 的 table ,down() 的時候便要把 posts 這個 table drop 走。 Schema 是另一個在建立/修改數據庫表格會用到的Class,create() function 有 2 個 arguments,第 1 個是數據庫名稱,另一個是開 column 的 closure。$table 是這個表格的 instance 。

建立 comments 表格

同樣地將以下程式碼貼到 建立時間_comments.php
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Comments extends Migration {
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    //id, on_blog, from_user, body, at_time
    Schema::create('comments', function(Blueprint $table)
    {
      $table->increments('id');
      $table -> integer('on_post') -> unsigned() -> default(0);
      $table->foreign('on_post')
          ->references('id')->on('posts')
          ->onDelete('cascade');
      $table -> integer('from_user') -> unsigned() -> default(0);
      $table->foreign('from_user')
          ->references('id')->on('users')
          ->onDelete('cascade');
      $table->text('body');
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    // drop comment
    Schema::drop('comments');
  }
}

User 表格:

User 表格的 migration 已經存在,但我們想增加用戶身份角色,所以要加多一行:
$table->string('password', 60);
// add this one line
$table->enum('role',['admin','author','subscriber'])->default('author');
$table->rememberToken();
儲存所有更改 最後是 generate 表格,在 Terminal 輸入:
php artisan migrate
數據庫設置完成

Part 2: Route 路由控制 與 Model

現在是管理 blog 的路徑,我們會用 Route 類別將 url 與 Controller 聯繫,Route 類別有 get(), post(), delete(), put(), all() 及 match() 共6個 function,我們只會用到 get() 和 post()。把 app/Http/routes.php 改成以下程式碼:
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
Route::get('/','PostController@index');
Route::get('/home',['as' => 'home', 'uses' => 'PostController@index']);
//authentication
Route::controllers([
 'auth' => 'Auth\AuthController',
 'password' => 'Auth\PasswordController',
]);
// check for logged in user
Route::group(['middleware' => ['auth']], function()
{
 // show new post form
 Route::get('new-post','PostController@create');
 // save new post
 Route::post('new-post','PostController@store');
 // edit post form
 Route::get('edit/{slug}','PostController@edit');
 // update post
 Route::post('update','PostController@update');
 // delete post
 Route::get('delete/{id}','PostController@destroy');
 // display user's all posts
 Route::get('my-all-posts','UserController@user_posts_all');
 // display user's drafts
 Route::get('my-drafts','UserController@user_posts_draft');
 // add comment
 Route::post('comment/add','CommentController@store');
 // delete comment
 Route::post('comment/delete/{id}','CommentController@distroy');
});
//users profile
Route::get('user/{id}','UserController@profile')->where('id', '[0-9]+');
// display list of posts
Route::get('user/{id}/posts','UserController@user_posts')->where('id', '[0-9]+');
// display single post
Route::get('/{slug}',['as' => 'post', 'uses' => 'PostController@show'])->where('slug', '[A-Za-z0-9-_]+');

建立 Model

1. Posts

現在會建立 Model。在 app 資料夾新增 2 個檔案:Posts.php 及 Comments.php。在 Posts.php 貼上以下程式碼:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
// instance of Posts class will refer to posts table in database
class Posts extends Model {
  //restricts columns from modifying
  protected $guarded = [];
  // posts has many comments
  // returns all comments on that post
  public function comments()
  {
    return $this->hasMany('App\Comments','on_post');
  }
  // returns the instance of the user who is author of that post
  public function author()
  {
    return $this->belongsTo('App\User','author_id');
  }
}

2) Comments

同樣原理在 Comments.php 貼上以下程式碼:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Comments extends Model {
  //comments table in database
  protected $guarded = [];
  // user who has commented
  public function author()
  {
    return $this->belongsTo('App\User','from_user');
  }
  // returns post of any comment
  public function post()
  {
    return $this->belongsTo('App\Posts','on_post');
  }
}

3) User

把 app/User.php 改成:
<?php namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
  use Authenticatable, CanResetPassword;
  /**
   * The database table used by the model.
   *
   * @var string
   */
  protected $table = 'users';
  /**
   * The attributes that are mass assignable.
   *
   * @var array
   */
  protected $fillable = ['name', 'email', 'password'];
  /**
   * The attributes excluded from the model's JSON form.
   *
   * @var array
   */
  protected $hidden = ['password', 'remember_token'];
  // user has many posts
  public function posts()
  {
    return $this->hasMany('App\Posts','author_id');
  }
  // user has many comments
  public function comments()
  {
    return $this->hasMany('App\Comments','from_user');
  }
  public function can_post()
  {
    $role = $this->role;
    if($role == 'author' || $role == 'admin')
    {
      return true;
    }
    return false;
  }
  public function is_admin()
  {
    $role = $this->role;
    if($role == 'admin')
    {
      return true;
    }
    return false;
  }
}

Part 3: Controllers

我們可用以下指令讓 Artisan 幫助我們自動產生 controllers:
php artisan make:controller UserController
php artisan make:controller PostController
php artisan make:controller CommentController
這些指令會在 app/Http/Controllers 產生 3 個檔案:UserController.php、PostController.php 及 CommentController.php

PostController

:
<?php namespace App\Http\Controllers;
use App\Posts;
use App\User;
use Redirect;
use App\Http\Controllers\Controller;
use App\Http\Requests\PostFormRequest;
use Illuminate\Http\Request;
class PostController extends Controller {

// 顯示所有文章
public function index()
  {
    //fetch 5 posts from database which are active and latest
    $posts = Posts::where('active',1)->orderBy('created_at','desc')->paginate(5);
    //page heading
    $title = 'Latest Posts';
    //return home.blade.php template from resources/views folder
    return view('home')->withPosts($posts)->withTitle($title);
  }

// 建立新文章
public function create(Request $request)
  {
    // if user can post i.e. user is admin or author
    if($request->user()->can_post())
    {
      return view('posts.create');
    }    
    else 
    {
      return redirect('/')->withErrors('You have not sufficient permissions for writing post');
    }
  }

// 儲存文章
public function store(PostFormRequest $request)
  {
    $post = new Posts();
    $post->title = $request->get('title');
    $post->body = $request->get('body');
    $post->slug = str_slug($post->title);
    $post->author_id = $request->user()->id;
    if($request->has('save'))
    {
      $post->active = 0;
      $message = 'Post saved successfully';            
    }            
    else 
    {
      $post->active = 1;
      $message = 'Post published successfully';
    }
    $post->save();
    return redirect('edit/'.$post->slug)->withMessage($message);
  }

// 顯示一篇文章及它的留言
public function show($slug)
  {
    $post = Posts::where('slug',$slug)->first();
    if(!$post)
    {
       return redirect('/')->withErrors('requested page not found');
    }
    $comments = $post->comments;
    return view('posts.show')->withPost($post)->withComments($comments);
  }

// 編輯文章
public function edit(Request $request,$slug)
  {
    $post = Posts::where('slug',$slug)->first();
    if($post && ($request->user()->id == $post->author_id || $request->user()->is_admin()))
      return view('posts.edit')->with('post',$post);
    return redirect('/')->withErrors('you have not sufficient permissions');
  }

// 更新資料庫的文章
public function update(Request $request)
  {
    //
    $post_id = $request->input('post_id');
    $post = Posts::find($post_id);
    if($post && ($post->author_id == $request->user()->id || $request->user()->is_admin()))
    {
      $title = $request->input('title');
      $slug = str_slug($title);
      $duplicate = Posts::where('slug',$slug)->first();
      if($duplicate)
      {
        if($duplicate->id != $post_id)
        {
          return redirect('edit/'.$post->slug)->withErrors('Title already exists.')->withInput();
        }
        else 
        {
          $post->slug = $slug;
        }
      }
      $post->title = $title;
      $post->body = $request->input('body');
      if($request->has('save'))
      {
        $post->active = 0;
        $message = 'Post saved successfully';
        $landing = 'edit/'.$post->slug;
      }            
      else {
        $post->active = 1;
        $message = 'Post updated successfully';
        $landing = $post->slug;
      }
      $post->save();
           return redirect($landing)->withMessage($message);
    }
    else
    {
      return redirect('/')->withErrors('you have not sufficient permissions');
    }
  }

// 刪除文章
public function destroy(Request $request, $id)
  {
    //
    $post = Posts::find($id);
    if($post && ($post->author_id == $request->user()->id || $request->user()->is_admin()))
    {
      $post->delete();
      $data['message'] = 'Post deleted Successfully';
    }
    else 
    {
      $data['errors'] = 'Invalid Operation. You have not sufficient permissions';
    }
    return redirect('/')->with($data);
  }

CommentController:

<?php namespace App\Http\Controllers;
use App\Posts;
use App\Comments;
use Redirect;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class CommentController extends Controller {
  public function store(Request $request)
  {
    //on_post, from_user, body
    $input['from_user'] = $request->user()->id;
    $input['on_post'] = $request->input('on_post');
    $input['body'] = $request->input('body');
    $slug = $request->input('slug');
    Comments::create( $input );
    return redirect($slug)->with('message', 'Comment published');     
  }
}
UserController 會稍後完成

Part 4: 前端網頁

之前主要完成了日誌的後端程式,現在會完成前端版面設計。

把 resources/views/app.blade.php 改成

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Blog Demo | Find All Together</title>
    <link href="{{ asset('/css/app.css') }}" rel="stylesheet">
    <!-- Fonts -->
    <link href='//fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet' type='text/css'>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
            <span class="sr-only">Toggle Navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="http://www.findalltogether.com">Find All Together</a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li>
              <a href="{{ url('/') }}">Home</a>
            </li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            @if (Auth::guest())
            <li>
              <a href="{{ url('/auth/login') }}">Login</a>
            </li>
            <li>
              <a href="{{ url('/auth/register') }}">Register</a>
            </li>
            @else
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>
              <ul class="dropdown-menu" role="menu">
                @if (Auth::user()->can_post())
                <li>
                  <a href="{{ url('/new-post') }}">Add new post</a>
                </li>
                <li>
                  <a href="{{ url('/user/'.Auth::id().'/posts') }}">My Posts</a>
                </li>
                @endif
                <li>
                  <a href="{{ url('/user/'.Auth::id()) }}">My Profile</a>
                </li>
                <li>
                  <a href="{{ url('/auth/logout') }}">Logout</a>
                </li>
              </ul>
            </li>
            @endif
          </ul>
        </div>
      </div>
    </nav>
    <div class="container">
      @if (Session::has('message'))
      <div class="flash alert-info">
        <p class="panel-body">
          {{ Session::get('message') }}
        </p>
      </div>
      @endif
      @if ($errors->any())
      <div class='flash alert-danger'>
        <ul class="panel-body">
          @foreach ( $errors->all() as $error )
          <li>
            {{ $error }}
          </li>
          @endforeach
        </ul>
      </div>
      @endif
      <div class="row">
        <div class="col-md-10 col-md-offset-1">
          <div class="panel panel-default">
            <div class="panel-heading">
              <h2>@yield('title')</h2>
              @yield('title-meta')
            </div>
            <div class="panel-body">
              @yield('content')
            </div>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-10 col-md-offset-1">
          <p>Copyright © 2015 | <a href="http://www.findalltogether.com">Find All Together</a></p>
        </div>
      </div>
    </div>
    <!-- Scripts -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
  </body>
</html>

首頁版面 home.blade.php :

@extends('app')
@section('title')
{{$title}}
@endsection
@section('content')
@if ( !$posts->count() )
There is no post till now. Login and write a new post now!!!
@else
<div class="">
  @foreach( $posts as $post )
  <div class="list-group">
    <div class="list-group-item">
      <h3><a href="{{ url('/'.$post->slug) }}">{{ $post->title }}</a>
        @if(!Auth::guest() && ($post->author_id == Auth::user()->id || Auth::user()->is_admin()))
          @if($post->active == '1')
          <button class="btn" style="float: right"><a href="{{ url('edit/'.$post->slug)}}">Edit Post</a></button>
          @else
          <button class="btn" style="float: right"><a href="{{ url('edit/'.$post->slug)}}">Edit Draft</a></button>
          @endif
        @endif
      </h3>
      <p>{{ $post->created_at->format('M d,Y \a\t h:i a') }} By <a href="{{ url('/user/'.$post->author_id)}}">{{ $post->author->name }}</a></p>
    </div>
    <div class="list-group-item">
      <article>
        {!! str_limit($post->body, $limit = 1500, $end = '....... <a href='.url("/".$post->slug).'>Read More</a>') !!}
      </article>
    </div>
  </div>
  @endforeach
  {!! $posts->render() !!}
</div>
@endif
@endsection
在 resources/views 新增一個 posts 資料夾

在 resources/views/posts 建立一個新文件 create.blade.php

@extends('app')
@section('title')
Add New Post
@endsection
@section('content')
<form action="/new-post" method="post">
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <div class="form-group">
    <input required="required" value="{{ old('title') }}" placeholder="Enter title here" type="text" name = "title"class="form-control" />
  </div>
  <div class="form-group">
    <textarea name='body'class="form-control">{{ old('body') }}</textarea>
  </div>
  <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  <input type="submit" name='save' class="btn btn-default" value = "Save Draft" />
</form>
@endsection

在 resources/views/posts 建立另一個新文件 show.blade.php

@extends('app')
@section('title')
  @if($post)
    {{ $post->title }}
    @if(!Auth::guest() && ($post->author_id == Auth::user()->id || Auth::user()->is_admin()))
      <button class="btn" style="float: right"><a href="{{ url('edit/'.$post->slug)}}">Edit Post</a></button>
    @endif
  @else
    Page does not exist
  @endif
@endsection
@section('title-meta')
<p>{{ $post->created_at->format('M d,Y \a\t h:i a') }} By <a href="{{ url('/user/'.$post->author_id)}}">{{ $post->author->name }}</a></p>
@endsection
@section('content')
@if($post)
  <div>
    {!! $post->body !!}
  </div>    
  <div>
    <h2>Leave a comment</h2>
  </div>
  @if(Auth::guest())
    <p>Login to Comment</p>
  @else
    <div class="panel-body">
      <form method="post" action="/comment/add">
        <input type="hidden" name="_token" value="{{ csrf_token() }}">
        <input type="hidden" name="on_post" value="{{ $post->id }}">
        <input type="hidden" name="slug" value="{{ $post->slug }}">
        <div class="form-group">
          <textarea required="required" placeholder="Enter comment here" name = "body" class="form-control"></textarea>
        </div>
        <input type="submit" name='post_comment' class="btn btn-success" value = "Post"/>
      </form>
    </div>
  @endif
  <div>
    @if($comments)
    <ul style="list-style: none; padding: 0">
      @foreach($comments as $comment)
        <li class="panel-body">
          <div class="list-group">
            <div class="list-group-item">
              <h3>{{ $comment->author->name }}</h3>
              <p>{{ $comment->created_at->format('M d,Y \a\t h:i a') }}</p>
            </div>
            <div class="list-group-item">
              <p>{{ $comment->body }}</p>
            </div>
          </div>
        </li>
      @endforeach
    </ul>
    @endif
  </div>
@else
404 error
@endif
@endsection

resources/views/posts/edit.blade.php

@extends('app')
@section('title')
Edit Post
@endsection
@section('content')
<form method="post" action='{{ url("/update") }}'>
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <input type="hidden" name="post_id" value="{{ $post->id }}{{ old('post_id') }}">
  <div class="form-group">
    <input required="required" placeholder="Enter title here" type="text" name = "title" class="form-control" value="@if(!old('title')){{$post->title}}@endif{{ old('title') }}"/>
  </div>
  <div class="form-group">
    <textarea name='body'class="form-control">
      @if(!old('body'))
      {!! $post->body !!}
      @endif
      {!! old('body') !!}
    </textarea>
  </div>
  @if($post->active == '1')
  <input type="submit" name='publish' class="btn btn-success" value = "Update"/>
  @else
  <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  @endif
  <input type="submit" name='save' class="btn btn-default" value = "Save As Draft" />
  <a href="{{  url('delete/'.$post->id.'?_token='.csrf_token()) }}" class="btn btn-danger">Delete</a>
</form>
@endsection

Part 5: 增添 tinyMCE 及 用戶個人檔案

這個系列最後一篇教學是在建立文章的版面嵌入 tinyMCE(所見即所得編輯器)及完成用戶個人檔案的前後端。

增添 tinyMCE

http://www.tinymce.com/download/download.php 下載 tinyMCE 共解壓縮至 public/js 把 resources/views/posts/edit.blade.php 改成
@extends('app')
@section('title')
Edit Post
@endsection
@section('content')
<script type="text/javascript" src="{{ asset('/js/tinymce/tinymce.min.js') }}"></script>
<script type="text/javascript">
  tinymce.init({
    selector : "textarea",
    plugins : ["advlist autolink lists link image charmap print preview anchor", "searchreplace visualblocks code fullscreen", "insertdatetime media table contextmenu paste"],
    toolbar : "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
  }); 
</script>
<form method="post" action='{{ url("/update") }}'>
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <input type="hidden" name="post_id" value="{{ $post->id }}{{ old('post_id') }}">
  <div class="form-group">
    <input required="required" placeholder="Enter title here" type="text" name = "title" class="form-control" value="@if(!old('title')){{$post->title}}@endif{{ old('title') }}"/>
  </div>
  <div class="form-group">
    <textarea name='body'class="form-control">
      @if(!old('body'))
      {!! $post->body !!}
      @endif
      {!! old('body') !!}
    </textarea>
  </div>
  @if($post->active == '1')
  <input type="submit" name='publish' class="btn btn-success" value = "Update"/>
  @else
  <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  @endif
  <input type="submit" name='save' class="btn btn-default" value = "Save As Draft" />
  <a href="{{  url('delete/'.$post->id.'?_token='.csrf_token()) }}" class="btn btn-danger">Delete</a>
</form>
@endsection
同樣地,把 resources/views/posts/create.blade.php 改成
@extends('app')
@section('title')
Add New Post
@endsection
@section('content')
<script type="text/javascript" src="{{ asset('/js/tinymce/tinymce.min.js') }}"></script>
<script type="text/javascript">
  tinymce.init({
    selector : "textarea",
    plugins : ["advlist autolink lists link image charmap print preview anchor", "searchreplace visualblocks code fullscreen", "insertdatetime media table contextmenu paste"],
    toolbar : "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
  }); 
</script>
<form action="/new-post" method="post">
  <input type="hidden" name="_token" value="{{ csrf_token() }}">
  <div class="form-group">
    <input required="required" value="{{ old('title') }}" placeholder="Enter title here" type="text" name = "title"class="form-control" />
  </div>
  <div class="form-group">
    <textarea name='body'class="form-control">{{ old('body') }}</textarea>
  </div>
  <input type="submit" name='publish' class="btn btn-success" value = "Publish"/>
  <input type="submit" name='save' class="btn btn-default" value = "Save Draft" />
</form>
@endsection

用戶個人檔案

UserController.php 改成
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\User;
use App\Posts;
use Illuminate\Http\Request;
class UserController extends Controller {
  /*
   * Display active posts of a particular user
   * 
   * @param int $id
   * @return view
   */
  public function user_posts($id)
  {
    //
    $posts = Posts::where('author_id',$id)->where('active',1)->orderBy('created_at','desc')->paginate(5);
    $title = User::find($id)->name;
    return view('home')->withPosts($posts)->withTitle($title);
  }
  /*
   * Display all of the posts of a particular user
   * 
   * @param Request $request
   * @return view
   */
  public function user_posts_all(Request $request)
  {
    //
    $user = $request->user();
    $posts = Posts::where('author_id',$user->id)->orderBy('created_at','desc')->paginate(5);
    $title = $user->name;
    return view('home')->withPosts($posts)->withTitle($title);
  }
  /*
   * Display draft posts of a currently active user
   * 
   * @param Request $request
   * @return view
   */
  public function user_posts_draft(Request $request)
  {
    //
    $user = $request->user();
    $posts = Posts::where('author_id',$user->id)->where('active',0)->orderBy('created_at','desc')->paginate(5);
    $title = $user->name;
    return view('home')->withPosts($posts)->withTitle($title);
  }
  /**
   * profile for user
   */
  public function profile(Request $request, $id) 
  {
    $data['user'] = User::find($id);
    if (!$data['user'])
      return redirect('/');
    if ($request -> user() && $data['user'] -> id == $request -> user() -> id) {
      $data['author'] = true;
    } else {
      $data['author'] = null;
    }
    $data['comments_count'] = $data['user'] -> comments -> count();
    $data['posts_count'] = $data['user'] -> posts -> count();
    $data['posts_active_count'] = $data['user'] -> posts -> where('active', '1') -> count();
    $data['posts_draft_count'] = $data['posts_count'] - $data['posts_active_count'];
    $data['latest_posts'] = $data['user'] -> posts -> where('active', '1') -> take(5);
    $data['latest_comments'] = $data['user'] -> comments -> take(5);
    return view('admin.profile', $data);
  }
}

前端版面:

resources/views 新增一個 admin 資料夾,然後在此資料夾新增一個 profile.blade.php 檔案
@extends('app')
@section('title')
{{ $user->name }}
@endsection
@section('content')
<div>
  <ul class="list-group">
    <li class="list-group-item">
      Joined on {{$user->created_at->format('M d,Y \a\t h:i a') }}
    </li>
    <li class="list-group-item panel-body">
      <table class="table-padding">
        <style>
          .table-padding td{
            padding: 3px 8px;
          }
        </style>
        <tr>
          <td>Total Posts</td>
          <td> {{$posts_count}}</td>
          @if($author && $posts_count)
          <td><a href="{{ url('/my-all-posts')}}">Show All</a></td>
          @endif
        </tr>
        <tr>
          <td>Published Posts</td>
          <td>{{$posts_active_count}}</td>
          @if($posts_active_count)
          <td><a href="{{ url('/user/'.$user->id.'/posts')}}">Show All</a></td>
          @endif
        </tr>
        <tr>
          <td>Posts in Draft </td>
          <td>{{$posts_draft_count}}</td>
          @if($author && $posts_draft_count)
          <td><a href="{{ url('my-drafts')}}">Show All</a></td>
          @endif
        </tr>
      </table>
    </li>
    <li class="list-group-item">
      Total Comments {{$comments_count}}
    </li>
  </ul>
</div>
<div class="panel panel-default">
  <div class="panel-heading"><h3>Latest Posts</h3></div>
  <div class="panel-body">
    @if(!empty($latest_posts[0]))
    @foreach($latest_posts as $latest_post)
      <p>
        <strong><a href="{{ url('/'.$latest_post->slug) }}">{{ $latest_post->title }}</a></strong>
        <span class="well-sm">On {{ $latest_post->created_at->format('M d,Y \a\t h:i a') }}</span>
      </p>
    @endforeach
    @else
    <p>You have not written any post till now.</p>
    @endif
  </div>
</div>
<div class="panel panel-default">
  <div class="panel-heading"><h3>Latest Comments</h3></div>
  <div class="list-group">
    @if(!empty($latest_comments[0]))
    @foreach($latest_comments as $latest_comment)
      <div class="list-group-item">
        <p>{{ $latest_comment->body }}</p>
        <p>On {{ $latest_comment->created_at->format('M d,Y \a\t h:i a') }}</p>
        <p>On post <a href="{{ url('/'.$latest_comment->post->slug) }}">{{ $latest_comment->post->title }}</a></p>
      </div>
    @endforeach
    @else
    <div class="list-group-item">
      <p>You have not commented till now. Your latest 5 comments will be displayed here</p>
    </div>
    @endif
  </div>
</div>
@endsection
快速入門教學到此為止,完整文檔請到 Laravel 官方網站查看。

留言

這個網誌中的熱門文章

政府T-Contract vs Vendor vs In-house

我的 2 年 Developer 生涯