[Day 31] 會員登入及登出(三)

會員登入及登出

今天我們來把登入的功能完成.

會員登入

當驗證通過之後,
就可以正常登入,
在登入的時候,
我們會透過session來記錄會員的編號,
來作為會員已登入的驗證.

app/Http/Controllers/UserAuthController.php的signInProcess的方法

//前面省略

//session紀錄會員編號
session()->put('user_id', $User->id);

//重新導向到原先使用者造訪頁面,沒有嘗試造訪頁則重新導向回自我介紹頁
return redirect()->intended('/admin/user');

使用者在瀏覽網站的時候,
有可能會因為不同的原因而進入登入頁,
這時候如果能夠重新回到登入前的頁面,
是比較好的使用者體驗,
Laravel也考慮到了這一點,
所以提供redirect()->intended()方法,
來處理重新導向的功能,
intended方法裡面可以放預設的頁面,
如果使用者是直接進入登入頁,
就會重新導向到指定的頁面.

在這個部落格網站裡面,
會有所謂的前台後台,
以我所認知的部落格而言,
每一個使用者的權限都是一樣的,
可以編輯屬於自己的後台,
也可能觀看所有人的前台,
當我們要觀看前台的時候不需要登入,
(但是要留言的話需要登入)
不過當我們要進入後台的話就需要驗證你是否已登入,
我們在登入完畢之後會進入到後台編輯自我介紹的部分.

Session的設定

所有session的設定都放在config/session.php這個設定檔案內,

其中有一行

'driver' => env('SESSION_DRIVER', 'file'),

表示要用甚麼方法來儲存session,
Laravel有提供filecookiedatabaseapcmemcachedredisarray等等儲存方式,
如果只有一台主機,
大部分都使用file就可以了,
可是如果有好幾台主機,
假如沒辦法做到同步,
就會有「明明已經登入,系統卻判斷為沒有登入」的問題,
就可以使用database、redis或memchached等等的方式,
我們這裡只考慮一台主機的情況,
所以不需要修改session的設定.

登入的判斷

所以我們要判斷是否會員已登入,
就是透過session的user_id來判斷,
我們用Laravel提供的session()->has()方法來判斷是否已經登入.

if(session()->has('user_id'))

登出的動作

既然我們是透過session來判斷會員登入,
登出的時候就是透過Laravel提供的session()->forget()方法來清除session資料,
之後再重新導向到首頁.

app/Http/Controllers/UserAhthController.php的signOut方法

//登出
public function signOut()
{
    //清除Session
    session()->forget('user_id');

    //重新導向回首頁
    return redirect('/');
}

重新修改Menu

我們之前把所有的功能都放在Menu上,
但是這樣子是很奇怪的,
如果沒有登入,
就不應該會有登出的功能,
在這裡我們分成幾種情況,
一種是尚未登入的情況,
會有部落格註冊登入等頁籤, => 在此將原先的首頁改成部落格
一種是已登入而且在前台,
會有部落格進入後台登出等頁籤
另外一種是已登入而且在後台,
會有自我介紹心情隨筆回到前台等頁籤

我們把resources/views/layout/master修改如下


<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>@yield('title')</title>
        <script src="/js/app.js"></script>
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="/css/app.css?<?php echo date("mj", time())?>">
    </head>
    <boby>
        <div class="toolbar_section">
            <span class="toolbar_title">@yield('title')</span>
            <span class="toolbar_title2">小魚</span>
            
        </div>

        <div class="container">
            <div class="col-sm-1 form background_white">
                <ul class="nav nav-pills nav-stacked">
                @if($page == "admin" && session()->has('user_id'))
                    <!-- 自我介紹 -->
                    <li 
                    @if($name == "user")
                        class="active"
                    @endif
                    >
                        <a href="/admin/user">自我介紹</a>
                    </li>
                    <!-- 心情隨筆 -->
                    <li 
                    @if($name == "mind")
                        class="active"
                    @endif
                    >
                        <a href="/admin/mind">心情隨筆</a>
                    </li>
                    <!-- 回到前台 -->
                    <li>
                        <a href="/">部落格</a>
                    </li>
                @else
                    <!-- 首頁 -->
                    <li 
                    @if($name == "home")
                        class="active"
                    @endif
                    >
                        <a href="/">部落格</a>
                    </li>
                    @if(session()->has('user_id'))
                        <!-- 自我介紹 -->
                        <li>
                            <a href="/admin/user">進入後台</a>
                        </li>
                    @else
                        <!-- 註冊 -->
                        <li 
                        @if($name == "sign_up")
                            class="active"
                        @endif
                        >
                            <a href="/user/auth/sign-up">註冊</a>
                        </li>
                        <!-- 登入 -->
                        <li 
                        @if($name == "sign_in")
                            class="active"
                        @endif
                        >
                            <a href="/user/auth/sign-in">登入</a>
                        </li>

                    @endif
                @endif
                @if(session()->has('user_id'))
                    <!-- 登出 -->
                    <li>
                        <a href="/user/auth/sign-out">登出</a>
                    </li>
                @endif
                </ul>
            </div>
            <div class="col-sm-11 background_white2">
                @yield('content')
            </div>
        </div>
    </body>
</html>

而routes/web.php也修改如下

<?php

use Illuminate\Support\Facades\Route;

//首頁
Route::get('/', 'HomeController@indexPage');

Route::group(['prefix' => 'user'], function(){
    //使用者驗證
    Route::group(['prefix' => 'auth'], function(){
        //使用者註冊畫面
        Route::get('/sign-up', 'UserAuthController@signUpPage');
        //處理註冊資料
        Route::post('/sign-up', 'UserAuthController@signUpProcess');
        //使用者登入畫面
        Route::get('/sign-in', 'UserAuthController@signInPage');
        //處理登入資料
        Route::post('/sign-in', 'UserAuthController@signInProcess');
        //處理登出資料
        Route::get('/sign-out', 'UserAuthController@signOut');
    });
});

Route::group(['prefix' => 'admin'], function(){
    //自我介紹相關
    Route::group(['prefix' => 'user'], function(){
        //自我介紹頁面
        Route::get('/', 'AdminController@editUserPage');
        //處理自我介紹資料
        Route::post('/', 'UserAuthController@editUserProcess');
    });

    //心情隨筆相關
    Route::group(['prefix' => 'mind'], function(){
        //心情隨筆列表頁面
        Route::get('/', 'AdminController@mindListPage');
        //新增心情隨筆資料
        Route::get('/add', 'AdminController@addMindPage');
        //處理心情隨筆資料
        Route::post('/edit', 'AdminController@editMindProcess');
        //單一資料
        Route::group(['prefix' => '{mind_id}'], function(){
            //編輯心情隨筆資料
            Route::get('/edit', 'AdminController@editMindPage');
            //刪除心情隨筆資料
            Route::get('/delete', 'AdminController@deleteMindProcess');
        });
    });

});
?>

在這裡我們新增一個變數$page來判斷是否是後台,
所以之前的Controller都要加上$page這個變數,
否則就會出錯,
我們重新修改HomeController和UserAuthController兩個檔案

app/Http/Controllers/HomeController.php

<?PHP
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Module\ShareData;

class HomeController extends Controller
{
    public $page = "";
    //首頁
    public function indexPage()
    {
        $name = 'home';
        $binding = [
            'title' => ShareData::TITLE,
            'page' => $this->page,
            'name' => $name,
        ];
        return view('home', $binding);
    }
}
?>

app/Http/Controllers/UserAhthController.php

<?PHP
namespace App\Http\Controllers;

use Mail;
use Hash;
use Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use App\Module\ShareData;
use App\Entity\User;

class UserAuthController extends Controller
{
    public $page = "";
    //使用者註冊畫面
    public function signUpPage()
    {
        $name = 'sign_up';
        $binding = [
            'title' => ShareData::TITLE,
            'page' => $this->page,
            'name' => $name,
        ];
        return view('user.sign-up', $binding);
    }

    //處理註冊資料
    public function signUpProcess()
    {
        //接收輸入資料
        $input = request()->all();

        //驗證規則
        $rules = [
            //暱稱
            'name' => [
                'required',
                'max:50',
            ],
            //帳號(E-mail)
            'account' => [
                'required',
                'max:50',
                'email',
            ],
            //密碼
            'password' => [
                'required',
                'min:5',
            ],
            //密碼驗證
            'password_confirm' => [
                'required',
                'same:password',
                'min:5'
            ],
        ];

        //驗證資料
        $validator = Validator::make($input, $rules);

        if($validator->fails())
        {
            //資料驗證錯誤
            return redirect('/user/auth/sign-up')
                    ->withErrors($validator)
                    ->withInput();
        }

        $input['password'] = Hash::make($input['password']);

        //Log::notice(print_r($input, true));

        //啟用紀錄SQL語法
        DB::enableQueryLog();

        //新增使用者資料
        User::create($input);

        //取得目前使用過的SQL語法
        Log::notice(print_r(DB::getQueryLog(), true));

        //寄送註冊通知信
        $mail_binding = [
            'name' => $input['name']
        ];

        Mail::send('email.signUpEmailNotification', $mail_binding,
        function($mail) use ($input){
            //收件人
            $mail->to($input['account']);
            //寄件人
            $mail->from('henrychang0202@gmail.com');
            //郵件主旨
            $mail->subject('恭喜註冊Laravel部落格成功!');
        });

        //重新導向到登入頁
        return redirect('/user/auth/sign-in');
    }

    //使用者登入畫面
    public function signInPage()
    {
        $name = 'sign_in';
        $binding = [
            'title' => ShareData::TITLE,
            'page' => $this->page,
            'name' => $name,
        ];
        return view('user.sign-in', $binding);
    }

    //處理登入資料
    public function signInProcess()
    {
        //接收輸入資料
        $input = request()->all();

        //驗證規則
        $rules = [
            //帳號(E-mail)
            'account' => [
                'required',
                'max:50',
                'email',
            ],
            //密碼
            'password' => [
                'required',
                'min:5',
            ],
        ];

        //驗證資料
        $validator = Validator::make($input, $rules);

        if($validator->fails())
        {
            //資料驗證錯誤
            return redirect('/user/auth/sign-in')
                ->withErrors($validator)
                ->withInput();
        }

        //取得使用者資料
        $User = User::where('account', $input['account'])->first();

        if(!$User)
        {
            //帳號錯誤回傳錯誤訊息
            $error_message = [
                'msg' => [
                    '帳號輸入錯誤',
                ],
            ];

            return redirect('/user/auth/sign-in')
                ->withErrors($error_message)
                ->withInput();
        }

        //檢查密碼是否正確
        $is_password_correct = Hash::check($input['password'], $User->password);

        if(!$is_password_correct)
        {
            //密碼錯誤回傳錯誤訊息
            $error_message = [
                'msg' => [
                    '密碼輸入錯誤',
                ],
            ];

            return redirect('/user/auth/sign-in')
                    ->withErrors($error_message)
                    ->withInput();
        }

        //session紀錄會員編號
        session()->put('user_id', $User->id);

        //重新導向到原先使用者造訪頁面,沒有嘗試造訪頁則重新導向回自我介紹頁
        return redirect()->intended('/admin/user');
    }

    //登出
    public function signOut()
    {
        //清除Session
        session()->forget('user_id');

        //重新導向回首頁
        return redirect('/');
    }
}
?>

以下是未登入的畫面
 

https://ithelp.ithome.com.tw/upload/images/20201003/20105694P00xIiJu10.png

以下是登入後的前台畫面
 

https://ithelp.ithome.com.tw/upload/images/20201003/201056941RXxKFPWka.png

至於登入後的後台畫面明天才開始做,
不過因為登入之後會直接導到後台,
目前還沒有Controller跟頁面,
各位大大也可以嘗試根據目前所學的,
自己先做一個頁面代替,
明天再正式做新的頁面.