[ Laravel ] 初心者之路#13 – 整合實作 PART 1

標籤: , ,

前言

在之前的文章[ 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的連結,各位可以直接去參考內容

我這邊提幾個我有做的小重點

下載下來後放到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!



相關文章

初心者之路#11 – 透過Migration建立Datebase實作... 前言 今天將會利用上一次 初心者之路#10 – Migrations 和 Schema的內容,利用Migration帶各位建立目前專案的資料庫結構   建立Database 要透...
初心者之路#06 – Views, Blade Templates 前言 之前在Laravel專案結構中跟各位介紹過MVC的概念,View就是在MVC中的V,負責的是網站界面的呈現,也是我們所熟知的前端。今天要來跟各位介紹的就是在Laravel中怎麼管理View,以...
初心者之路#01 – Laravel介紹 Laravel是什麼? 可以吃嗎(X 不能吃唷! 根據官方的說法,Laravel是一個PHP框架(Framework),專門為了熱愛簡潔、漂亮、優雅程式碼的你所打造的。 &nbs...
初心者之路#12 – Controller 控制器 前言 今天要來介紹的,是Laravel中的Controller,它做的事情就像先前提過的MVC中的Controller一樣,是用來處理網頁的要求邏輯。 在Laravel中,Controller...