Skip to content

Instantly share code, notes, and snippets.

@idhowardgj94
Last active December 22, 2018 16:07
Show Gist options
  • Save idhowardgj94/5c02dcd563f7c0fd3bc71e3705fa824a to your computer and use it in GitHub Desktop.
Save idhowardgj94/5c02dcd563f7c0fd3bc71e3705fa824a to your computer and use it in GitHub Desktop.
learning record

learning record

learning record by idhowardgj94

just write down my learning progress by topic format.

hope this would help for other starter like me.

git: how to ignore file already in git ?

git的邏輯是,已經上傳過的檔案,會放入追蹤的檔案清單中,持續的追綜該檔案的變化。 所以要讓gitignore作用,要將遠端上傳的檔案刪除。

刪除的旨令為:

git rm <file>
git rm <file> --cache

git的邏輯是mirror,也就是刪除遠端的檔案,本地端的檔案也會跟著被刪除。 這時可以加入 --cache 旨令,代表「刪除檔案,但是保留本地端端存」, 白話一點就是「我只刪除遠端檔案」。

這時,因為遠端刪除的檔案已經被加入.gitignore了, 所以之後使用 git add .git add -A就不會再被加入到stage中。

另外,git 有三種快速加入satage的旨令:

git add . # 加入所有更動過和新增的檔案,不加入刪除的檔案。
git add -A # 加入所有更動及新加入及刪除的檔案
git add -U # 加入所有更動及刪除的檔案,不加入新增的檔案

git add . + git add -U 等價於 git add -A

因此,要使.gitignore作用最簡單的方法為, 將所有追蹤的檔案刪除,但保存本地端的cache(此時如果直接commit,遠端會被清空), 然後再加入所有新增的檔案。(.gitignore會在這時候作用)。

git rm -r --cached . #刪除所有檔案,保留cache。
git add . #新增所有新加入的檔案
git commit -m ".ignore now working"

在做這動作以前,要先commit所有有變化的檔案, 不然所有的變化會失去追蹤(因為將檔案全部刪除,再全部重新新增, 可以想成是將這個repository 刪掉重建一次)。

當然,如果沒那麼懶的話,還是用 git rm -r --cache <ignore file>將ignore檔案從遠端刪除最好…

另外,如果只想要停止追蹤,但是保留遠端的檔案,可以使用旨令:

git update-index --assume-unchanged file.name #更新index,將某個檔案標為未更新

index是git中,用來記錄檔案變化的索引,作用在將本地端檔案加入statge中時。 因此,在index中將檔案標為未更新,就會停止對這個檔案的追蹤。

git的邏輯是,將已更動的檔案加入stage,然後將這個statge commit成一次上傳後,上傳到遠端。 所以遠端的repository是一次一次的stage。

looking table update in mysql (phpmyadmin)

最近的任務,需要寫一支WP的API, 將外來的資料insert進WP DB中。

因為要直接修改DB,要對DB有一定的了解, 除了看官網的文件外,大概就只能 try and error了。

但是,目前WP包含外掛程式,一共有超過84個資料表, 又亂又雜。

雖然知道posts是WP的資料的來源(by 官網文件), 但是不知道灌的其它外卦會在什麼時候去操作資料表。

這時可以使用以下旨令:

SHOW TABLE STATUS FROM <database name> WHERE name=<table name>

出來的資訊中,有 Update_time欄位,可以知道該資料表最後操作的時間。 對WP操作後,看update_time,就可以知道,一次post到底有多少資料表被更動了。

另外,如果想要修改 select的表,可以使用以下方式

SELECT table_name,Engine,Version,Row_format,table_rows,Avg_row_length,
    Data_length,Max_data_length,Index_length,Data_free,Auto_increment,
    Create_time,Update_time,Check_time,table_collation,Checksum,
    Create_options,table_comment FROM information_schema.tables
    WHERE table_schema = <database name>;

引用自https://dba.stackexchange.com/questions/3221/how-to-select-from-show-table-status-results

這次的使用方式

SELECT table_name,table_rows,Auto_increment, Create_time,Update_time FROM information_schema.tables WHERE table_schema = 'bitnami_wordpress'  ORDER BY Update_time DESC

lunux ubuntu general

npm 權限問題

不確定是不是權限問題,但是原來npm install 一直失敗,執行這行就好了:

 sudo chown -R $USER:$(id -gn $USER) /home/howardgj94/.config

後面改成根目錄底下的.config檔

也許不是這行,但還是記綠一下

刪除git變更

因為開發環境也有git,每次在pull之前都先刪掉之前的變更 (同步時以git為準,暫時先這樣):

git clean -d -f

-d 代表刪除沒有被追的檔案 -f 代表強制執行

virtual box ubuntu 設定 host-only + NAT netowrk

架設host-only的用意目前只為了讓主機可以使用ssh和ftp直接連到客機, 之後如果有特殊環境需求(如:平行運算) 也可以使用host-only建立測試環境

參考

ubuntu server跟一般的ubunto網路設定方式不一樣。先說一般的設定方式。

查看adapters名稱

ls /sys/class/net

上面的指令可以看到主機的設定。網路上的教學常常可以略過這步,但是我想可能是版本的問題,我在嘗試架設環境時,/etc/networks/interface 中不會看到 Host-only的網卡有設定固定IP,必須手動設定。 而且可能是版本的關係,新版的adapters名字通常不是eth0這種格式,而是enp0s1這種格式。 自動設定的例子在 http://kanchengzxdfgcv.blogspot.com/2015/10/putty-windows-ubuntu-oracle-vm.html

設定好 host-only網路後 開啟 VM 用指令 ($ ifconfig )我們可以觀察到多了一張 eth1的網卡,其 IP 為 192.168.56.101

設定靜態IP ####

sudo vi /etc/network/interfaces
# The loopback network interface
auto lo
iface lo inet loopback

# Host-only interface
auto eth1
iface eth1 inet static
        address         192.168.56.20
        netmask         255.255.255.0
        network         192.168.56.0
        broadcast       192.168.56.255

# NAT interface
auto eth2
iface eth2 inet dhcp

以上為範例,把eth1, eth2改成第一步查到的名稱即可。

設定叢集電腦

分散運算(如hadoop平台)用。

sudo gedit /etc/hosts
127.0.0.1 localhost
127.0.1.1 hadoop
192.168.60.100 master
192.168.60.101 data1
192.168.60.102 data2
192.168.60.103 data3

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

master data1...為設定每一台主機的對應IP。

新版ubuntu、server版

新增一個 adapter 設定檔,如:

sudo vi /etc/netplan/02-netcfg.yaml

輸入以下:

network:
    version: 2
    renderer: networkd
    ethernets:
        enp0s3:
            addresses:
                - 192.168.56.12/24
            dhcp4: no

enp0s3為host-only的adpter名 之後執行旨令

sudo netplan generate
sudo netplan apply

應該不需要其它設定就設定完成了。可以使用ifconfig看網路狀態

ifconfig
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.56.12  netmask 255.255.255.0  broadcast 192.168.56.255
        inet6 fe80::a00:27ff:fe06:6cdd  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:06:6c:dd  txqueuelen 1000  (Ethernet)
        RX packets 252  bytes 23076 (23.0 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 208  bytes 30015 (30.0 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.3.15  netmask 255.255.255.0  broadcast 10.0.3.255
        inet6 fe80::a00:27ff:fe4d:a6b8  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:4d:a6:b8  txqueuelen 1000  (Ethernet)
        RX packets 95  bytes 94894 (94.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 85  bytes 7436 (7.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions

how to check stat in number format?


stat --format '%a' <file>

how to search file pattern in linux?


quote by: https://stackoverflow.com/questions/16956810/how-do-i-find-all-files-containing-specific-text-on-linux

grep -rnw '/path/to/somewhere/' -e 'pattern'
  • -r or -R is recursive,
  • -n is line number, and
  • -w stands for match the whole word.
  • -l (lower-case L) can be added to just give the file name of matching files.

about apache server

restart apache service in ubuntu

every time you change the conf files or other setting, you need to restart apache service

sudo service apache2 reload

open virtual host module

sudo a2ensite default

restart apache server (bitnami)

sudo /opt/bitnami/ctlscript.sh restart apache

apache user (bitnami)

qoute from https://community.bitnami.com/t/apache-user-for-bitnami-stacks/1024

If you have installed as a regular user, the Apache user is the same that your user. If you have installed as root user, the default > > Apache user is "daemon". I hope it helps.

about laravel


建立專案


這次實作使用ubuntu當作業系統。 因為ubuntu的套件管理工具穩定版提供到7.0 所以這次使用7.0使用的最新版laravel。

安裝過程照著官網文件。

https://laravel.com/docs/5.5/installation

首先check是否所有的php套件都已經成功安裝。 (ubuntu底下可以使用apt-cache search php-來查找套件。通常都可以使用apt-get install來安裝。

接著,可以開始創建專案。

  1. 透過composer安裝laravel/installeer,並使用laravel旨令建立專案。
composer global require "laravel/installer"

記得修改環境變數$path:

vim ~/.profile

在檔案中加入此行

PATH="$PATH:$HOME/.config/composer/vendor/bin"

之後執行旨令:

laravel new blog

實作後發現,使用這個方式,資料夾中少了vendor資料夾,猜測是跟composer的環境設定有關。 因此認為使用下面的方式應該是比較簡單,也比較易於管理的方式(每一個網站app都有獨立的composer.json管理套件, 符合一般使用情形。 2. 從composer中建立專案。 執行以下指令:

composer create-project --prefer-dist laravel/laravel blog "5.5.*"

可使用artisan來測試是否有建成功:

php artisan serve

依經驗法則,應該是會遇到一堆權限相關的問題…

code snippest


ajax setup(csrf)

// in header
<meta name="csrf-token" content="{{ csrf_token() }}">

// in javascript 
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

指令整理


config cache

用來應付bootstrap資料夾不可寫的error

 php artisan config:cache

優化指令

 composer update
 composer dump-autoload 
 php artisan clear:route 
 php artisan clear:config 
 php artisan optimize

check routes status

php artisan route:list

設定test資料庫 以及 test 資料庫 migration

因為有一個test資料庫,test使用,不去動到正式資料庫。 因此,必須先設定config。 在app/config/database.php中,加入以下:

return [
 // .....
 // .....
 
   'connections' => [
     // ...
     // ...
   
    'mysql_test'=> [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE2', 'epa_test'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
      ],
      // ...
   ]
  //..
];

之後,去修改.env,加入DB_DATABASE2的值 artisan 指令:

php artisan migrate --database=mysql_test

controller

可以使用指令快速增加controller:

php artisan make:controller PhotoController --resource

--resource為定義此controller為一增刪修型的資源controller。

Migration

migration是laravel中用來管理資料庫版本的方式,它可以確保開發中的每一個人都使用最新版本的資料庫。 並且其中還有提供資料填充的功能,方便測式。

可以使用 artisan來建立migration:

php artisan migrate:make create_users_table

在執行migration時,會照著時間戳記裡執行。

php artisan migrate:make add_votes_to_user_table --table=users

php artisan migrate:make create_users_table --create=users
  • table: 用來指定資料表名稱
  • create: 指定建立新的資料表。

執行所有migration

php artisan migrate
php artisan migrate --package=vendor/package
  • package可以用來指定持定套件的migration

提示: 如果在執行遷移時發生「class not found」錯誤,試著先執行 composer dump-autoload 命令後再進行一次。

回復功能:

php artisan migrate:rollback
php artisan migrate:reset
php artisan migrate:refresh
  • rollback: 回復上一版
  • reset 全部回復
  • refresh 全部回復後重新執行

資料填充

用來產生測試資料。

php artisan db:seed
php artisan db:seed --class=UserTableSeeder

可以使用class來指定單獨執行的類別。

也可以使用migrate:refresh:

php artisan migrate:refresh --seed

seeder

php artisan make:seeder UsersTableSeeder

測試相關


想要一頭栽進自動化測試,所以學習記錄一下……

TDD

test-driven development,或稱TDD,是軟體工程中一種開發軟體的方法。它的主要概念很簡單,再開發功能的時候,都從測試開始寫起。 如此的好處有以下幾點:

  • 當你寫完,你可以很輕易的使用unit test工具驗証你寫的程式有沒有問題
  • 當你在還未開發前就有辦法寫測試,代表對功能有非常具體的了解。
  • 未來再加上新功能後,常常會有從前寫的code東爆西爆的狀況。若從開始就有好好的寫test,則可以非常輕易的就抓到在哪個環節出了問題。

然而,unit test的世界,包含思維要改變(每次在開發的時候,要從測試開始想起)、更花時間(如果不寫測試的話,直接一頭幹下去,速度應該是會快很多的;test的好處,在專案大到一定的程度,才會慢慢的顯現出來)、以及unit-test的工具有一定的學習門檻(太多不同的工具可以使用),其實之前一直想要學習這種開發模式,但總是忘而卻步。

這次學laravel,laravel框架剛好提供非常完整的測試環境及工具,以及有幸之前找到前人寫的不錯的測試教學。於是把自己所學的也記錄下來,除了給自己備忘,也 希望能幫助到其它人。

TDD開發流程 in laravel

其實任何的TDD開發流程應該都差不多。在laravel中,使用phpunit當做測試工具,所以當要開發一個新功能,流程都是一樣的:先寫測試→run測試→紅燈→寫code→run測試→紅燈→不斷修改直到正確→綠燈。

我是使用composer來安裝以管理laravel的,所以要使用php unit,指另如下:

vendor/bin/phpunit

這樣做會測試tests資料夾底下的所有測試。也可以指定資料夾或者是檔案執行。

laravel的框架會把所有該loading 以及 mapping的檔案準備好。若在執行測試的時候產生測試以外的error,可以嘗試重新執行dump-autoload

 composer dump-autoload

eloquent

eloquent是laravel提供的一個非常方便的控製資料庫的類別。在laravel中,每一個資料庫操作都對應一個model,這個model操做及為eloquent所提供的功能。

要建立一個model,可以使用以下指令:

php artisan make:model Flight

如果要順便產生migration檔案,可以加入參數 -m

產生出來的檔案(model)Flight.php如下:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

這裡有幾個規則:

  • table的名稱會是你的model的復數型。以例子來說,table名稱為flights

可以在model中定義table名稱:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_flights';
}
  • primary key:Eloquent 會預設primarykey為 id欄位,如果要修改primarykey,可以新增一個$primaryKey變數:
class Flight extends Model
{
    // ......
    protected $primarykey = "id2";   
    //......
}
  • Timestamps: Eloquent預設有 create_at以及 update_at欄位。你可以透過$timestaps=false來取消這個預設值,也可以透過const變數自定義兩個欄位的名稱,並可以使用 $dateFormat變數去改變日期的格式。

    • 取消 timestamp功能:
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}
  • 自定義格式:
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}
  • 自訂資料表名稱
<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

convensions在軟體工程中,是很重要的一塊。當限定了規則後,共同開發的大家使用相同的規則,管理起來比較方便。 既然都使用了這個框架,我的建議是,如果沒有特別必要的話,就不要自定義這些規則了,一方面使用方便,一方面也比較容易統一規格。

資料庫測試

做系統的工程師,最一開始會碰到的問題,應該就是資料庫要怎麼測試了吧。 然後因為要新增、assert、然後為了不污染現有資料庫,要刪除; 如果測試完全不想要跟真實資料庫有互動,就必須使用其它的套件來產生「假的」資料表物件。

剛開始要測試時,又希望資料庫中有些測試資料,才做功能測試…

感覺太麻煩了,最後干脆手動測試,然後就放棄TDD了。我是這樣啦…

laravel中提供了許多方便的功能,讓測試資料庫變的比較方便。

產生假資料

laravel提供兩個方法來產生測試資料:seed以及 factory。 正確的來說,seed是提供 migrate時產生假資料的實例;而factory是可以快速產生假資料的方法。 你可以在seed中使用factory。你也可以選擇不用factory,而使用model產生假資料。

技巧-測試的時候使用sqlLite:memory測試

當你的程式已經是正式的資料庫時(當然,實務上來說不可能會使用正式資料庫來做開發), 會不希望碰觸到資料庫中真實的資料,但又要簡單確認資料庫邏輯正不正確。

這時,可以使用這個技巧,把測試的database換成sqlite::memory對做測試。 說穿了其實就是在setUp時修改database的config檔。

use Illuminate\Support\Facades\Artisan;

class TestCase extends Illuminate\Foundation\Testing\TestCase {

    // ...

    // 每個 test case 都會重新初始化資料庫
    protected function initDatabase()
    {
        // 在測試時動態修改 config
        // 使其連接 sqlite
        config([
            'database.default' => 'sqlite',
            'database.connections.sqlite' => [
                'driver'    => 'sqlite',
                'database'  => ':memory:',
                'prefix'    => '',
            ],
        ]);

        // 呼叫 php artisan migrate
        // 及 php artisan db:seed
        Artisan::call('migrate');
        Artisan::call('db:seed');
    }

    // 重置資料庫
    // 讓下次測試不會被舊資料干擾
    protected function resetDatabase()
    {
        // 呼叫 php artisan migrate:reset
        // 這樣會把所有的 migration 清除
        Artisan::call('migrate:reset');
    }

error


error "No application encryption key has been specified."

今天遇到這個問題,上網查了一下,跟沒有密鑰有關。密鑰應該是跟安全有關。 從5.4開始,要先手動產生密鑰:

"Before using Laravel's encrypter, you must set a key option in your config/app.php configuration file. You should use the php artisan key:generate command to generate this key"

因此,解決這問題,應該使用指令:php artisan key:generate即可解決。

但是使用這個指令時,又遇到另一個問題:產生 no .env file error。 搜尋了一下,大部份的人遇到這個問題都是因為沒有把.env.example範例檔修改為.env, 但我目前實作的資料夾連.env 檔都沒有。 現在嘗試刪掉專案重新建立…

重新建立後,問題已修正,猜測是上一個專案為了建立git,開新git repostory後,將程式碼cp過去,可能沒有cp到.env檔。

How to fix Error: laravel.log could not be opened?

quote by https://stackoverflow.com/questions/23411520/how-to-fix-error-laravel-log-could-not-be-opened

Sometimes we need to do more, because

chmod -R 775 storage

Means 7 - Owner can write 7 - Group can write 5 - Others cannot write! If your webserver is not running as Vagrant, it will not be able to write to it, so you have 2 options:

chmod -R 777 storage

or change the group to your webserver user, supposing it's www-data:

chown -R vagrant:www-data storage

can't not read filestream error

起因是因為權限的問題。laravel中會把一些需要I/O的檔案, 如log檔,存放在storage資料夾底下,但是因為apache執行的使用者不為擁有者。 (如:apache以homestead執行I/O,但資料夾雍有讀寫權限的擁有者為howard。) 會造成沒有辦法寫檔而產生error

解決方式

原本想要找找看把apache的執行身份與資料夾使用者一致,但好像有點麻煩, 所以使用網路上通用的解:調整storage權限。

chmod -R 755 storage/

另外 這篇可能可以參考(改執行身份)

https://plus.google.com/101151239666088731701/posts/C8BRPWN2TZ3

Specified key was too long error

在新版的lavavel當中,使用全新的編碼方式(utf8mb4),這種編碼將顏文字也編入編碼中。 但舊版的mysql以及mariadb並不支援。

解決方法:把defaultStringLength調回舊版。 appServiceProvider.php :

use Illuminate\Support\Facades\Schema;

public function boot()
{
   Schema::defaultStringLength(191);
}

npm run dev error

剛剛想要把SASS的環境也灌進去,使用num init才發現laravel專案裡面早就包含了package.json了。

npm install 後,試著想要執行

npm run dev

結果一直跑出error, 上網查了一下, 原來是因為laravel的package.json預設裝了cross-env套件,這個套件只有windows需要 因為環境在 linux上,所以找不到這個套件,造成錯誤。 現在就把package.json中的cross-env都刪掉,重新npm install一次

  • 更新 找了一天,發現上面的問題其實只出現在mac電腦 ubuntu可以使用預設的package.json檔就ok了…

會發生這個問題,其實跟它的error message一樣, 真的是nodejs的版本太舊(可惡的apt-get)

所以就用最簡單的方法升級就好~

上網搜尋以後,我決定用n來控管node版本。

sudo npm cache clean -f
sudo npm install -g n
sudo n stable

記得重新開terminal。 然後

npm install
npm run dev

laravel mix

使用ubuntu 18,run 原生的run install會有錯誤 ubuntu 的npm的安裝方式,不小心解決就直接關掉了。不過應該很好找。

npm install會有error,主要原因是少了兩個dependency:

sudo apt install libpng-dev
sudo npm install cross-env -g

cross-env會安裝在global環境中。 之後,可以刪除專案資料夾底下的node_modules資料夾

sudo rm -rf node_modules
npm cache clear --force
rm package-lock.json yarn.lock
npm install
npm install --save-dev

--save-dev會安裝在dev裡面的套件。

另外,在網站上線以後,如果要更動內容,必須打開apache2的rewrite套件:

sudo a2enmod rewrite

lavarel/passport 安裝失敗

最近發生的問題。似乎是laravel passport的相依套件更新沒有跟舊版的merge好。 any,解法如下:

composer require paragonie/random_compat:2.*
composer require laravel/passport

laravel 認証方式記錄


laravel 5 中,提供了非常簡單而快速的Auth樣版,套用後,基本上可以直接使用它的樣版,系統的登入機制便會非常完整而安全。

為何說完整呢?因為laravel的auth樣版包含了 注冊、登入、忘記密碼、記住我等功能。 為何說安全呢?因為laravel的auth還包括了幫你定義好的 miidleware,可以在每一次登入的時候,使用guards驗証(預設http guard的方法是session。 在laravel中,不用擔心session,全部交給框架處理即可。);並且可以透過csrf認証,避免有心人事post資料進來。

總的來說,是非常方便的,加上php 5.4後提供的 traits功能,如果你趕時間的話,使用artisan指令建立auth後,幾乎可以立刻使用了。

php artisan指令:

php artisan make:auth

不過,缺點是,所有動作laravel都已經幫你定義好了。所以非常的不自由。如果你的登入模組有客製化的部分,應該要怎麼改呢?

我記錄一下我遇到的問題以及解決方法。


不負責任聲明

以下的內容,皆是我理解,並不一定都是正確的。所以我把這偏視做心得,並非教學。若有觀念錯誤的地方,歡迎與我討論。

如果照著我的流程,把app 玩爆,我不負任何責任 XD


要解決的問題

目前做的系統其實很單純,我們的系統需要登入機制,但暫時不需要註冊模組,也不需要忘記密碼跟記住我的功能,單純登入模組即可。

原本想說可以直接用laravel提供的流程,但是我的系統有一個非常蛋疼的地方:

密碼不是使用者自行設定,而是我們提供的亂碼。並且在管理後台中,管理者必需能看到各個使用者的密碼。 所以我們的資料庫中所設定的密碼,必須為明碼。這跟laravel提供的認証機制不同:

laravel提供的 `auth::attempt()``方法,會將密碼使用laravel所提供的加密法加密後,跟資料庫中已經加密的密碼做比對,符合才給登入。

另外,其實我的系統是從原本手刻的php,移植到laravel上,資料庫的schema其實已經定義好了。我的臭脾氣是,已經定義好的東西,我就不想要再去更動它… (這不一定是好的,如果laravel有 conventions,應該跟著它定義的convention,如此風格才會與大家一致。軟體工程中,風格一致是一件非常棒棒的事情。)

因此,要解決的問題主要有兩個

  1. 密碼認証機制要改掉。
  2. 有些 convetions 的地方要改客制化。

一些 convensions

先給沒有軟體工程中毒的人,或者是沒有軟體工程背景的人: convention直翻是"慣例"的意思。在軟體工程中,在一個專案的開發上,會希望有一些共同的規則。這些規則可能是變數命名的規則;也有可能是某些情況下寫code的風格。 定義規則的好處是,當多人同時合作時,風格一致,大家的code比較容易看懂、容易管理修改。

Eloquent model中,有一些conventions:

  • Eloquent中,資料表的名稱為class名稱的復數型態。其實我覺得這樣蠻合理的,而且處理復數型真的蠻貼心的… 舉例來說,laravel預設的任証model名稱為 User,則laravel在資料庫撈資料時,會預期資料表名稱為Users 若要修改這個convention,要自行定義protected $table變數。

  • Eloquent model中,假定primary key 為 名為id。 要修改這個convention,自行定義protected $primaryKey變數,注意,K是大寫……damn。

*可以定義一個protected $fillable變數,其為一個普通陣列。定義完後,可以使用 create直接快速新增資料,這種方法叫做"mass assignment"。

引用官網的範例:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

然後就可以使用create:

$flight = App\Flight::create(['name' => 'Flight 10']);

如果已經存在一個model實例,可以使用fill方法:

$flight->fill(['name' => 'Flight 22']);

以上是處理這問題的時候遇到的一些需要注意的convention。

範例流程

首先,需要有facadescontract的觀念。沒有的話,先上官網看吧。

簡單的來說,facades是laravel提供的工具集,為一群靜態的方法。意思是,不需要去定義,也不需要產生實例,就可以使用facade提供的functions。

contract定義一些核心功能的界面。如果有需要,可以自行實現contract介面。官網說明文件上寫,基本上,facadescontract應該是可以一對一的。

我們只聚焦範例的登入流程。

首先,login模組會檢查是否登入,如果已經登入了,則道入到登入後的頁面。 接著,使用者輸入表單,案下登入後,會呼叫login() 方法。此方法為範例中繼承的 Authenticatable類中實作的。

login會檢查使用者的帳號是否存在資料庫中。若存在,則將使用者輸入的密碼加密後,與資料庫中的密碼比對。 都沒有錯後,guard將使用者的資訊存進session中,並且授權登入,重新導入home(homeController)

homeController會先用middleware('auth')確認使用者是否已經登入(其實就是檢查sessions)。 如果有,就導入使用者需要看到的畫面。


修改

ok,所以要達到我們的需求,我們必須要手動登入流程,不使用官網提供的traits:

官網提供的方法

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /**
     * Handle an authentication attempt.
     *
     * @return Response
     */
    public function authenticate()
    {
        if (Auth::attempt(['email' => $email, 'password' => $password])) {
            // Authentication passed...
            return redirect()->intended('dashboard');
        }
    }
}

這是官網提供的手動流程,簡單來說,就是自行定義一個authenticate()方法,在裡面使用Auth::attemp()方法驗証登入。

BTW,官網的這例子寫的不好,它並沒有說明 $email跟 $password變數如何產生的。


注: 在laravel中,只要提供type-hint的變數(有指定型別的變數),因為service contaner的特性,會自動的將服務載入,這比較潮的名詞叫 "dependency injection"(依賴注入)。其實就是類似在類中的依賴,會有一個類似setter的方法,去載入這個依賴。如此一來,依賴可以被獨立(解偶合)出來,容> 易針對依賴做修改。 如果沒有 service provider 跟 service container的概念,也先去看官網吧。以我的理解,service container其實是任何可以裝載laravel服務的「容器」。事實上,所有在laravel中,實作出來的類都含有一個「容器」(透過$this->app可存取這個「容器」)。透過解析容器內的類,可以在實作出來的類中注入不同的依賴。


事實上,你可以在呼叫authenticate時,透過type-hint的方法,注入request,並從request中解析使用者post的資料。

  public function authenticate(Request $request)
   {
       $UserId = $request -> UserId;
       $password = $request -> password;
       
       .......
   }

事實上,依官網文件,比較好的做法,應該是將注入放在constructor中。

問題

這其中還有個比較讓人蛋疼的問題:attempt(['email' => $email, 'password' => $password]) 中, email為資料表中的欄位名稱,這沒有問題;但是'password'並不能隨便改名,因為官網提供的attempt源碼中,password是直接寫死去資料庫撈資料的。所以,如果密碼欄位不叫password,那就注定要自己寫登入邏輯了。

attempt認証失敗,原因是因為之前提到的:原方法會使用加密方法加密後,跟資料庫中已加密的密碼做比對。

因此,要怎麼解決這個問題呢?

解決

目前查到有兩個方法。

  1. 抽換hash方法。 參考 https://laravel-china.org/articles/5963/toggle-laravel-login-default-bcrypt-encryption-validation 範例為將hash方法換成md5。 只要將md5方法改寫為不用任何hash方法即可。

  2. 更手動的認証

attempt將認証的方式包起來了。所以我們可以實作認証邏輯。

從官網可以得知,Auth::login($user)可以將一個存在的使用者登入app。 另外 Auth::check()可以查詢登入狀態。

因此,邏輯其實很簡單,與資料庫比對使用者帳號與密碼,沒問題就呼叫Auth::login($user)登入即可。

這裡有一個官網寫的不清楚的地方,$user是從哪來的?

引用官網:

The given object must be an implementation of the Illuminate\Contracts\Auth\Authenticatable contract. Of course, the App\User model included with Laravel already implements this interface:

重點是,這個物件需要實作 Authenticatable contract。 所以,這個實例應該從哪裡來呢?

我原本以為是將注入的user類執行搜尋後,將之傳入,結果出現error(認証失敗)。 原來,eloquent在執行完任何搜尋後,回傳的結果是原來的類的實例, 所以就是傳這個實例給login就對了…

範例:

public function authenticate(Request $request)
   {
       $UserId = $request -> UserId;
       $password = $request -> password;

       // 從資料庫中搜尋UserId是否有資料
       $User = User::where('UserId', $UserId)->first();
       // dd(print_r($check, true));
       if($User) {
         //確認密碼相符
         if($User['Password'] == $password) {
           Auth::login($User);
         }
       }
        // Authentication passed...
        return redirect()->intended('home');


   }
@idhowardgj94
Copy link
Author

webpack的設定檔,resolve.alias
別名的路徑,他不認識上一層(../),經過我好多次的實驗,應該是這樣 = =
它可以指定當前目錄(./),但沒辦法返回上一層

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment