30分鐘用Laravel實現一個部落格
- 2019 年 11 月 13 日
- 筆記
介紹
- Laravel 是一款 MVC架構、 目前最流行的 PHP框架。
- Laravel的優點在於:豐富的composer類庫支援, 優雅的程式碼, 未來的主流框架(目前市場佔有率最高的框架)
- Laravel的缺點在於:過於優雅(我們只需要編寫極少的程式碼即可實現功能,意味著底層極其複雜的封裝)導致程式的執行效率略低, 和thinkphp等中國主流框架相比,上手難度略高(因為它為我們集成了很多其他的功能,甚至你還需要學習nodeJS相關的知識)。
- 本項目,是完全使用 Laravel框架 內的所提供的最基礎,但是又是最有用(能顯著提升我們開發效率)的工具而開發出來的。在學習過程中,你只需要操作一次資料庫,不需要自己構建html視圖模板(當然還是要寫一些html和js程式碼的),不需要考慮外部的css、js。本教程的目的完全為向各位 phper 以及對 laravel 有興趣的小夥伴推薦這款我相信是未來主流的php框架。
準備工作
- 確保你了解 php面向對象編程 的基礎知識, 會html和簡單的js, 在css方面:我們使用laravel內置的 bootstrap4, 最後,一定要會使用 composer。
- 唯一一次操作資料庫:創建用戶、資料庫,授權
如果你願意用root用戶,你甚至只需要 create 一個 database 即可。(不過不推薦,我的習慣是一個項目 對應 一個用戶 + 一個資料庫,root則只用來管理他們)
# 創建用戶 blog, 密碼自定義 CREATE USER 'blog'@'%' IDENTIFIED BY '密碼'; # 創建資料庫 blog, 設置默認編碼為utf8 CREATE DATABASE `blog` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; # 授權 授予 blog庫下所有表的 所有許可權 給 用戶blog GRANT ALL on blog.* to 'blog'@'%';
使用 composer 創建一個 laravel 項目 取名叫blog
# 進入你本地伺服器用於存放網站文檔的目錄,輸入命令 composer create-project --prefer-dist laravel/laravel blog
- 你還需要配置一個虛擬主機以提升開發效率(直接訪問url,不需要 「localhost/項目/public」 訪問 ),你可以選擇手動配置,或者選擇集成開發環境創建項目(推薦:mac=>mamp 、 windows=>phpstudy。)。
下文中,「/」 即表示 laravel 框架的根目錄
- 配置 /.env 文件
# 資料庫配置 DB_CONNECTION=mysql #類型 DB_HOST=127.0.0.1 #ip DB_PORT=3306 #埠 DB_DATABASE=資料庫名 DB_USERNAME=用戶名 DB_PASSWORD=密碼
- 下載中文包
composer require caouecs/laravel-lang
然後將 /vendor/caouecs/src/zh-CN/ 放入 /resources/lang/ 下 - 配置一下 /config/app.php
# 時區 'timezone' => 'Asia/Shanghai', # 語言 'locale' => 'zh-CN',
準備工作總結
- 1、創建用戶、資料庫,然後授權。
- 2、使用 composer 創建項目。
- 3、配置 laravel 的環境 ./env 。然後使用 composer 安裝了漢化包,並且在 /config/app.php 中設置時區並且讓中文包生效。
第一階段:Migration、Factory、Seeder
你可能沒有見過上面3個名詞,不過和他們有關的文件都存放在 /database/ 下:通過這個文件夾的名稱,你大概已經猜到:這三個文件都是用來操作資料庫的。
- 上文中,我們只是創建了資料庫,並沒有創建數據表,現在來確定一下我們的數據表 項目是一個個人部落格,因此只有部落客可以發布、刪除、修改部落格。其他用戶則可以查看部落格和發布評論。
- 一個用戶表 users
- 一個部落格表 blogs
- 一個評論表 comments
- 使用 Migration 創建這3張數據表 php aritsan 是laravel內置的命令 你可以直接在控制台輸入它,則會在控制台提示你接下來你能輸入的命令。 上文我們就使用 make:migration 幫我們創建了遷移文件, –create 是參數,即告訴這條命令,幫我們創建一個用於創建數據表的遷移文件
- 打開命令行,創建 migrations
php artisan make:migration create_blogs --create=blogs
部落格表,php artisan make:migration create_comments --create=comments
評論表
- 打開命令行,創建 migrations
- 為什麼不創建用戶表呢?打開 /database/migrations/ 你會發現有一個2014年就創建好的 針對 users 和 password_resets 表。這是框架自帶的。
- 編輯這兩個遷移文件
- create_blogs
// 首先類定義中,有兩個方法,up()可以理解為正向操作:創建表,而 down()可以理解為回滾操作:刪除表。 // up() Schema::create('blogs', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('content'); $table->timestamps(); }); // down() 已經自動幫我們寫好了。
create_comments
// up() Schema::create('comments', function (Blueprint $table) { $table->increments('id'); $table->string('content'); $table->integer('blog_id'); //這條評論是針對哪一篇部落格的? $table->integer('user_id'); //這條評論是哪一位用戶發送的? $table->timestamps(); });
- 執行遷移:1、確保你的 /.env 配置正確 2、確保你的資料庫可以正常使用 3、確保資料庫中沒有數據表或者沒有和users blogs comments重名的數據表
php artisan migrate
- 打開資料庫(你可以任選一款資料庫管理工具,或者直接使用mysql的命令行),打開資料庫 blog ,你會發現有以下表
- blogs => 我們創建的部落格表
- comments => 我們創建的評論表
- migrations => 系統創建的遷移記錄表
- password_resets => 框架自帶遷移文件生成的重置密碼用表
- users => 框架自帶的用戶表
- 主要解釋一下 migrations 表:
- 這是一個記錄你的遷移文件名稱和批次的表。它的主要作用是通過記錄批次,方便你對資料庫進行版本控制:打開 migrations表,你會發現,當前記錄了4張表的遷移文件名,而他們的batch都是1,你可以理解為當前資料庫是第一批,版本1。
- 如果你執行
php artisan migrate:rollback
即回滾資料庫,將會執行批次batch最大的記錄的那些遷移文件的 down() 方法。
- migration 的作用:1、幫我們省略了去寫sql語句的麻煩,2、讓我們對資料庫可以進行有效的版本控制。
- 使用模型工廠 Factory 來插入虛構的數據
在日常的開發中,我們需要很多模擬的數據進行測試,模型工廠的作用就是幫我們快速的,隨機的生成這些數據。
- 創建模型工廠
php artisan make:factory BlogFactory --model=Blog
, 關於評論表的模型工廠請自己寫。 - 注意此時我們其實沒有模型 Blog 和模型 Comment,我們只是創建了數據表而已。因此我們再創建兩個模型。
php artisan make:model Blog
, 關於評論表的模型請自己寫。
細心的你可能發現了,我們的數據表和模型的名字是有區別的:數據表為「小寫複數形式」,而模型名為「大寫單數形式」。創建的模型都存在於 /app/ 下。
- 編輯模型工廠 /database/factories
- BlogFactory
// 使用 Faker 類為我們提供的生成隨機偽造數據的方法生成數據 return [ 'title' => $faker->name, 'content' => $faker->text, ];
CommentFactory
return [ 'content' => $faker->text, 'blog_id' => 1, 'user_id' => 1, ];
- 使用 tinker 模式調試程式碼
- 進入 「修補匠模式」
php artisan tinker
, 當命令提示符變為 ">>>" 時,你就處於tinker模式下了,此時你可以輸入php程式碼,或者調用laravel提供的全局函數,甚至引用一個類,調用它的靜態方法或者實例化它。 - 在 tinker 模式下使用全局函數 factory() 生成模擬的數據
factory(AppBlog::class)->make()
此時螢幕上會顯示,它給你模擬出來的一個虛擬數據數組。 - 使用 create() 一次性向數據表中插入100條模擬的數據
factory(AppBlog::class, 100)->create()
打開資料庫,您會發現100條標題和內容都無關緊要,但是對我們快速開發特別有用的測試數據已經存放在資料庫中了。
- 進入 「修補匠模式」
- 使用 Seeder 一次性完成多個資料庫的批量虛擬數據插入
- 創建 Seeder (如果你處於 tinker, 【ctrl】+【c】 先退出)
php artisan make:seeder UserTableSeeder
,針對部落格表和評論表的Seeder創建命令自己寫。 - 打開 /database/seeds/ 我們創建的Seeder都在這裡了,不過多了一個 DatabaseSeeder.php,我們等下再來了解它,先編輯其他Seeder,以 UserTableSeeder.php 為例
... use AppUser; // 在 class 關鍵字前面,引用一下 User 模型 class ... public function run() { factory(AppUser::class, 50)->create(); //向users表中插入50條模擬數據 $user = User::find(1); //插入完後,找到 id 為 1 的用戶 $user->name = "najiuyuanzou"; //設置 用戶名 $user->email = "[email protected]"; //設置 郵箱 $user->password = bcrypt('liuhaoyu'); //設置 密碼 $user->save(); //保存 }
在這裡我們明確1號用戶為真實的可用的管理員用戶!所以我們設置一下它的 用戶名 郵箱 以及密碼
- 其餘的Seeder我們可以只插入模擬的數據即可。
- 現在回過頭來,編輯 DatabaseSeeder
public function run(){ // $this->call(UsersTableSeeder::class); // 這裡給我們舉例了如何引用其他Seeder $this->call(UserTableSeeder::class); $this->call(BlogTableSeeder::class); $this->call(CommentTableSeeder::class);// 這裡有先後關係需要注意一下:評論n : 1文章/用戶,所以應該把它寫在最後 }
- 使用命令,刷新整個資料庫並且執行模擬數據插入
php artisan migrate:refresh --seed
=> 查看資料庫,發現資料庫重置了,並且 users blogs comments 每張表都有很多虛擬的數據。
第一階段總結
- 學習使用 migrations 的創建、編輯、執行以及回滾:實現對數據表的結構更改以及資料庫版本管理(說白了就是個帶日誌的數據表結構管理工具)
- 學習使用 factories 的創建、使用 tinker 調試、使用 factory() 全局函數製造和插入數據。
- 學習使用 seeds 的創建(Seeder)、編輯其他Seeder(在 run() 中調用 factory() )、編輯DatabaseSeed(在 run() 中調用 其他 Seeder)。
- 最後通過它們3個的配合,使用命令刷新了整個資料庫並且分別向3張表插入了很多模擬的數據,便於我們開發。
也許你到這裡會覺得這還不如你寫sql語句。但是請相信我,等你熟練掌握使用這些東西之後,你的開發速度會非常快!(畢竟你不需要再 "INSERT INTO table values ()" 複製粘貼修改100遍了)
第二階段:Auth的使用
- 輸入神奇的命令,看看發生了什麼
php artisan make:auth
- 打開瀏覽器輸入你配置的虛擬主機地址,你會看到一個Laravel框架的歡迎頁面,這不是重點,點擊右上角的 register ,你可以註冊帳號,點擊 login 你可以登陸…
- 本階段結束。
第二階段繼續:Auth
- 好吧你可能很懵逼,但這就是Laravel的厲害之處,那個2014年就建好的migration遷移文件可不是個擺設。它就是通過操作users表來實現註冊登陸等等的。
php artisan make:auth
到底幹了哪些事情呢? Auth::routes(); //這是用戶操作相關的路由 Route::get('/home', 'HomeController@index')->name('home'); //這是主頁的路由- 它給你創造了一組視圖 /resources/views/auth/ 下是用戶操作相關的路由, home.blade.php是主頁, layouts/下是布局模板。
- 它給你創造了一組控制器,位於 /app/Http/Controllers/Auth
- 它在 /routes/web.php 中給你定義了2條路由
- 我們改良一下它自動為我們生成的東西
- 路由方面 (routes/web.php) 我們將根路由也指向視圖 home.blade.php ,這是最後的有效程式碼:
// Route::action('uri', 'Controller@function'); Route::get('/', 'HomeController@index'); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home');
控制器方面 app/Http/Controllers/ 我們修改一下 HomeController.php
/** * 這裡這個構造函數調用了 中間件auth 對我們進行許可權認證 * 即要求我們必須登陸才可以訪問該控制器的其他方法 * 有兩種解決方法,一直是在 $this->middleware('auth')->except('你要排除許可權認證的方法'),比如 ...->except('index') * 另一種是直接幹掉這個函數(我們確定這個控制器就只是來展示首頁的,那麼就幹掉它吧) */ // public function __construct() // { // $this->middleware('auth'); // }
視圖方面:HomeController@index (這裡我指的是 Home控制器的 index() 方法)調用了 return view('視圖名稱')
來抓取視圖顯示在頁面上,現在打開瀏覽器訪問主頁,你就可以看得到 home.blade.php 中的內容了,我們看看 /resources/views/home.blade.php 的內容: 重點:@extens @section
{{-- 內容不重要我們等下要改,先來看下 @符號 的作用 --}} {{-- @extends繼承其他模板 --}} @extends('layouts.app') {{-- 這裡的layouts.app => /resources/views/layouts/app.blade.php --}} {{-- @section 填充在布局模板上用 @yield 標註的佔位符 --}} @section('content') {{-- 你可以在 /resources/views/layouts/app.blade.php 看到 @ yield('content')標註的佔位符 --}} ... 這裡面是html內容 @endsection
Auth為我們生成的整個視圖模板的邏輯:layouts/app.blade.php 為布局模板,其他模板都繼承該模板。
- 最後我們「漢化」這些視圖
- home.blade.php,你可以自由發揮,展示一個好看的主頁,這是我的
@extends('layouts.app') @section('content')<div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">歡迎!這裡是 「那就遠走」 的個人部落格</div> <div class="card-body"> 如無牽掛,那就遠走。 {{-- 這裡等下要添加一個跳轉到展示文章列表頁面的按鈕 --}} </div> </div> </div> </div> </div> @endsection
../layouts/app.blade.php & ../auth/login.blade.php & register.blade.php
# 首先布局模板我們需要把 brand 登陸、註冊這些東西改一改 {{ config('app.name', 'Laravel') }} => 我的部落格 //注意這裡有一個 config('app.name') 該函數其實是讀取的 /.env 里的 APP_NAME 值,且默認值為 'Laravel' ,也就是說,你改 APP_NAME 也可以改這裡顯示的值,不過我嫌麻煩,直接查找替換了。 {{ __('Login') }} => 登陸 {{ __('Register') }} => 註冊 {{ __('Logout') }} => 退出 # 然後登陸模板: {{ __('Login') }} => 登陸 {{ __('E-Mail Address') }} => 郵箱 {{ __('Password') }} => 密碼 {{ __('Remember Me') }} => 記住我 然後我們把 ForgetPassword 那個按鈕給幹掉吧(這個找回密碼的功能需要一個SMTP服務的郵箱才能實現,現在暫時不弄) # 然後註冊模板 {{ __('Register') }} => 註冊 {{ __('Name') }} => 昵稱(用戶名) {{ __('Confirm Password') }} => 密碼確認 # 有可能有說漏的,反正自己看著頁面上的英文查找替換成中文就可以了。
第二階段總結
- 我們使用一條命令就實現了用戶操作的相關功能。
- 但是這條命令生成的視圖是英文的,所以需要我們改成中文。
- 這條命令主要是 生成了一組用戶操作的控制器+主頁控制器(其實還有中間件),生成了2條路由,生成了一組視圖。
第三階段_1:路由、模型、視圖、控制器詳解。
- 如果你完全熟悉MVC架構,可以跳過這一步。
- 這裡用大白話解釋:
- 路由:/routes/web.php 瀏覽器中輸入的地址,比如定義
Route::get('home', 'HomeController@home')
=> 即表示,你輸入 "http://localhost/blog/public/home" 是以GET的請求方式去請求 HomeController 的 home() 方法。 - 模型:/app/ 一個模型對應資料庫中的一張數據表。(注意大小寫和單複數,模型:Model => 數據表:models)
- 視圖:/resources/views/ 視圖就是普通的html模板,它等待控制器通過
return view()
調用和渲染它,最終展示給網站訪客。 - 控制器:/app/Http/Controllers/ 處理數據、調用模型、簡單地操作資料庫、渲染視圖…,都由它完成。
- 總結 => 路由定義在瀏覽器中訪問某控制器中某方法的地址,控制器完成一系列操作:如果需要操作資料庫,需要調用模型,每一個模型對應一張表。如果需要顯示數據,則需要找到框架內指定位置的視圖,對它完成渲染。
- 路由:/routes/web.php 瀏覽器中輸入的地址,比如定義
第三階段_2:資源路由、在資源控制器中完成對部落格的增刪改查。
我們寫的程式,除了前台好看的介面,就是後台的程式,而後台的程式無非就是「增刪改查」以及「花式增刪改查」罷了。 因此,仔細想想,對於一張數據表的操作,我們通常就需要這些行為:1、一個分頁展示所有數據的列表 2、一個添加數據的功能 3、一個編輯數據的功能 4、一個顯示單條數據詳細資訊的功能 5、一個刪除功能。
- 創建一個資源控制器,一次性幫我們生成能實現上面5個功能的方法
php artisan make:controller BlogController --resource --model=Blog
( –resouce生成的控制器為資源控制器即自帶 CURD增刪改查 所有方法的控制器 ) ( –model 是讓生成的控制器在參數列表中自動幫我們完成依賴注入生成實際變數 ) - 根據 三_1 階段的說法,我們其實需要設置很多路由,來對應生成的 BlogController 下的各種方法,Laravel已經幫我們想到了所以它給我們提供了這樣一種方法配置路由,編輯 /routes/web.php ,在最後面添加這麼一句
Route::resource('blog', 'BlogController');
- 有一個命令可以看到當前所有生效的路由
php artisan route:list
,控制台自動彈出的表格中,Method 表示 請求方法,即Route::這裡的方法()
,URI 表示 請求地址,即Route::action('這裡就是uri')
, name 表示 路由別名,可以通過Route::action('uri', 'Controller@function')->name('這裡可以定義路由別名')
。 - 你會發現,有7條關於 blog 的路由,這就是
Route::resource('blog', 'BlogController')
幫我們生成的。(5個功能7條是因為 添加和編輯多了2條載入視圖的路由) - 完成增刪改查吧: 首先完成 BlogController@index : 展示列表
- 先來個入口鏈接,打開 home.blade.php
{{-- 上面說過這裡會添加一個按鈕 --}} <a href="{{ route('blog.index') }}" class="btn btn-lg btn-block btn-primary">點擊這裡查看我的部落格</a>
編輯 BlogController@index => 這裡還是再提醒一下吧,這是說 BlogController 的 index() 方法。控制器文件都在 appHttpControllers 中
<?php namespace AppHttpControllers; use AppBlog; //這裡是使用命令創建控制器時,通過 --model=Blog 自動幫我們生成的 use IlluminateHttpRequest; class BlogController extends Controller{ public function index() { // 查詢數據,並且讓查詢結果是一個可分頁對象 $blogs = Blog::orderBy('created_at', 'desc') // 調用 Blog模型 的靜態方法 orderBy('根據created_at欄位', '倒敘排序') ->paginate(6); // -> 鏈式操作:paginate(6) 即數據沒頁6條 // 跳轉到視圖並傳值 return view('blog.index', [ //第一個參數是說,視圖模板是 /resources/views/blog/index.blade.php 'blogs' => $blogs, //這裡是說,我們給視圖傳遞一個叫 $'blogs'的變數,值是前面我們查詢的數據,也叫$blogs。 ]); // view() 的第二參數也可以使用 view(..., compact('blogs')) }
此時刷新頁面當然會報錯了,因為我們的視圖還不存在,新建文件夾 /resources/views/blog/index.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="card"> <div class="card-header">這是頁面小標題</div> <div class="card-body"> 這裡是內容 </div> </div> </div> </div> @endsection
這就是我們所有頁面的布局,可以複製一份,作為模板。
- 完成視圖裡面的內容
<div class="container"> <div class="row justify-content-center"> <div class="card"> <div class="card-header">文章列表</div> <div class="card-body"> <table class="table table-hover table-bordered"> <thead class="bg-info"> <tr> <th>文章標題</th> <th>發布時間</th> <th>相關操作</th> </tr> </thead> <tbody> {{-- 這裡通過 @foreach 遍曆數據 --}} @foreach ($blogs as $blog) <tr> <td>{{ $blog->title }}</td> <td>{{ $blog->created_at }}</td> <td></td> </tr> @endforeach </tbody> <tfoot> {{-- 這裡通過 $blogs->links() 來顯示分頁按鈕 --}} {{ $blogs->links() }} </tfoot> </table> </div> </div> </div> </div>
完成文章添加 添加入口鏈接, ../layouts/app.blade.php
{{-- route('路由別名') 在視圖上就是一個指向 BlogController@create 的鏈接 --}} <a href="{{ route('blog.create') }}" class="dropdown-item"> 添加文章 </a>
完成文章的添加 BlogController@create
public function create(){ return view('blog.create'); //載入視圖}
編輯視圖 重點:表單中添加@csrf告訴框架,這是我們自己的表單,不用擔心csrf跨站請求偽造的攻擊
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="card"> <div class="card-header">添加文章</div> <div class="card-body"> {{-- from.method="POST" action="通過 route()函數讀取路由別名 " --}} <form method="POST" action="{{ route('blog.store') }}"> {{-- 聲明 csrf 令牌 --}} @csrf <div class="form-group"> <label for="title">文章標題</label> <input type="text" class="form-control" id="title" placeholder="請輸入文章標題" name="title"> </div> <div class="form-group"> <label for="content">文章內容</label> <textarea id="content" cols="30" rows="10" class="form-control" name="content"></textarea> </div> <button class="btn btn-primary" type="submit">發布新文章</button> </form> </div> </div> </div> </div> @endsection
所謂跨站請求偽造,可以理解為來自於其他ip的表單,惡意請求我們的伺服器。Laravel提供了一種防範這種攻擊的手段,即將自己的路由隱藏起來,只有帶有 @csrf 聲明的表單可以找得到接收表單資訊的路由
- 編輯 BlogController@store
public function store(Request $request) //這裡的 $request 是通過依賴注入的方法實例化的 Request 類的對象,包含的有所有請求的資訊 { // 我們只需要調用 Blog模型 的靜態方法 create() 插入 $request->post() 數據即可 $blog = Blog::create($request->post()); //改方法的返回值是新插入的數據生成的對象 // redirect() 頁面重定向 return redirect()->route('blog.show', $blog); // 這裡我們將 $blog 作為參數請求 BlogController@show }
回到頁面,點擊提交,會發現報錯了,Laravel是一個極其注重安全的框架,用戶能修改哪些欄位,必須要在模型文件中聲明,因此打開 appBlog.php 模型文件
// 可填欄位白名單 protected $fillable = [ 'title', 'content' ];
- 再次提交,頁面一片空白,是因為我們的 BlogController@show 方法還沒有寫,不過你可以注意到地址欄已經發生了改變。
- 完成 show 方法
public function show(Blog $blog) //這裡已經通過依賴注入的形式幫我們實例化了 $blog { return view('blog.show', [ 'blog' => $blog, //直接將$blog傳給視圖進行渲染 ]); }
新建 ../blog/show.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="card"> <div class="card-header">文章詳情</div> <div class="card-body"> <h1 class="text-center">{{ $blog->title }}</h1> <p>發布時間<small>{{ $blog->created_at }}</small></p> <hr> <p> {{ $blog->content }} </p> </div> </div> </div> </div> @endsection
- 刷新頁面,文章就顯示出來了。
- 完成我們的編輯入口鏈接: 在 ../blog/index.blade.php & show.blade.php 中合理的位置添加一個編輯按鈕
<a href="{{ route('blog.edit', $blog->id) }}" class="btn btn-info">編輯文章</a>
完成 BlogController@edit
public function edit(Blog $blog){ return view('blog.edit', [ 'blog' => $blog, ]); }
完成視圖 重點:action聲明文章編號,根據路由要求action在表單中使用@method偽造請求動作類型
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="card"> <div class="card-header">編輯文章</div> <div class="card-body"> {{-- action需要聲明當前編輯的文章編號$blog->id --}} <form method="POST" action="{{ route('blog.update', $blog->id) }}"> {{-- 聲明 csrf 令牌 --}} @csrf {{-- 偽造 PATCH 方法 --}} @method("PATCH") <div class="form-group"> <label for="title">文章標題</label> <input type="text" class="form-control" id="title" placeholder="請輸入文章標題" name="title" value="{{ $blog->title }}"> </div> <div class="form-group"> <label for="content">文章內容</label> <textarea id="content" cols="30" rows="10" class="form-control" name="content">{{ $blog->content }}</textarea> </div> <p>發表於<small>{{ $blog->created_at }}</small></p> <p>修改於<small>{{ $blog->updated_at }}</small></p> <button class="btn btn-primary" type="submit">確認編輯</button> </form> </div> </div> </div> </div> @endsection
完成 BlogController@update
public function update(Request $request, Blog $blog){ $blog->update($request->post()); //調用 $blog對象->update(更新數據組成的數組) 更新 return redirect()->route('blog.show', $blog); }
完成刪除功能 在 index.blade.php 和 show.blade.php 合理的位置插入刪除按鈕
<a href="javascript:deleteConfirm({{ $blog->id }});" class="btn btn-danger btn-sm">刪除文章</a> {{-- 因為刪除也需要 csrf 令牌認證,所以弄個表單 --}} <form method="POST" action="{{ route('blog.destroy', $blog->id) }}" id="delete-blog-{{ $blog->id }}"> @csrf {{-- 這裡偽造DELETE請求 --}} @method("DELETE") </form>
「刪除文章」 按鈕其實是調用了一個 js 函數,我們在 ../layousts/app.blade.php 中完成
<script> function deleteConfirm(id) { var confirm = window.confirm('確認要刪除這篇文章嗎?'); if(confirm === true) { $("#delete-blog-" + id).submit(); //提交表單 }else { window.alert('你選擇不刪除!'); } }</script>
完成 BlogController@delete 方法
public function destroy(Blog $blog){ $blog->delete(); return redirect()->route('blog.index'); //跳轉到首頁 }
- 增刪改查完成。
- 完善和優化
- 新建組件視圖文件夾 /resources/views/components/
- 然後新建一個組件視圖 _message.blade.php => 組件視圖我們都用
_
下劃線開頭 - 首先無論增刪改查操作,成功後我們沒有任何提示,我們使用 session 快閃記憶體方法消息吧:
<div class="container"> {{-- 遍歷 success danger 這兩個我們等會會在 session->flash() 方法中設置的"key" --}} @foreach (['success', 'danger'] as $msg) {{-- 當key存在的時候,證明我們給 session flash 快閃記憶體裡面裝載了一次提示資訊,那麼就顯示提示資訊 --}} @if (session()->has($msg)) <div class="alert alert-{{ $msg }}"> <ul> <li>{{ session()->get($msg) }}</li> </ul> </div> @endif @endforeach </div>
- 在 ../layousts/app.blade.php 中導入該組件 重點:@include 導入html片段
{{-- 在導航下面,內容上面導入 --}}@include('components._message')
- 編輯 BlogController 里的各種方法,在執行成功某些方法時,頁面重定向前,裝載快閃記憶體。以刪除舉例
$blog->delete(); session()->flash('success', '刪除文章成功!'); //裝載session快閃記憶體 return redirect()->route('blog.index');
然後有個問題,就是在於,我們這是一個個人部落格,所以只有我們自己可以對部落格文章進行增刪改,而用戶只可以進行查看。因此我們需要:
- 使用構造函數調用 auth中間件 來排除沒有登陸的用戶查看文章詳情: 編輯 BlogController
public function __construct(){ $this->middleware('auth')->except('index'); }
在 新增create、編輯edit、和刪除方法中加入一次用戶認證,以 create 方法舉例
// 因為比較簡單,所以我們不用Policy進行認證,我會在以後的教程裡面教大家如何使用Policy策略進行許可權認證 // 這裡我們就使用判斷當前用戶在數據表中資訊的主鍵id是不是1即可(因為我們在Seeder裡面把編號為1的用戶設置為了可用的管理員帳號) // 1、在程式碼開頭引用 Auth // 2、在方法內先判斷一下是不是 1號用戶 if(Auth::user()->id != 1) { // Auth::user() 獲取當前用戶資訊 -> id獲取屬性id(主鍵) session()->flash('danger', '抱歉,只有部落客才可以新增文章!'); return redirect()->back(); }
- 針對部落格的增刪改查在這裡就結束了。
第三階段總結
- 我們使用命令創建了一個 「資源控制器」
- 我們在 /routes/web.php 定義了一條資源路由
- 我們使用 BlogController 中的7個方法完成了對 部落格文章 的 CURD(增刪改查)操作。
- 我們優化了一下體驗,使用
session()->flash()
裝載快閃記憶體資訊,用一個組件html片段載入資訊,最後用@include()
在模板上載入這個html組件。 - 我們最後增加了一個簡單的許可權認證,判斷進行增刪改的用戶是不是管理員,不是管理員則不允許操作,直接裝載一條錯誤提示快閃記憶體,然後返回。
第四階段 評論功能
- 新建一個評論資源控制器
php artisan make:controller CommentController --model=Commment
- 新增一條資源路由,但只支援發表 /routes/web.php
Route::resource('comment', 'CommentController', ['only' => 'store']);
其實我們可以定義一條
Route::post('comment', 'CommentController@store')
路由,但是為什麼要寫資源路由呢?因為我要告訴你資源路由可以用['onlu'=>'操作']
讓其只支援一種操作:)
- 在文章詳情頁面下方增加一個表單 show.blade.php
<form method="POST" action="{{ route('comment.store') }}"> @csrf <input type="hidden" name="user_id" value="{{ Auth::user()->id }}"> <input type="hidden" name="blog_id" value="{{ $blog->id }}"> <div class="form-group"> <label for="content"></label> <textarea id="content" class="form-control" cols="30" rows="10" name="content">您對這篇文章有什麼看法呢?</textarea> </div> <button class="btn btn-primary" type="submit">發表評論</button> </form>
編輯 CommentController@store
public function store(Request $request){ Comment::create($request->post()); session()->flash('success', '評論成功!'); return redirect()->back(); }
提交評論出錯了,又忘了寫可填欄位白名單,編輯模型 appComment
protected $fillable = [ 'content', 'user_id', 'blog_id' ];
展示評論
- 首先我們需要確定 Blog 和 Comment 的關係 => Blog 1:n Comment 「一篇部落格有多個評論」
- 我們來綁定他們的關係
- appBlog.php
// 綁定1:n關係 public function comments() { return $this->hasMany('AppComment'); // 1 hasMany n }
appComment.php
public function blog() { return $this->belongsTo('AppBlog'); // n belongsTo 1 }
然後通過他們的關係,我們可以在 BlogController@show 方法中調用 $blog->comments
來獲取屬於這篇文章的評論
// 查詢評論 $comments = $blog->comments; // 視圖渲染 return view('blog.show', [ 'blog' => $blog, 'comments' => $comments, //把評論也傳過去 ]);
在視圖層遍歷評論 show.blade.php
<h3>評論</h3> <ul> @foreach ($comments as $comment) <li><small>{{ $comment->userName() }} 評論說:</small>「 {{ $comment->content }} 」</li> @endforeach</ul>
注意我們調用了 $comment->userName() 方法,現在沒有,所以我們再 appComment.php 模型中完成
use AppUser; //引用模型 // 根據 user_id 獲取用戶名 public function userName() { return User::find($this->user_id)->name; //這裡通過當前對象的 user_id 獲取 user對象, 然後指向->name屬性 }
- 評論驗證
在部落格中,我們就沒有使用驗證,那是因為項目定位是一個個人部落格,能夠操縱部落格增刪改的只有我們自己。而評論則是只要有人註冊帳號,就可以評論了,所以我們應該對評論進行一些校驗以防惡意攻擊。
- 新建一個請求Request
php artisan make:request CommentRequest
- 新建的請求位於 appHttpRequests 下,編輯 CommentRequest
// 1、 開啟授權 public function authorize(){ return true; //如果返回false則所有請求都無法生效,會告訴你沒有授權(其實在這裡面我們是需要去進行判斷的,但是這裡的邏輯很簡單:只有登陸才能查看文章詳情,才能看到文章詳情下面發表評論的表單,才能發表評論。)所以我們這裡直接 return true;}// 2、 編輯規則public function rules(){ return [ 'content' => 'required|min:15|max:100', //這裡你可以定義規則我的是:必填、最少15字、最多100字 ]; }
在 CommentController@store 方法的參數列表中通過 CommentRequest 構造 $request, 自動完成校驗
public function store(CommentRequest $request) // 這將 Request 改成 CommentRequest 就會自動調用 CommentRequest@rules 來校驗請求的數據了{ Comment::create($request->post()); session()->flash('success', '評論成功!'); return redirect()->back(); }
優化視圖 show.blade.php
{{-- 樣式裡面加一個判斷,判斷是否有關於content的錯誤有的話給樣式給文本域加一個紅邊邊 --}} <textarea id="content" class="form-control {{ $errors->has('content') ? ' is-invalid' : '' }}" cols="30" rows="10" name="content">你對文章有什麼看法呢?</textarea> {{-- 如果有錯誤,再顯示一個小的錯誤提示資訊 --}} @if ($errors->has('content')) <span class="invalid-feedback"> <strong>{{ $errors->first('content') }}</strong> </span> @endif
此時提交表單,左下角會提醒你 「內容不能為空」,如果你想改「內容兩個字」,可以打開 /resources/lang/zh-CN/validation.php
'content' => '內容', //這裡就是配置欄位的中文名,你把它改成評論即可。
有時候文章過長,導致提交了,往下拉才看得到文本域變紅,所以我們需要新建一個錯誤組件../components/_error.blade.php
{{-- 判斷是否有錯誤 --}} @if (count($errors) > 0) <div class="alert alert-danger"> {{-- 遍歷所有錯誤 --}} @foreach ($errors->all() as $error) <ul> {{-- 列印錯誤 --}} <li> {{ $error }}</li> </ul> @endforeach </div> @endif
- 然後在 show.blade.php 中引用
@include('components._error')
第四階段總結
- 我們依然創建資源控制器,但是在路由中使用
['only'=>'store']
讓資源路由只暴露指向 CommentController@store 的路由 - 我們學會了通過
hasMany() & belongsTo()
綁定模型之間的1:n關係。然後通過文章->評論+s;
的方法直接獲取了屬於某篇文章的所有評論。 - 我們學會了創建請求Request,並且在它的內部配置驗證規則,在控制器層中通過依賴注入的形式驗證數據。
- 一旦表單提交的數據不符合 Request@rules Laravel會自動幫我們生成一個叫 $errors 的數組,它存放著所有的錯誤資訊, 我們在視圖上通過判斷它是否有
content
欄位來判斷是否是表單提交的評論有問題,然後修改文本域的樣式並且在下方用一個小的提示span顯示錯誤提示資訊 - 錯誤提示資訊顯示的是「內容 怎麼怎麼樣…」,我們想把「內容」改成評論只需要修改中文語言包下的validation.php中的'content'欄位的別名即可。
第五階段 最後總結
- 想讓項目上線,也許你需要
- 更好看的html排版
- 重新執行一次
php artisan migrate:rollback
- 許可權認證太水了。你需要學習使用 Policy 來進行更安全和全面的許可權認證。
也許文字很多,但是真正的程式碼可能只有不到100行,你如果熟練掌握,可能不需要30分鐘,甚至10分鐘,你就可以開發出這樣一個部落格了。
- 視圖方面
- 我們有通過 auth 生成的模板
- Laravel 自帶的 bootstrap4 + jquery
- 所以我們解決了css和js的問題 => 我們只是寫了一個 「確認刪除」 的前端程式碼
- 資料庫方面
- 我們有 /database/ 下提供的 3套解決方案 Migration / Factory / Seeder 來幫我們解決資料庫管理的問題
- 因為上面的解決方案,我們甚至只寫了 建用戶、建表、授權3條 資料庫操作語句。
- 路由方面
- Auth 自動幫我們生成了用戶操作相關路由
- 我們使用資源路由來映射一個 CURD 控制器
- 控制器和模型方面,通過命令生成的所有類文件,都幾乎幫我們寫好了,我們只需要完成裡面的邏輯。
- 當然,我們還有 Request 請求認證 Policy 策略控制等等一些列的特性沒有學習,我們也只使用了一次composer,其實在開發Laravel時,我們還可以使用非常多的,支援Laravel的,完善的輪子可以利用。
現在請告訴我,它是否配得上 「優雅」 的兩字?:)
希望大家可以喜歡、學習和推廣Laravel。如果您願意付出比學習thinkphp5多0.01分的努力,我想這個框架是非常簡單的。
如果您依然討厭它的龐大,我向您推薦 Lumen 框架。