前言
在之前的文章[ Laravel ] 初心者之路#11 – 透過Migration建立Datebase實作中,我們透過migration建立了Database,另外也跟各位介紹過如何操作Database([ Laravel ] 初心者之路#09 – Database in Laravel)
大部分基礎的都跟各位介紹完畢了,接下來就要帶各位進行最後的整合實作
這一次會先帶各位建立後台相關的內容,讓我們可以透過後台來新增、修改、刪除Database的資料
在開始之前,讓我們先把上次的實作建立一個git commit吧
git add . git commit -m "Add migrations for create tables"
補上Create User的Migration
因為應該要進行登入才可以進入後台,所以我們需要一個table用來存放帳號密碼
所以要補上一個create users table的migration
php artisan make:migration create_users_table --create
然後內容為
public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->string('username'); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); }
另外讓他建立一個預設的管理者帳號,打開database/seeds/DatabaseSeeder.php,加入
public function run() { DB::table('users')->insert([ 'username'=>'admin', // 帳號 'password'=>bcrypt('admin'), // 密碼 ]); }
執行下面指令,讓他跑migrate然後同時跑seed
php artisan migrate:fresh --seed
然後打開app/User.php,這是預設的User Model,我們要根據我們的需求稍微修改一下內容
protected $fillable = [ 'username', 'password', ];
建立Model
為了方便我們存取Database
我們需要建立Model,用ORM的方式存取資料
User Model已經有預設了,我們就不去新增
php artisan make:Model Models\Website php artisan make:Model Models\Home php artisan make:Model Models\About php artisan make:Model Models\Product php artisan make:Model Models\Store
然後在每個Model中指定table的名稱,並且取消自動產生timestamps
protected $table = 'website'; protected $timestamps = false;
記得每個的$table都要改為正確的table name喔!
建立後台的Controller
所以讓我們先建立起我們所需的Controller
// 主要用於新增、修改、刪除Database的資料 php artisan make:controller Backend\WebsiteController php artisan make:controller Backend\HomeController php artisan make:controller Backend\AboutController php artisan make:controller Backend\ProductController -resource php artisan make:controller Backend\StoreController
接著讓我們在class前方分別加入它們各自的Model供使用
namespace App\Http\Controllers\Backend; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\Website; class AboutController extends Controller ...
記得每個的Controller都要加喔!
然後應該除了ProductController外
其他的Controller產生出來都沒有方法
所以我們要在除了ProductController外的每個Controller中加入兩個方法 edit() 跟 update()
public function edit() { // ... } public function update(Request $request) { // ... }
等等會在裡面新增我們需要的程式碼
然後ProductController中,我們用不到 show() 這個方法,所以可以將它刪掉
新增Route
要新增後台的頁面,當然需要跟Router講一下我們有那些頁面囉
讓Router知道要轉介給哪個Controller的哪個方法
開啟routes/web.php
加入
// 登入頁面 Route::get('/admin/login', function (){ return view('backend.login'); }); Route::post('/admin/login', 'Auth\LoginController@login')->name('login'); // 用auth middleware進行驗證 // prefix 代表group內的url路徑前都有/admin/ // name 代表 group中route命名時,每個name的前綴會加入admin. Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function() { // 登出 Route::get('/admin/logout', 'Auth\LoginController@logout')->name('logout'); // Website的更新 Route::get('/', 'Backend\WebsiteController@edit')->name('website.edit'); Route::post('/', 'Backend\WebsiteController@update')->name('website.update'); // Home的更新 Route::get('home', 'Backend\HomeController@edit')->name('home.edit'); Route::post('home', 'Backend\HomeController@update')->name('home.update'); // About的更新 Route::get('about', 'Backend\AboutController@edit')->name('about.edit'); Route::post('about', 'Backend\AboutController@update')->name('about.update'); // Product的增刪改查還有index頁面 Route::resource('product', 'Backend\ProductController', ['except'=> ['show']]); // Store的更新 Route::get('store', 'Backend\StoreController@edit')->name('store.edit'); Route::post('store', 'Backend\StoreController@update')->name('store.update'); });
建立後台頁面views
在resources/views中,建立backend資料夾
然後在裡面建立類似於前台的頁面結構
大概會是像這個樣子
view的部分比較簡單
我最後會附上github的連結,各位可以直接去參考內容
我這邊提幾個我有做的小重點
- 建立一個後台的backend.css (內容很少XD)
- 使用tinymce的編輯插件 (https://www.tinymce.com/download/)
下載下來後放到public底下,將連結放到head內,並且呼叫tinymce.init(...)
使用即可,詳細可參考github
加入Controller功能
讓各位看看,之後我們的後台大概是長成這副德行
除了product,其他頁面都是直接進行更新,畢竟像是website, home, about, store這些資料本來也就只會有一筆
但是我們第一次進入時是沒有資料的,也無法根據laravel的原先的update更新習慣,傳入一個id參數,找到該筆資料並進行更新
所以這邊我的做法會是,更新時直接找到id為1的資料去做更新,找不到時則直接建立
所以讓我們看看該怎麼做
WebsiteController
public function edit() { $website = Website::find(1); if (empty($website)) return view('backend.website.edit'); else return view('backend.website.edit', compact('website')); } public function update(Request $request) { // 因為沒有特別建立create頁面,所以特別判斷資料庫中是否有資料可以更新 $website = Website::find(1); if (empty($website)) { // 沒有資料 -> 新增 $website = new Website; } $website->title = $request->input('title'); $website->subtitle = $request->input('subtitle'); $website->footer = $request->input('footer'); $website->save(); return redirect()->route('admin.website.edit'); }
HomeController
public function edit() { $home = Home::find(1); if (empty($home)) return view('backend.home.edit'); else return view('backend.home.edit', compact('home')); } public function update(Request $request) { // 如果路徑不存在,就自動建立 if (!file_exists('uploads/home')) { mkdir('uploads/home', 0755, true); } // 因為沒有特別建立create頁面,所以特別判斷資料庫中是否有資料可以更新 $home = Home::find(1); if (empty($home)) { // 沒有資料 -> 新增 $home = new Home; $fileName = 'default.jpg'; } if ($request->hasFile('image')) { // 先刪除原本的圖片 if ($home->image != 'default.jpg') @unlink('uploads/home/' . $home->image); $file = $request->file('image'); $path = public_path() . '\uploads\home\\'; $fileName = time() . '.' . $file->getClientOriginalExtension(); $file->move($path, $fileName); } $home->content_1 = $request->input('content_1'); $home->content_2 = $request->input('content_2'); if ($fileName) { $home->image = $fileName; } $home->save(); return redirect()->route('admin.home.edit'); }
AboutController
public function edit() { $about = About::find(1); if (empty($about)) return view('backend.about.edit'); else return view('backend.about.edit', compact('about')); } public function update(Request $request) { // 如果路徑不存在,就自動建立 if (!file_exists('uploads/about')) { mkdir('uploads/about', 0755, true); } // 因為沒有特別建立create頁面,所以特別判斷資料庫中是否有資料可以更新 $about = About::find(1); if (empty($about)) { // 沒有資料 -> 新增 $about = new About; $fileName = 'default.jpg'; } if ($request->hasFile('image')) { // 先刪除原本的圖片 if ($about->image != 'default.jpg') @unlink('uploads/about/' . $about->image); $file = $request->file('image'); $path = public_path() . '\uploads\about\\'; $fileName = time() . '.' . $file->getClientOriginalExtension(); $file->move($path, $fileName); } $about->content = $request->input('content'); if ($fileName) $about->image = $fileName; $about->save(); return redirect()->route('admin.about.edit'); }
StoreController
public function edit() { $store = Store::find(1); if (empty($store)) return view('backend.store.edit'); else return view('backend.store.edit', compact('store')); } public function update(Request $request) { // 因為沒有特別建立create頁面,所以特別判斷資料庫中是否有資料可以更新 $store = Store::find(1); if (empty($store)) { // 沒有資料 -> 新增 $store = new Store; } // 去除字串前後空白 $input = $request->all(); $input = array_map('trim', $input); $sun_open = $input['sun_open_h'] . ':' . $input['sun_open_m']; $mon_open = $input['mon_open_h'] . ':' . $input['mon_open_m']; $tue_open = $input['tue_open_h'] . ':' . $input['tue_open_m']; $wed_open = $input['wed_open_h'] . ':' . $input['wed_open_m']; $thu_open = $input['thu_open_h'] . ':' . $input['thu_open_m']; $fri_open = $input['fri_open_h'] . ':' . $input['fri_open_m']; $sat_open = $input['sat_open_h'] . ':' . $input['sat_open_m']; $sun_close = $input['sun_close_h'] . ':' . $input['sun_close_m']; $mon_close = $input['mon_close_h'] . ':' . $input['mon_close_m']; $tue_close = $input['tue_close_h'] . ':' . $input['tue_close_m']; $wed_close = $input['wed_close_h'] . ':' . $input['wed_close_m']; $thu_close = $input['thu_close_h'] . ':' . $input['thu_close_m']; $fri_close = $input['fri_close_h'] . ':' . $input['fri_close_m']; $sat_close = $input['sat_close_h'] . ':' . $input['sat_close_m']; $store->sun_open = $sun_open; $store->mon_open = $mon_open; $store->tue_open = $tue_open; $store->wed_open = $wed_open; $store->thu_open = $thu_open; $store->fri_open = $fri_open; $store->sat_open = $sat_open; $store->sun_close = $sun_close; $store->mon_close = $mon_close; $store->tue_close = $tue_close; $store->wed_close = $wed_close; $store->thu_close = $thu_close; $store->fri_close = $fri_close; $store->sat_close = $sat_close; $store->address = $input['address']; $store->phone = $input['phone']; $store->save(); return redirect()->route('admin.store.edit'); }
圖片的部分,因為資料庫是沒有辦法存檔案格式
所以做法是讓資料庫存圖片的檔案名稱,然後圖片檔案存到server的某個資料夾中
當需要取用時,從資料庫取出檔案名稱,然後在存檔的路徑下找該檔案
而更新時我們也必須將原先的檔案刪除,所以會使用unlink的方法刪除檔案
另外為了避免刪除檔案時找不到檔案刪除,會在unlink前方加上@符號,即使找不到檔案也不會因錯誤回傳給使用者錯誤訊息
我有針對每個需要圖片的資料,在它們各自的資料夾中,預先放置好default.jpg,例如:
/public/product/default.jpg
如此即使沒有圖片,他也會去找到default.jpg作替代
ProductController
/** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $products = Product::orderBy('id')->get(); return view('backend.product.index', compact('products')); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('backend.product.create'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // 如果路徑不存在,就自動建立 if (!file_exists('uploads/product')) { mkdir('uploads/product', 0755, true); } $product = new Product; if ($request->hasFile('image')) { $file = $request->file('image'); $path = public_path() . '\uploads\product\\'; $fileName = time() . '.' . $file->getClientOriginalExtension(); $file->move($path, $fileName); } else { $fileName = 'default.jpg'; } $product->title = $request->input('title'); $product->subtitle = $request->input('subtitle'); $product->image = $fileName; $product->description = $request->input('description'); $product->save(); return redirect()->route('admin.product.index'); } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { $product = Product::find($id); return view('backend.product.edit', compact('product')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // 如果路徑不存在,就自動建立 if (!file_exists('uploads/product')) { mkdir('uploads/product', 0755, true); } $product = Product::find($id); if ($request->hasFile('image')) { // 先刪除原本的圖片 if ($product->image != 'default.jpg') @unlink('uploads/product/' . $product->image); $file = $request->file('image'); $path = public_path() . '\uploads\product\\'; $fileName = time() . '.' . $file->getClientOriginalExtension(); $file->move($path, $fileName); $product->image = $fileName; } $product->title = $request->input('title'); $product->subtitle = $request->input('subtitle'); $product->description = $request->input('description'); $product->save(); return redirect()->route('admin.product.index'); } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { $product = Product::find($id); if ($product->image != 'default.jpg') @unlink('uploads/product/' . $product->image); $product->delete(); return redirect()->route('admin.product.index'); }
然後因為我們登入是使用Laravel預設的Auth機制,
但是他登入時的驗證是採用email
所以我們需要進行小小的修改
打開app/Http/Controllers/Auth/LoginController.php
將$redirectTo更改為’admin’,讓登入後導向admin頁面
protected $redirectTo = '/admin';
然後在最下面加入,讓帳號是使用username的欄位去進行驗證
public function username() { return 'username'; }
好了!
到目前為止,你只要開啟你的網站,後面加上/admin/
就會出現登入畫面
然後登入後就可以進到後台去修改Database中的資料啦
總結
這次我們帶著各位將整個網站的後台架起
讓管理者可以直接透過後台的介面
修改Database中的資料
下一次我會帶各位將後台的資料帶入到前台的網站頁面中
如此一來你在後台所做的修改就會直接影響到前台的網站頁面囉!
最後附上專案的Github
https://github.com/chasewwy/laravel_tutorial
See you!