Mars's Blog

MySQL時間型態的毫秒與微秒

一、說明

平常用到MySQL時間欄位 DATETIME, TIMESTAMP, TIME 類型大多使用「秒」為最小單位,但其實這三種資料型態可以支援到小數下6位的精確度。

如果資料表沒有 unique 的需求(如Log),可以使用適當精度的時間欄位當索引。

二、環境建置

2.1 時間型態

2.2 建立資料表

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `test_microsecond`(
`pk` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`batch` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0,
`time1` TIMESTAMP NULL DEFAULT NULL,
`time2` DATETIME NULL DEFAULT NULL,
`time3` TIMESTAMP(3) NULL DEFAULT NULL,
`time4` DATETIME(3) NULL DEFAULT NULL,
`time5` TIMESTAMP(6) NULL DEFAULT NULL,
`time6` DATETIME(6) NULL DEFAULT NULL,
`time7` DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY(`pk`),
KEY `time6`(`time6`)
) ENGINE = InnoDB CHARSET = utf8;

欄位 time7 可採用預設值方式自動獲取內容

2.3 插入測試資料

一次查詢中插入一筆

1
2
INSERT INTO test_microsecond(`batch`, `time1`, `time2`, `time3`, `time4`, `time5`, `time6`) VALUES
(1, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), CURRENT_TIMESTAMP(6), NOW(6));

一次查詢中插入二筆

1
2
3
INSERT INTO test_microsecond(`batch`, `time1`, `time2`, `time3`, `time4`, `time5`, `time6`) VALUES
(2, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), CURRENT_TIMESTAMP(6), NOW(6)),
(2, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), CURRENT_TIMESTAMP(6), NOW(6));

CURRENT_TIMESTAMP()、NOW()取得的值皆為本次session的時間

一次查詢中插入三筆 - SYSDATE()

1
2
3
4
INSERT INTO test_microsecond(`batch`, `time1`, `time2`, `time3`, `time4`, `time5`, `time6`) VALUES
(3, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), SYSDATE(6), NOW(6)),
(3, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), SYSDATE(6), NOW(6)),
(3, CURRENT_TIMESTAMP(), NOW(), CURRENT_TIMESTAMP(3), NOW(3), SYSDATE(6), NOW(6));

SYSDATE()取得的是系統時間,不同於CURRENT_TIMESTAMP()、NOW()取得的值皆為本次session的時間

三、參考

Git常用指令學習

一、說明

版本控制在軟體開發中非常重要,有了版本控制不但可以記錄軟體變動脈絡,還能多線開發、合併、摘取、回遡、差異比對等功能,在團隊合作中尤其重要。

版本控制用指令很多,在只介紹Git在開發中的常用指令。

二、環境準備

  • 於 GitHub 中建立 repository: git-learning

    • 登入Github並點選右上角+號,再選New repository
    • 填寫資料
  • 取得 repo: git-learning 的uri

    • 進入git-learning後點選 clone or download,並複製網址

      上圖採用ssh方式與git連線

三、指令介紹

環境設定

設定Git操作者的名子與信箱

git使用前,需設定作者資訊

1
2
$ git config --global user.name {我的名子}
$ git config --global user.email {我的信箱}

註:{我的名子},{我的信箱}代表變數,在此輸入Git操作者名子,信箱

語系設定

Linux環境下git操作需設定執行語系,以免看到亂碼

1
2
3
4
$ echo "export LESSCHARSET=utf-8;" | sudo tee -a ~/.bashrc
$ echo "export LESSCHARSET=utf-8;" | sudo tee -a /etc/bash.bashrc
$ echo "export LC_ALL=en_US.UTF-8;" | sudo tee -a ~/.bashrc
$ echo "export LC_ALL=en_US.UTF-8;" | sudo tee -a /etc/bash.bashrc

原因是 git log 預設是使用 less 這個工具來瀏覽,所以需要額外設定編碼。

編輯器

設定操作git編輯時使用的編輯器,推荐vim或vi

1
$ git config --global core.editor vim

將操作Git的編輯器設定成vim,如無vim可改用vi

與遠端協同工作

Git工作分本地端(local)、遠端(remote),自己在本地端的任何變更需推送(push)到遠端,其他人才可以獲取(fetch)自己的變動內容。

下載遠端 repository

下載遠端資源庫至本地端工作目錄中。

  • 指令 git clone
    1
    2
    3
    # 格式:git clone <repo> [<directory>]
    # 範例:
    $ git clone git@github.com:marshung24/git-learning.git
    • 克隆目標資源庫
    • 克隆成功後,可在執行目錄下看到剛剛克隆的專案目錄

推送至遠端

推送本地端變動至遠端。

  • 指令 git push
    1
    2
    3
    # 格式:git push
    # 範例:
    $ git push
    • 將本地commit推送至遠端伺服器
    • 強制推送:git push -f

獲取遠端記錄

獲取遠端的變動記錄。當他人有推送變動時,需使用本指令獲取最新記錄。

  • 指令 git fetch
    1
    2
    3
    # 格式:git fetch
    # 範例:
    $ git fetch
    • 當多人協同作業時,要看到其他人推送至伺服器的結果時使用 git fetch 取得

獲取並合併

  • 指令 git pull
    1
    2
    3
    # 格式:git pull <branch>
    # 範例:
    $ git pull master

    git pull 效果等於 git fetch 加 git merge

查看記錄

查看記錄

  • 指令 git log
    1
    2
    3
    # 格式:git log
    # 範例:
    $ git log

查看HEAD的移動記錄

  • 指令 git reflog
    1
    2
    3
    # 格式:git reflog
    # 範例:
    $ git reflog
    • 如果誤操作時,可用此找查之前的記錄
    • 有commit過的檔案被蓋掉,可通過git reflog、git reset還原

檔案變動管理

查看目前變動

  • 指令 git status
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 格式:git status
    # 範例:
    # 查看目錄下的檔案清單
    $ ls
    LICENSE README.md
    # 建立空檔案 index.php
    $ touch index.php
    # 在檔案 README.md 最後增加一新行,文字為test
    $ echo "test" >> README.md
    # 查看目前變動
    $ git status

    可看到一個檔案變動 README.md ,一個未處理檔案 index.php

暫存變動:

  • 指令 git add
    1
    2
    3
    4
    # 格式:git add <file>
    # 範例:
    $ git add README.md
    $ git status

    暫存變動,使用git status查看目前變動

暫存全部變動:
git add -u

不含未處理過的檔案

暫存全部變動:
git add .

含未處理過的檔案

暫存路徑變動: git add

含未處理過的檔案

取消暫存變動:

  • 指令 git reset HEAD
    1
    2
    3
    4
    # 格式:git reset HEAD <file>
    # 範例:
    $ git add README.md
    $ git status

    取消不小心 git add 的檔案

收藏

收藏變動

  • 指令 git stash
    1
    2
    3
    # 格式:git stash
    # 範例:
    $ git stash
    • 承 查看目前變動 狀況下做git stash,只會收藏Git處理過的檔案,不會收藏Git未處理過的檔案
    • 收藏後,檔案會回到變動前的狀況

查看收藏:

  • 指令 git stash list
    1
    2
    3
    # 格式:git stash list
    # 範例:
    $ git stash list

    圖中可看到目前有一筆收藏

取出收藏:

  • 指令 git stash pop
    1
    2
    3
    # 格式:git stash pop
    # 範例:
    $ git stash pop

    取出收藏後,檔案會回到變動後的狀況

提交:

  • 指令 git commit
    1
    2
    3
    4
    # 格式:git commit
    # 範例:
    $ git add README.md
    $ git commit
    • 提交前(git commit)必需暫存變動(git add)
    • 不進編輯器直接提交
      • git commit -m “提交內容描述”
    • 修改最近的提交記錄
      • git commit –amend

分支管理

查看分支

  • 指令 git branch
    1
    2
    3
    # 格式:git branch
    # 範例:
    $ git branch

    不加任何參數時為查看分支

建立新分支

  • 指令 git branch
    1
    2
    3
    4
    5
    6
    # 格式:git branch
    # 範例:
    # 以SHA-1 commit位置為基底建立branch_a
    $ git branch branch_a cbbb519abe0b959443c22aab38fe8cab9702eb2b
    # 以master位置為基底建立branch_b
    $ git branch branch_b master
    • 不會checkout到該分支
    • 推送剛建立的分支new_branch至遠端
      • git push origin new_branch

刪除分支

  • 指令 git branch -d
    1
    2
    3
    4
    # 格式:git branch -d <branch>
    # 範例:
    # 刪除分支new_branch (本地端)
    $ git branch -d new_branch

    有未合併資料時不可刪

  • 強制刪除:git branch -D
  • 刪除遠端分支:git push origin :
    • 使用git push 並在分支名前加上冒號(:)

合併分支: git merge

切換分支: git checkout

清除變動檔案: git checkout

清除分支所有變動: git checkout – .

變更工作目錄至commit: git reset

只移動branch位置,不變動檔案

變更工作目錄至commit: git reset –hard
移動branch位置,並重置檔案

建立tag: git tag

推送tag: git push origin

同等: git push origin refs/tags/

刪除tag: git tag -d

刪除遠端tag: git push origin refs/tags/
必需先把本地tag刪除(推送空tag)

變基: git rebase -i

摘取: cherry-pick

worktree

git worktree add -b hotfix ../hotfix master
git worktree prune
git worktree list
git worktree lock

Web伺服器安裝(XAMPP)-Windows

一、說明

安裝網頁執行平台,以供開發作業

很多人都知道,windows有不少PHP開發環境的安裝包,如xampp, appserv, wamp,雖然一個成熟的開發者,是不會用和線上不一樣的環境開發的,但入門時可安裝這些套件來學習開發。

二、環境

  • windows
  • xampp 7.1
    • Apache 2.4
    • MariaDB 10.4
    • PHP 7.1

三、安裝

  • 前往XAMPP官網下載

    下載windows版安裝檔

  • 安裝軟體
  • 選擇安裝內容
  • 選擇安裝目錄
  • 過程
  • 開啟防火牆
  • 安裝結束,啟動管理面版

    啟動伺服器

  • 設定與管理

四、設定

4.1 增加虛擬站台

  • 打開設定檔 D:\xampp\apache\conf\extra\httpd-vhosts.conf
  • 新增設定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <VirtualHost *:80>
    DocumentRoot "D:/xampp/htdocs/site1"
    ServerName site1.dev.idv
    </VirtualHost>

    <VirtualHost *:80>
    DocumentRoot "D:/xampp/htdocs/site2"
    ServerName site2.dev.idv
    </VirtualHost>
  • 新增虛擬站台工作目錄

    在 D:/xampp/htdocs/ 中新增目錄 site1, site2

  • 增加站台首頁檔
    • Site1
      檔案:D:/xampp/htdocs/site1/index.php
      內容
      1
      2
      <?php
      echo "Hello Site 1...";
    • Site2
      檔案:D:/xampp/htdocs/site2/index.php
      內容
      1
      2
      <?php
      echo "Hello Site 2...";

4.2 設定DNS解析

  • 如果你的域名不是DNS可查到的,請在作業系統的hosts中增加解譯規則
  • 使用記事本打開 C:\Windows\System32\drivers\etc\hosts (需用管理者權限打開)
  • 在檔案最下方增加二行設定
    • 127.0.0.1 site1.dev.idv
    • 127.0.0.1 site2.dev.idv

五、檢視成果

CRUD表單設計 Day-04 Javascript

一、說明

「互動式網頁」概念由來已久,但實作的的方式一直不斷更新,如javascript, Java applet, flash, css等。其中Javscript支援度高、應用廣、學習容易,很不錯的語言。

  • 在此透過jQuery來使用Javascript, AJAX
  • 本文中採用Google Chrome瀏覽器及其開發人員工具

二、主要應用

  • 選擇器(Selector)
  • DOM控制
    • 結構
    • 事件
  • 資料處理
  • AJAX

三、練習模版

3.1 增加路徑常數

  • 打開檔案
    application/config/constants.php

  • 加入設定

    1
    2
    3
    4
    5
    6
    /**
    * System Path
    */
    define('ASSETS', '/assets/');
    define('CSS_DIR', ASSETS . 'css/');
    define('JS_DIR', ASSETS . 'js/');
  • 建立資料夾
    assets/js/
    assets/css/

3.2 建立Controller

  • 新增檔案
    application/controllers/Js_training.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');

    class Js_training extends CI_Controller
    {

    /**
    * Index Page for this controller.
    */
    public function index()
    {
    $this->load->view('jsTraining');
    }
    }

3.3 建立View

  • 新增檔案
    application/views/jsTraining.php

    以此為HTML練習模版

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');
    ?>
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <title>Bootstrap 4 Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
    <!-- Font Awesome Icon -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" />

    <script src="<?= JS_DIR; ?>jsTraining/jsTraining.js"></script>
    </head>

    <body>

    <div class="container">
    <!-- Title -->
    <div class="row">
    <div class="col-sm-12">
    <h1>Javascript Training</h1>
    <hr>
    </div>
    </div>

    <!-- Controll Form -->
    <div class="row">
    <div class="col-sm-12"><button class="btn btn-warning float-right ctrl-btn">新增</button></div>
    </div>

    <!-- Table -->
    <div class="row">
    <table class="table table-striped table-bordered table-hover ctrl-table">
    </table>
    </div>

    <!-- Message box -->
    <div class="row">
    <div id="ctrl-message" class="text-danger ctrl-message"></div>
    </div>

    </div>

    </body>

    </html>

3.4 建立JS檔

  • 新增檔案
    assets/js/jsTraining/jsTraining.js

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    /**
    * 說明:
    * <li>1. 頁面函式只會初始化一次
    * <li>2. 如果是多頁面組合時,可能被其他頁面呼叫,因此需使用namespane:Page,以方便外部呼叫或試調
    *
    * 執行順序:
    * 1. 註冊$(document).ready()函式,但先不執行
    * 2. $(document).ready()之外的程式碼依序執行 - 建構變數、函式obj
    * 3. 執行$(document).ready()內註冊的函式
    * 4. 確定window.Page是否存在,不存在則初始化
    * 5. 執行obj()物件,並將結果存入window.Page[name]
    * 6. obj()回傳內容為 new obj.fn.init(options);
    * 7. 實例化obj.fn.init(options);並在最後執行函式 _construct(_options);
    */

    // IIFE 立即執行函式
    (function(window, document, $, undefined) {
    // 使用嚴格模式
    'use strict';

    // DOM下載完後執行
    $(document).ready(function() {
    // init this page
    window.Page = window.Page || new function() {}();
    window.Page[name] = obj();
    });

    // Class Name
    var name = '{name}';
    // Version
    var version = '{version}';
    // Default options
    var defaults = {};

    /**
    * *************** Object Build ***************
    */

    // Define a local copy of Object
    var obj = function(options) {
    return new obj.fn.init(options);
    };

    // Prototype arguments
    obj.fn = obj.prototype = {
    // Object Name
    _name: name,

    // Default options
    _defaults: defaults,

    // AJAX URL
    _ajaxUrls: {
    list: '/controller_group_forder/controller/ajax',
    edit: '/controller_group_forder/controller_edit/ajax',
    },
    };

    /**
    * Javascript物件
    */
    obj.fn.init = function(options) {
    /**
    * *************** Object Argument Setting ***************
    */
    var self = this;
    var _options = options || {};
    // Ajax Response - jqXHR(s)
    var _jqXHRs;

    /**
    * *************** 屬性設定 ***************
    */

    /**
    * *************** 物件必要函式 ***************
    */

    /**
    * 建構子
    */
    var _construct = function() {
    console.log('_construct');

    _initialize();
    };

    /**
    * 解構子
    */
    var _destruct = function() {};

    /**
    * 初始化
    */
    var _initialize = function() {
    console.log('_initialize');

    /**
    * 事件綁定
    */
    _evenBind();
    };

    /**
    * 事件綁定
    */
    var _evenBind = function() {
    console.log('_evenBind');

    /**
    * 事件 - 增加
    */

    /**
    * 事件 - 清除規
    */
    };

    /**
    * *************** 功能函式 ***************
    */

    /**
    * *************** 事件函式 ***************
    */

    /**
    * 事件 - 送出
    */
    var _submit = function(e) {
    return this;
    };

    /**
    * 事件 - 清除
    */
    var _clear = function(e) {
    return this;
    };

    /**
    * 事件 - 增加
    */
    var _add = function(e) {
    return this;
    };

    /**
    * *************** 私有函式 ***************
    */

    /**
    * *************** Run Constructor ***************
    */
    _construct();
    };

    // Give the init function the Object prototype for later instantiation
    obj.fn.init.prototype = obj.prototype;

    // Alias prototype function
    $.extend(obj, obj.fn);
    })(window, document, $);

    物件化Javascript模版

3.5 查看結果

四、選擇器(jQuery Selector)

使用tag, id, class取得DOM,並讀取物件內容

4.1 使用tag selector

讀取tag:h1內容字串
在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
3
4
5
6
// 讀取tag:h1內容字串
var text = $('h1').text();
// 在DevTools的console介面中印出
console.log('----');
console.log('tag:h1的內容為:' + text);
console.log('----');
  • 按F12打開「開發人員工具」,移到console頁籤
  • JS翻釋:使用tag選擇器尋找名為h1的元件,並取得元件內容字串
  • tag為HTML標籤名稱,參考文件 HTML5 Tutorial

4.2 使用id selector

寫入「測試字串」至id=”ctrl-message”的元件
在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
// 寫入「測試字串」至id="ctrl-message"的元件
$('#ctrl-message').text('測試字串');
  • JS翻釋:使用ID選擇器(#)尋找id為ctrl-message的元件,並將字串「測試字串」寫入元件內容

4.3 使用class selector

變更class=”ctrl-btn”按鈕的元件的顏色為btn-danger
在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
// 變更class="ctrl-btn"按鈕的元件的顏色為btn-danger
$('.ctrl-btn').removeClass('btn-warning').addClass('btn-danger');
  • JS翻釋:使用class選擇器(.)尋找含有class為ctrl-btn的元件,並移除class btn-warning、增加class btn-danger

4.4 查看結果

五、DOM控制

5.1 結構

5.1.1 移除按鈕

在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
// 移除按鈕
$('.ctrl-btn').remove();
  • JS翻釋:使用class選擇器(.)尋找含有class為ctrl-btn的元件,並移除該元件

5.1.2 建立表格

在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 建立表格
*/
// 建立變數
var tmp, table, thead, tbody, tr, th, td;
// 建立暫存容器
tmp = $('<div></div>');
// 建立thead區塊資料,並放到tmp之中
thead = $('<thead></thead>').appendTo(tmp);
// 建立tr標籤,並放到thead之中
tr = $('<tr></tr>').appendTo(thead);
// 建立th標籤,並放到tr之中
th = $('<th></th>').appendTo(tr);
// 寫入th內容文字
th.text('Count');
th = $('<th></th>').appendTo(tr);
th.text('Name');
th = $('<th>Gender</th>').appendTo(tr);

// 建立tbody區塊資料
tbody = $('<tbody></tbody>').appendTo(tmp);
tr = $('<tr></tr>').appendTo(tbody);
td = $('<td></td>').appendTo(tr);
td.text('1');
td = $('<td></td>').appendTo(tr);
td.text('Joe');
td = $('<td>male</td>').appendTo(tr);

// 取得table元件
table = $('.ctrl-table');
// 將暫存容器內容移至table元件
tmp.children().appendTo(table);

// 印出table元件HTML碼
console.log(table.html());

參考文件:jQuery Add, jQuery Remove

5.2 事件

5.2.1 按下按鈕時,變更tag:h1文字為「** Javascript Training **」

在函式 _evenBind 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
3
4
// 按下按鈕時,變更tag:h1文字為「** Javascript Training **」
$('.ctrl-btn').on('click', function() {
$('h1').text('** Javascript Training **');
});
  • JS翻釋:
    • 當class為ctrl-btn的元件,在(on)按下click時,執行匿名函式
    • 匿名函式:對tag為h1的元件內容改寫為「** Javascript Training **」

5.2.2 偵測滑鼠座標

在函式 _evenBind 中寫入下列程式碼後,按ctrl+F5重整網頁

1
2
3
4
5
6
7
8
9
10
// 偵測滑鼠座標
$(document).on('mousemove', function(ev) {
// 取得事件物件
ev = ev || window.event;
// 如果事件物件含有座標資訊
if (ev.pageX || ev.pageY) {
// 在ctrl-message元件中印出座標資訊
$('.ctrl-message').html('X: ' + ev.pageX + '<br>Y: ' + ev.pageY);
}
});
  • JS翻釋:
    • 當在整個文件區(document)中偵測到滑鼠移動事件(mousemove)時,執行匿名函式
    • 匿名函式:取得事件物件,如果事件物件含有座標資訊,在ctrl-message元件中印出座標資訊

參考文件:jQuery Events

5.3 查看結果

六、AJAX

AJAX即「Asynchronous JavaScript and XML」(非同步的JavaScript與XML技術),使用AJAX執行前後端資料交換有助於減少資料量,非同步特性讓ajax處理時,可同時處理本其他JS程式,讓網頁執行更順暢更多樣化。

AJAX的目的可粗略分為建立(create)、讀取(read)、更新(update)、刪除(delete)。

在此使用RESTFul Style實作ajax前後端資料交換

6.1 修改AJAX server side相關路徑

編輯檔案:assets/js/jsTraining/jsTraining.js

1
2
3
4
5
// AJAX URL
_ajaxUrls: {
// Account CRUD AJAX server side url.
accountApi: '/js_training/ajax',
},

6.2 建立伺服器端接收函式

檔案:application/controllers/Js_training.php
增加下列函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* AJAX controller.
*/
public function ajax($id = null)
{
// 參數處理
$method = strtoupper($_SERVER['REQUEST_METHOD']);
// 此處應有對傳入參數$_POST消毒的處理,此處簡化
//parse_str(file_get_contents('php://input'), $data);
$data = $this->input->input_stream();

// 行為分類
switch ($method) {
case 'POST':
// 新增一筆資料
$this->_create($data);
break;
case 'GET':
if (empty($id)) {
// 讀取全部資料
$this->_list();
} else {
// 讀取一筆資料
$this->_read($id);
}
break;
case 'PATCH':
case 'PUT':
// 更新一筆資料
$this->_update($data, $id);
break;
case 'DELETE':
if (empty($id)) {
// 錯誤
http_response_code(404);
echo 'No Delete ID';
exit;
} else {
// 刪除一筆資料
$this->_delete($id);
}
break;
}
}

/**
* 新增一筆
*
* @param array $data
* @return array
*/
protected function _create($data)
{
// 建立輸出陣列
$opt = [
// 行為:新增一筆
'type' => '新增一筆',
// 前端AJAX傳過來的資料
'data' => $data,
];

// 輸出JSON
echo json_encode($opt);
}

/**
* 讀取全部
*
* @return array
*/
protected function _list()
{
// 建立輸出陣列
$opt = [
// 行為:讀取全部
'type' => '讀取全部',
// 標題資料
'head' => [
'name',
'location',
],
// 多筆內容資料
'data' => [
[
'name' => 'John',
'location' => 'Boston',
],
[
'name' => 'Joe',
'location' => 'New York',
],
[
'name' => 'Gary',
'location' => 'Taipei',
],
],
];

// 輸出JSON
echo json_encode($opt);
}

/**
* 讀取一筆
*
* @param int $id 目標資料id
* @return array
*/
protected function _read($id)
{
// 建立輸出陣列
$opt = [
// 行為:讀取一筆
'type' => '讀取一筆',
// 前端AJAX傳過來的資料
'id' => $id,
];

// 輸出JSON
echo json_encode($opt);
}

/**
* 更新一筆
*
* @param array $data 資料內容
* @param int $id 目標資料id
* @return array
*/
protected function _update($data, $id)
{
// 建立輸出陣列
$opt = [
// 行為:更新一筆
'type' => '更新一筆',
// 前端AJAX傳過來的資料
'data' => $data,
'id' => $id,
];

// 輸出JSON
echo json_encode($opt);
}

/**
* 刪除一筆
*
* @param int $id 目標資料id
* @return string
*/
protected function _delete($id)
{
// 建立輸出陣列
$opt = [
// 行為:刪除一筆
'type' => '刪除一筆',
// 前端AJAX傳過來的資料
'id' => $id,
];

// 輸出JSON
echo json_encode($opt);
}

6.3 Create

6.3.1 程式碼

  • 新增一筆
    在函式 _initialize 中寫入下列程式碼後,按ctrl+F5重整網頁
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 新增一筆
    */
    $.ajax({
    // 傳送方法
    method: 'POST',
    // 目標網址
    url: self._ajaxUrls.accountApi,
    // 傳送資料
    data: { name: 'John', location: 'Boston' },
    // 回傳資料格式
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料 - 印出json字串
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));
    // 輸出至console
    console.log(data);
    }).fail(function (jqXHR) {
    // 處理回傳資料
    $('<div>' + jqXHR.responseText + '</div>').appendTo($('.ctrl-message'));
    console.log(jqXHR);
    });

    示範AJAX Request & Reponse

6.3.2查看結果

6.4 Read

  • 讀取全部

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /**
    * 讀取全部
    */
    $.ajax({
    method: 'GET',
    url: self._ajaxUrls.accountApi,
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料 - 印出json字串
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));

    /**
    * 陣列資料配合$.each建立表格
    */
    // 建立變數
    var tmp, table, thead, tbody, tr, th, td;
    // 建立暫存容器
    tmp = $('<div></div>');
    // 建立thead區塊資料
    thead = $('<thead></thead>').appendTo(tmp);
    // 建立tbody區塊資料
    tbody = $('<tbody></tbody>').appendTo(tmp);

    // 建立標題
    tr = $('<tr class="bg-info"></tr>').appendTo(thead);
    $.each(data.head, function(index, value) {
    th = $('<th>'+value+'</th>').appendTo(tr);
    });

    // 建立內容
    $.each(data.data, function(index1, value1) {
    tr = $('<tr></tr>').appendTo(tbody);
    $.each(value1, function(index2, value2) {
    td = $('<td>'+value2+'</td>').appendTo(tr);
    });
    });

    // 取得table元件
    table = $('.ctrl-table');
    // 將暫存容器內容移至table元件
    tmp.children().appendTo(table);
    });

    陣列資料配合$.each建立表格

  • 讀取一筆

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 讀取一筆
    */
    $.ajax({
    method: 'GET',
    // 讀取id為3的資料
    url: self._ajaxUrls.accountApi + '/3',
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));
    });

參考文件:$.each()

6.5 Update

  • 更新一筆
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 更新一筆
    */
    $.ajax({
    method: 'PUT',
    url: self._ajaxUrls.accountApi,
    data: { name: 'John', location: 'Boston' },
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));
    });

6.6 Delete

  • 刪除錯誤 No Delete ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * 刪除錯誤 No Delete ID
    */
    $.ajax({
    method: 'DELETE',
    url: self._ajaxUrls.accountApi,
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));
    }).fail(function (jqXHR) {
    // 錯誤處理
    $('<div>' + jqXHR.responseText + '</div>').appendTo($('.ctrl-message'));
    console.log(jqXHR);
    });
  • 刪除一筆

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 刪除一筆
    */
    $.ajax({
    method: 'DELETE',
    // 刪除id為2的資料
    url: self._ajaxUrls.accountApi + '/2',
    dataType: 'json',
    }).done(function(data) {
    // 處理回傳資料
    $('<div>' + JSON.stringify(data) + '</div>').appendTo($('.ctrl-message'));
    }).fail(function (jqXHR) {
    // 錯誤處理
    $('<div>' + jqXHR.responseText + '</div>').appendTo($('.ctrl-message'));
    console.log(jqXHR);
    });

參考文件:

6.7 資料交換要點

  • 使用方法POST,GET,PUT,DELETE分別對應新增,讀取,修改,刪除
  • 網址格式為:https://host_name/controller_name/function_name/parameter
  • 回傳的資料格式為json
  • POST,PUT需傳入data屬性,值為要新增/更新的資料
  • 使用callback函式done()處理成功回傳的資料
  • 使用callback函式fail()處理失敗回傳的資料
  • 需定義好從 前端傳到後端 與 後端傳到前端 的資料結構,以免資料處理雜亂

七、參考

7.1 知識

7.2 官網

7.3 教程


未完待續

CRUD表單設計 Day-03 安裝Bootstrap4

一、說明

隨者技術進步,網頁表現在更愈趨複雜多變,尤其是手機及平板普及帶來的顯示需求變化(如RWD),讓網頁前端開發難度大為提升。因此,利用前端框架輔助開發更顯重要。

在此使用Bootstrap4

二、安裝

2.1 使用NPM安裝

  • 在vscode終端機上輸入指令
    1
    $ npm install bootstrap jquery popper.js
    • 安裝完成後,資料會放在node_modules之下
    • 推送至Git

三、使用Bootstrap

3.1 建立第一個bootstrap頁面

  • 新增Controller
    application/controllers/Hello_bootstrap.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    defined('BASEPATH') OR exit('No direct script access allowed');

    class Hello_bootstrap extends CI_Controller {

    public function index()
    {
    $this->load->view('helloBootstrap');
    }
    }
  • 新增View:
    application/views/helloBootstrap.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');
    ?>
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <title>Bootstrap 4 Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/popper.js"></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
    </head>

    <body>

    <div class="container">
    <h1>Hello Bootstrap !</h1>
    <p>This is some text.</p>
    </div>

    </body>

    </html>
  • 查看結果
    前往網址:https://crud-training.dev.idv/hello_bootstrap

    上面的hello_bootstrap,是Controller名稱Hello_bootstrap字首轉小寫而來 (只有字首會轉小寫)

3.2 常用Bootstrap元件

  • Grid
  • Colors
  • Forms
  • Modal
  • Tooltip
  • Popover
  • Tables
  • Buttons
  • Dropdowns
  • Navs, Navsbar
  • Pagination

四、練習範例

  • 新增Controller
    application/controllers/Bootstrap_example.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    defined('BASEPATH') OR exit('No direct script access allowed');

    class Bootstrap_example extends CI_Controller {

    public function index()
    {
    $this->load->view('bootstrapExample');
    }
    }
  • 新增View:
    application/views/bootstrapExample.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');
    ?>
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <title>Bootstrap 4 Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/popper.js"></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
    <!-- Font Awesome Icon -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" />
    </head>

    <body>

    <div class="container">
    <!-- Title -->
    <div class="row">
    <div class="col-sm-12">
    <h1>帳號管理</h1>
    </div>
    </div>

    <!-- Controll Form -->
    <div class="row">
    <div class="col-sm-4 form-group">
    <input type="password" class="form-control" id="pwd">
    </div>
    <div class="col-sm-4"><button class="btn btn-primary">搜尋</button></div>
    <div class="col-sm-4"><button class="btn btn-warning float-right" data-toggle="modal" data-target="#myModal">新增</button></div>
    </div>

    <!-- Table -->
    <div class="row">
    <table class="table table-striped table-bordered table-hover">
    <thead>
    <tr>
    <th>&nbsp</th>
    <th>Firstname</th>
    <th>Lastname</th>
    <th>Email</th>
    </tr>
    </thead>
    <tbody>
    <tr>
    <td><i class="fas fa-pen color_green ml-3"></i> <i class="fas fa-trash text-danger ml-3"></i></td>
    <td>John</td>
    <td>Doe</td>
    <td>john@example.com</td>
    </tr>
    <tr>
    <td><i class="fas fa-pen color_green ml-3"></i> <i class="fas fa-trash text-danger ml-3"></i></td>
    <td>Mary</td>
    <td>Moe</td>
    <td>mary@example.com</td>
    </tr>
    <tr>
    <td><i class="fas fa-pen color_green ml-3"></i> <i class="fas fa-trash text-danger ml-3"></i></td>
    <td>July</td>
    <td>Dooley</td>
    <td>july@example.com</td>
    </tr>
    </tbody>
    </table>
    </div>

    <!-- Pagination -->
    <div class="row float-right">
    <ul class="pagination">
    <li class="page-item"><a class="page-link" href="#">Previous</a></li>
    <li class="page-item"><a class="page-link" href="#">1</a></li>
    <li class="page-item"><a class="page-link" href="#">2</a></li>
    <li class="page-item"><a class="page-link" href="#">3</a></li>
    <li class="page-item"><a class="page-link" href="#">Next</a></li>
    </ul>
    </div>
    </div>


    <!-- The Modal -->
    <div class="modal" id="myModal">
    <div class="modal-dialog">
    <div class="modal-content">

    <!-- Modal Header -->
    <div class="modal-header">
    <h4 class="modal-title">Modal Heading</h4>
    <button type="button" class="close" data-dismiss="modal">&times;</button>
    </div>

    <!-- Modal body -->
    <div class="modal-body">
    Modal body..
    </div>

    <!-- Modal footer -->
    <div class="modal-footer">
    <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
    </div>

    </div>
    </div>
    </div>

    </body>

    </html>

    請自行練習Bootstrap元件使用

  • 查看結果
    前往網址:https://crud-training.dev.idv/bootstrap_example

    上面的bootstrap_example,是Controller名稱Bootstrap_example字首轉小寫而來 (只有字首會轉小寫)

練習完畢後,將程式碼推送至Git上

五、練習

建立HTML基本頁面

建立標題

建立表單

建立表格

六、參考

5.1 官網

5.2 教程


未完待續: CRUD表單設計 Day-04 Javascript

PHP培訓-CRUD

一、開發環境

建議開發環境

  • LNMP, LAMP, XAMPP
  • 範圍:MySQL資料表建立、HTML、Javascript、PHP、物件導向

二、基本要求

  1. 請建構一個 帳號新增 的基本CRUD表單,欄位有:
    • 帳號 varchar
    • 姓名 varchar
    • 性別 varchar
    • 生日 date
    • 信箱 varchar
    • 備註 text

      資料表名稱 account_info
      需自行建立資料表

  2. 需要用到PHP物件、繼承、介面等物件導向技巧
    • 自行定義物件並展示繼承、介面,本題不算第三方程式物件的使用
  3. 清單頁 - 顯示已存在資料
  4. 新增 / 編輯 / 刪除(需二次確認) - 資料編輯用
  5. 需用到 jQuery, AJAX
  6. 帳號欄位是唯一值,不可重複
  7. 使用彈窗(Dialog)方式開啟 新增/編輯表單頁/確認視窗
    • 可使用套件 Bootstrap Modal, jQuery UI Dialog
  8. 支援 搜尋、排序 功能
  9. 每頁顯示數量、分頁(Pagination)
  10. 資料驗証
    • 必填:帳號、姓名、性別、生日、信箱
    • 帳號格式:英數混合(不分大小寫),位數為5~15碼
    • 日期格式:生日
    • 信箱格式:信箱

      前後端皆要驗証

  • 基本要求共10題,測試基礎能力
  • 可使用套件
  • 實作 搜尋、排序、每頁顯示數量、分頁 時不可使用套件 (除jQuery,Bootstrap)
  • 不可使用到 四、套件練習 中的套件

三、進階要求

以下需求 依個人能力 選擇項目完成

  1. 使用PHP Framework
    • 如:CodeIgniter3, Yii2, Laravel
  2. 使用RWD Framework
    • 如:Bootstrap
  3. 編碼風格:PSR-1 & PSR-2 & PHPDoc註釋 & PSR-4 & 使用Namespace
    • 自行開發spl_autoload_register()或用composer autoload輔助
  4. AJAX使用 RESTful style傳送資料,並且回傳支援 HTTP Status Code
  5. 資料處理
    • 帳號:字母全部轉小寫才寫入資料庫
    • 性別:顯示時為 男/女 ,資料庫儲存為 1/0
    • 生日:顯示時格式 2019年2月15日 ,資料庫儲存格式為 2019-02-15
  6. Javascript使用物件寫法
  7. 批次刪除功能
  8. 匯出資料(text or excel)
  9. 匯入資料(批次新增 or 批次修改)
  10. 匯入處理要求
    • 匯入時做差異處理,帳號存在者為更新,帳號不存在者為新增
    • 匯入時,也需驗証,當有一筆錯誤時,該次匯入不處理,且需返回錯誤訊息

四、套件練習

  1. 使用 DataTables 取代自行實作的 搜尋、排序、每頁顯示數量、分頁 功能

五、參考

官網

教程

套件

CRUD表單設計 Day-02 安裝PHP Framework

一、說明

一個優秀的PHP框架,能幫助我們的程式編寫更合理,更便於維護,框架題供的工具函式,也可以大量簡化我們的開發步驟,而在團隊開發中,框架更提供一個共同的規範讓團隊依循。

在此使用框架 CodeIngiter 3

二、建立Git repository

  • 前往GitHub註冊帳號

  • 建立repository: crud-training

    • 點選右上角頭像旁的+號,再選「New repository」

    • 填寫資料

  • 取得 crud-training 的 Git Repo RUL

    • 點選Clone or download
    • 使用SSH URL並複製
  • Clone crud-training

    1
    2
    3
    4
    5
    6
    7
    # 刪除原本的crud-training資料夾
    $ cd /var/www/html
    $ sudo rm -rf crud-training
    # Clone git repo 至 crud-training資料夾
    $ git clone {Git Repo RUL} crud-training
    # 回到crud-training
    $ cd crud-training

    將**{Git Repo RUL}**替換成剛剛複製的Git Repo RUL

    註:如果Linux與Github間的金鑰認証沒有做好,會clone失敗,請參考帳號安全設定-GitHub

三、安裝 CodeIngiter 3

3.1 下載CodeIngiter 3

前往 CodeIngiter 3 官網,下載檔案

3.2 解壓縮並上傳至遠端開發目錄

  • 下載並安裝WinSCP

  • 連線至遠端目錄

  • 上傳檔案

    沒用的檔案可以不傳

  • 查看上傳結果-從VSCode

  • 查看上傳結果-從瀏覽器

    前往網址:https://crud-training.dev.idv/

3.3 推送至Git

  • 查看目前git log

    1
    $ git log
  • 查看變動的檔案

    1
    $ git status
  • 暫存”所有”變動檔案

    1
    $ git add .
  • 再查看變動的檔案

    1
    $ git status
  • Commit 暫存檔案

    1
    2
    3
    $ git commit
    # 在第一行輸入: [Upload] Upload CodeIgniter 3
    # 儲存離開
    • 用#開頭的行不會被git處理,是給使用者看的
  • 推送至Git

    1
    $ git push
  • 再查看目前git log

    1
    $ git log
  • 去Github查看上傳結果

四、設定 CodeIngiter 3

4.1 建立Composer資料&目錄

  • 在application目錄下建立檔案composer.json (按右鍵=>新增檔案)

  • 填入composer.json檔案內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "name": "dev/codeigniter",
    "description": "Composer package list and local code autoload support.",
    "type": "project",
    "license": "MIT",
    "autoload": {
    "psr-4": {
    "app\\": "./"
    }
    },
    "require": {}
    }
  • 安裝composer相關資料

    1
    2
    $ cd application
    $ composer install

    安裝完成後,application下會出現vendor目錄

4.2 修改composer設定值

  • 開啟檔案 application/config/config.php
  • 修改 $config[‘composer_autoload’] = TRUE; (FALSE => TRUE)

4.3 編輯.gitignore(忽略git處理清單)

1
2
3
4
5
application/cache/*
application/logs/*
composer.lock
/vendor/
application/vendor

4.4 上傳Git

1
2
3
4
5
6
7
8
# 查看變動
$ git status
# 暫存所有變動
$ git add .
# commit 變動檔案
$ git commit
# 推送
$ git push

五、目錄結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|-- application 項目目錄
|-- core 項目的核心程序
|-- helpers 項目的輔助函數
|-- libraries 通用類庫
|-- language 語言包
|-- config 項目相關的配置
|-- config.php 項目相關的配置文件
|-- database.php 數據庫相關的配置文件
|-- autoload.php 設置自動加載類庫的配置文件
|-- constants.php 常量配置文件
|-- routes.php 路由配置文件
|-- controllers 控制器目錄
|-- welcome.php 控制器文件,繼承CI_Controller
|-- models 模型目錄
|-- welcome_model.php 模型文件,繼承CI_Model
|-- views 視圖目錄
|-- welcome.php 視圖模板文件,默認後綴名為.php
|-- cache 存放數據或模板的緩存文件
|-- errors 錯誤提示模板
|-- hooks 鉤子,在不修改系統核心文件的基礎上擴展系統功能
|-- third_party 第三方庫
|-- logs 日誌

|-- system 框架程序目錄
|-- core 框架的核心程序
|-- CodeIgniter.php 引導性文件
|-- Common.php 加載基類庫的公共函數
|-- Controller.php 基控制器類文件:CI_Controller
|-- Model.php 基模型類文件:CI_Model
|-- Config.php 配置類文件:CI_Config
|-- Input.php 輸入類文件:CI_Input
|-- Output.php 輸出類文件:CI_Output
|-- URL.php URL類文件:CI_URl
|-- Router.php 路由類文件:CI_Router
|-- Loader.php 加載類文件:CI_Loader
|-- helpers 輔助函數
|-- url_helper.php url相關的輔助函數,如:創建url的輔助函數
|-- captcha_helper.php創建圖形驗證碼的輔助函數
|-- libraries 通用類庫
|-- Pagination.php 通用分頁類庫
|-- Upload.php 通用文件上傳類庫
|-- Image_lib.php 通用圖像處理類庫
|-- Session.php 通用session類庫
|-- language 語言包
|-- database 數據庫操作相關的程序
|-- DB_active_rec.php 快捷操作類文件(ActiveRecord)
|-- fonts 字庫

|-- index.php 入口文件

system目錄下的資料不建議變動

六、第一支程式 Hello World!

6.1 建立Controller

  • 建立檔案
    application/controllers/Hello.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    defined('BASEPATH') OR exit('No direct script access allowed');

    class Hello extends CI_Controller {

    public function index()
    {
    $this->load->view('hello');
    }
    }
    • Controller檔名第一個字母大寫,class名子和檔名一樣
    • Load view時,只需填寫view的檔名

6.2 建立View

  • 建立檔案
    application/views/hello.php

  • 寫入內容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');
    ?>
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="utf-8">
    <title>Hello World!</title>
    </head>

    <body>

    <div>
    <h1>Hello World!</h1>
    <p>This is some text.</p>
    </div>

    </body>

    </html>

    View檔名小寫即可

6.3 查看結果

6.4 推送至Git

1
2
3
4
5
6
7
8
# 查看變動
$ git status
# 暫存所有變動
$ git add .
# commit 變動檔案
$ git commit
# 推送
$ git push

七、資料庫存取

本教學中選用MySQL資料庫

7.1 資料設計

使用者在網頁前端輸入的資料,傳送到後端處理完後,必需存到資料庫中,以待後續取用。但資料要如何存取才符合需求並有好的效率,資料庫設計至關重要。

7.1.1 建構原則

  • 編碼使用 utf8_unicode_ci

    如果資料欄位可能為中英夾雜,使用utf8_unicode_ci
    如果可以確定欄位為純ASCII可用ascii_bin (進階)

  • 使用小寫英文命名,單字用底線分隔

    不使用大寫及其他符號

  • 資料表名稱不宜太長,以 4 個字以下為原則

    字元過多可用縮寫代稱

  • 欄位名稱不宜太長,以4個字以下為原則

    字元過多可用縮寫代稱

  • 資料表及欄位需有備註(COMMENT)
  • 每個資料表需有主鍵、索引鍵

    建議擁有auto_increment型態的主鍵,以便使用where in精確查詢

  • 資料表建議有資料識別欄位

    建立日、建立者、更新日、更新者、軟刪日、軟刪者、資料狀態

7.1.2 常用格式

數字型態

格式名稱 位元數 備註
TINYINT[(M)] 1 byte Signed: -128 to 127 (-2^7 to 2^7-1)
Unsigned: 0 to 255 (0 to 2^8-1)
INT[(M)] 4 byte Signed: -2^31 to 2^31-1
Unsigned: 0 to 2^32-1
BIGINT[(M)] 8 byte Signed: -2^63 to 2^63-1
Unsigned: 0 to 2^64-1
DECIMAL[(M[,D])] M+2 byte M是數字的最大數(精度)
D是小數點右側數字的數目(標度)
decimal(5,2) 為 xxx.xx

上表中的 M 代表「最大顯示寬度」

字串型態

格式名稱 位元數 備註
VARCHAR(M) L+1 bytes 最大長度 M bytes
TEXT L+2 bytes 最大長度 2^16-1 bytes
MEDIUMTEXT L+3 bytes 最大長度 2^24-1 bytes
LONGTEXT L+4 bytes 最大長度 2^32-1 bytes

上表中的 L 代表「實際儲存的空間大小」

時間型態

格式名稱 位元數 備註
DATE 3 bytes ‘0000-01-01’ to ‘9999-12-31’
TIME 3 bytes ‘-838:59:59’ to ‘838:59:59’
DATETIME 8 bytes 1000-01-01 00:00:00 to
9999-12-31 23:59:59
TIMESTAMP 4 bytes 自 1970 年起,至 2037 年的某時

列舉型態

格式名稱 位元數 備註
ENUM 1~2 bytes 65535 個成員,單選
SET 1~8 bytes 64 個成員,可以多選

7.1.3 需求假設

有一部門管理介面,需要可填寫 部門代碼、部門名稱、部門層級、開始時間、結束時間、備註 ,其中部門代碼維一

欄位整理:

主鍵欄位 資料欄位 管理欄位
系統序號 代碼 建立日
名稱 建立者
層級 更新日
開始日 更新者
結束日 軟刪日
備註 軟刪者
資料狀態

管理欄位視狀況選用

7.1.4 需求設計

  • 資料表 : dept_info 單位資料
  • 欄位
欄位名稱 型態 長度 備註 說明 A_I 空值
d_id int 10 系統序號 SQL主鍵 V
d_code varchar 15 代碼
d_name varchar 60 名稱
d_level varchar 15 層級
date_start date 開始日 預設 0000-00-00
date_end date 結束日 預設 9999-12-31
remark text 備註
date_create datetime 建立日
user_create int 10 建立者
date_update datetime 更新日
user_update int 10 更新者
date_delete datetime 軟刪日
user_delete int 10 軟刪者
rec_status tinyint 1 資料狀態 無效(0) 有效(1)
  • 索引
    • 主鍵 : d_id
    • 索引 : d_code
  • 建構語法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CREATE TABLE `dept_info` (
    `d_id` int(10) UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '系統序號',
    `d_code` varchar(15) NOT NULL COMMENT '代碼',
    `d_name` varchar(60) NOT NULL COMMENT '名稱',
    `d_level` varchar(15) NOT NULL COMMENT '層級',
    `date_start` date NOT NULL COMMENT '開始日',
    `date_end` date NOT NULL DEFAULT '9999-12-31' COMMENT '結束日',
    `remark` text NOT NULL COMMENT '備註',
    `date_create` datetime NOT NULL COMMENT '建立日',
    `user_create` int(10) NOT NULL COMMENT '建立者',
    `date_update` datetime NOT NULL COMMENT '更新日',
    `user_update` int(10) NOT NULL COMMENT '更新者',
    `date_delete` datetime NOT NULL COMMENT '軟刪日',
    `user_delete` int(10) NOT NULL COMMENT '軟刪者',
    `rec_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '資料狀態 無效(0) 有效(1)',
    PRIMARY KEY (`d_id`),
    KEY `d_code` (`d_code`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='單位資料';
  • 在本表中除主鍵d_id外,另增加一組索引d_code
  • 索引建立原則為
    • 主鍵/外鍵必須有索引
    • 查詢常用條件
    • 識別性夠高
    • 資料表間聯合查詢常用
    • 長度小的欄位,如TEXT就不適合
    • 頻繁進行資料操作的表,不要建立太多的索引

7.2 資料庫建構

7.2.1 phpmyadmin管理

登入管理介面

7.2.2 建立資料庫

  • 建構語法
    1
    CREATE DATABASE training CHARACTER SET utf8 COLLATE utf8_unicode_ci;

7.2.3 建立資料表

方法一

  • 點選 資料庫 training
  • 點選「SQL」指令輸入
  • 輸入資料表建構指令
  • 點選「執行」

方法二

  • 點選 資料庫 training
  • 點選「結構」
  • 輸入資料表名稱及欄位數
  • 點選「執行」
  • 填入欄位設定

7.2.4 建立存取帳號

  • 點選 伺服器: localhost
  • 點選「使用者帳號」、「新增使用者帳號」
  • 輸入帳號密碼
  • 選擇權限 僅資料
  • 拉至最下方,點選「執行」

7.3 設定資料庫連線

  • 打開設定檔 application/config/database.php
  • 設定連線參數
    • 資料庫網址:hostname
    • 連線帳號:username
    • 連線密碼:password
    • 使用的資料庫:database

7.4 Model建構範例

  • 建立model檔案 application/models/Dept_info_model.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    <?php

    /**
    * 部門資料管理Model
    *
    * 提供通用函式 新增、讀取、更新、刪除、批次讀取、批次新增、批次更新、批次刪除 示範
    *
    * @author Mars.Hung 2020-02-29
    */
    class Dept_info_model extends CI_Model
    {

    /**
    * 資料表名稱
    */
    protected $table = "dept_info";

    /**
    * 欄位資料
    */
    protected $tableColumns = [
    'd_id',
    'd_code',
    'd_name',
    'd_level',
    'date_start',
    'date_end',
    'remark',
    'date_create',
    'user_create',
    'date_update',
    'user_update',
    'date_delete',
    'user_delete',
    'rec_status',
    ];

    public function __construct()
    {
    parent::__construct();

    // 載入資料連線
    $this->load->database();
    }

    /**
    * 取得資料 - 從主鍵
    *
    * 本函式只能取出 rec_status==1 的資料
    *
    * @param int $d_id 目標主鍵資料
    * @param string $col 輸出欄位
    * @return array
    */
    public function get($d_id, $col = '*')
    {
    return $this->db->select($col)->from($this->table)->where('d_id', $d_id)->where('rec_status', '1')->get()->result_array();
    }

    /**
    * 取得資料 - 從查詢條件
    *
    * 本函式只能取出 rec_status==1 的資料
    *
    * 格式:
    * $conditions = [
    * '欄位名' => '欄位值string/int/array',
    * ];
    *
    * @param array $conditions 查詢條件
    * @param string $col 輸出欄位
    * @return array
    */
    public function getBy($conditions = [], $col = '*')
    {
    // 查詢建構
    $query = $this->db->select($col)->from($this->table)->where('rec_status', '1');

    // 加入查詢條件
    foreach ($conditions as $key => $where) {
    if (is_array($where)) {
    // 加入陣列查詢條件
    $query->where_in($key, $where);
    } else {
    // 加入單一查詢條件
    $query->where($key, $where);
    }
    }

    // 執行查詢、取回資料並回傳
    return $query->get()->result_array();
    }

    /**
    * 新增資料
    *
    * @param array $data 部門資料
    * @return int
    */
    public function post($data)
    {
    // 過濾可用欄位資料
    $data = array_intersect_key($data, array_flip($this->tableColumns));

    // 移除主鍵欄位 - 新增時不帶入主鍵值,以便主鍵由sql自行增加
    unset($data['d_id']);

    // 寫入 date_create, user_create(未知,暫用0), rec_status
    $data['date_create'] = date('Y-m-d H:i:s');
    $data['user_create'] = 0;
    $data['rec_status'] = '1';

    // 移除 date_update, user_update, date_delete, user_delete
    unset($data['date_update']);
    unset($data['user_update']);
    unset($data['date_delete']);
    unset($data['user_delete']);

    // 寫入資料表
    $res = $this->db->insert($this->table, $data);

    // 寫入成功時回傳寫入主鍵鍵值,失敗時回傳 0
    return $res ? $this->db->insert_id() : 0;
    }

    /**
    * 更新資料 - 從主鍵
    *
    * @param array $data 部門資料
    * @return int
    */
    public function put($data)
    {
    // 過濾可用欄位資料
    $data = array_intersect_key($data, array_flip($this->tableColumns));

    $res = 0;

    // 檢查有無主鍵
    if (isset($data['d_id'])) {
    // 取出主鍵值並移除$data中主鍵欄位
    $d_id = $data['d_id'];
    unset($data['d_id']);

    // 寫入 date_update, user_update(未知,暫用0)
    $data['date_update'] = date('Y-m-d H:i:s');
    $data['user_update'] = 0;

    // 移除 date_create, user_create, date_delete, user_delete, rec_status
    unset($data['date_create']);
    unset($data['user_create']);
    unset($data['date_delete']);
    unset($data['user_delete']);
    unset($data['rec_status']);

    // 更新資料 - 成功時回傳主鍵鍵值,失敗時回傳 0
    $res = $this->db->where('d_id', $d_id)->update($this->table, $data) ? $d_id : 0;
    } else {
    // 報錯-沒有主鍵欄位
    throw new Exception('沒有主鍵欄位: d_id', 400);
    }

    return $res;
    }

    /**
    * 刪除資料 - 從主鍵
    *
    * @param array|int $d_id 欲刪除的主鍵值
    * @param bool $forceDelete 是否強制刪除 false時為軟刪除
    * @return bool
    */
    public function delete($d_id, $forceDelete = false)
    {
    $d_id = (array) $d_id;

    // 刪除條件
    $this->db->where_in('d_id', $d_id);

    if ($forceDelete) {
    // 直接刪除 - CI SQL Builder有限定需有where才可以執行delete
    return $this->db->delete($this->table);
    } else {
    // 標記成刪除狀態 - 本練習中無法得知操作者id,暫不處理user_delete值
    $data['date_delete'] = date('Y-m-d H:i:s');
    $data['user_delete'] = 0;
    $data['rec_status'] = 0;

    return $this->db->update($this->table, $data);
    }
    }


    /**
    * 批次寫入資料 - 未完成,請補完
    *
    * 整批處理時,有一筆錯誤,整批都不可以處理
    *
    * @param [type] $datas
    * @return void
    */
    public function postBatch($datas)
    {
    foreach ($datas as $key => $data) {
    // 過濾可用欄位資料

    // 移除主鍵欄位 - 新增時不帶入主鍵值,以便主鍵由sql自行增加

    // 寫入 date_create, user_create(未知,暫用0), rec_status

    // 移除 date_update, user_update, date_delete, user_delete

    }

    // 批次寫入資料表 - 成功時回傳插入列數,失敗時回傳 FALSE
    return $this->db->insert_batch($this->table, $datas);
    }

    /**
    * 批次更新資料 - 未完成,請補完
    *
    * 整批處理時,有一筆錯誤,整批都不可以處理
    *
    * @param [type] $datas
    * @return void
    */
    public function putBatch($datas)
    {
    foreach ($datas as $key => $data) {
    // 過濾可用欄位資料

    // 檢查有無主鍵

    // 寫入 date_update, user_update(未知,暫用0)

    // 移除 date_create, user_create, date_delete, user_delete, rec_status

    }

    // 批次更新資料 - 成功時回傳更新列數,失敗時回傳 FALSE
    $this->db->update_batch($this->table, $datas, 'd_id');
    }
    }

7.5 Model使用範例

  • 建立Controller檔案 application/controllers/Dept_info.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    <?php
    defined('BASEPATH') or exit('No direct script access allowed');

    /**
    * 部門資料庫存取範例
    *
    * 本Controller提供Model Dept_info_model 使用範例,請在觀察輸出時,也同步觀察資料庫中的資料
    *
    * @author Mars.Hung 2020-02-29
    */
    class Dept_info extends CI_Controller
    {

    public function __construct()
    {
    parent::__construct();

    // 開啟session功能
    session_start();
    }

    public function index()
    {
    // 載入部門資料庫
    $this->load->model('Dept_info_model');

    echo "<pre>";

    /**
    * ========== 範例-新增 ==========
    */
    // 新增一筆資料 - 使用session記錄計數
    if (!isset($_SESSION['count'])) {
    $_SESSION['count'] = 0;
    }
    $count = ++$_SESSION['count'];

    // 建構新增範例資料
    $data = [
    'd_code' => 'd' . str_pad($count, 4, '0', STR_PAD_LEFT),
    'd_name' => '部門1',
    'd_level' => '部',
    'date_start' => '2020-01-01',
    'remark' => '部門' . $count,
    ];

    // 使用Dept_info_model中的post函式新增資料
    $d_id = $this->Dept_info_model->post($data);

    echo "新增:" . $d_id;
    echo "\n";
    echo "\n";



    /**
    * ========== 範例-讀取 ==========
    */
    // 讀取剛才新增的資料
    $data = $this->Dept_info_model->get($d_id);
    echo "讀取:";
    var_export($data);
    echo "\n";



    /**
    * ========== 範例-修改 =========
    */
    // 建構修改範例資料 - 修改剛才新增的資料
    $data = [
    'd_id' => $d_id,
    'd_name' => '部門' . mt_rand(0000, 9999),
    'd_level' => '組',
    ];

    // 使用Dept_info_model中的put函式修改資料
    $d_id = $this->Dept_info_model->put($data);
    // 讀回修改的資料
    $data = $this->Dept_info_model->get($d_id, 'd_id,d_name,d_level');
    echo "修改後:";
    var_export($data);
    echo "\n";



    /**
    * ========== 範例-刪除 ==========
    */
    // 使用Dept_info_model中的put函式修改資料 - 軟刪
    $this->Dept_info_model->delete($d_id);
    // 使用Dept_info_model中的put函式修改資料 - 強刪
    // $this->Dept_info_model->delete($d_id, true);

    // 讀回刪除的資料
    $data = $this->Dept_info_model->get($d_id);
    echo "刪除後:";
    var_export($data);
    echo "\n";
    echo "請至資料庫中查看d_id=" . $d_id . "資料狀態";
    echo "\n";
    echo "\n";


    /**
    * ========== 範例-取得資料-從查詢條件 ==========
    * 請先新增好一些資料再來查詢
    */
    $where = [
    'd_id' => ['31', '32'],
    'd_level' => '部',
    ];
    $data = $this->Dept_info_model->getBy($where);
    var_export($data);
    }
    }

7.6 觀察資料庫存取狀況

7.7 推送至Git

1
2
3
4
5
6
7
8
# 查看變動
$ git status
# 暫存所有變動
$ git add .
# commit 變動檔案
$ git commit
# 推送
$ git push

八、參考


九、作業

  • 將「Model建構範例」中Dept_info_model的函式 postBatch($datas), putBatch($datas) 補完

未完待續: CRUD表單設計 Day-03 安裝Bootstrap4

帳號安全設定

一、說明

開發過程中會用到很多第三方服務,使用這些服務時,大多要開註冊帳號,但為避免麻煩都會使用 google/facebook 等大型服務認証登入,軟體開源服務(如Github,Packagist)也可能互相串接,如果沒有做好安全防護,一個帳號被破,可能要死一大票,因此做好安全設定是必要的
安全設定如下:

  • 可靠的密碼
  • 限定連線IP
  • SSH金鑰認證 (Authorized_keys)
  • 兩步驟驗證

其中

  • 密碼:建議最少8碼英數混合,含大小寫、符號更佳。

    但過複雜容易搞死自己

  • 限定ip:第三方服務大多無法限定連線ip。
  • SSH金鑰認證:使用Linux、Git服務時推荐
  • 二步驗証:強烈推荐,兩步驟驗證為透過異質管道取得驗証碼,且驗証碼通常有時限,可靠度高。

二、目標

  • Google
    • 兩步驟驗證
  • GitLab
    • SSH金鑰認證
    • 兩步驟驗證
  • GitHub
    • SSH金鑰認證
    • 兩步驟驗證

三、設定

3.1 Google

3.1.1 兩步驟驗證-啟用

  • 前往Google,登入個人帳號後,點擊右上角LOGO,選「Google帳戶」

  • 選擇「安全性」 => 「兩步驟驗證」

  • 選擇「開始使用」

  • 輸入密碼

  • 使用手機驗証 (必需設定備援手機)

  • 輸入傳至手機的驗証碼

  • 選擇綁定的手機,及驗証方式

  • 輸入驗証碼

  • 綁定手機完成,啟用「兩步驟驗證」

  • 啟用「兩步驟驗證」完成

3.1.2 兩步驟驗證-Authenticator

  • 設定 Authenticator

  • 安裝app: Authenticator

  • 選擇手機系統

  • 打開Authenticator,掃描QR Code

  • 新增完成

  • 輸入驗証碼

  • 設定Authenticator完成

Google兩步驟驗證教學

3.2 GitLab

3.2.1 SSH金鑰認證

  • 產生SSH金錀

    1
    $ ssh-keygen -t ed25519 -C "your_email@example.com"
    • 注意: 如果您使用的是不支持ed25519算法的舊系統,請使用以下指令
      • $ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
    • your_email@example.com請替換為您的電子信箱,也可以不指定電子信箱
    • 產生ssh key時,如果有設定密碼,以後使用此ssh key時都要輸入密碼
    • 非共用電腦上使用的ssh key,密碼通常不設,自行保管好即可
    • 會在家目錄中的.ssh目錄下產生二個檔案
      • 私錀:id_ed25519
      • 公錀:id_ed25519.pub
    • 家目錄位置
      • Windows:
        • C:\Users\你的帳號名\
        • C:\使用者\你的帳號名\
      • Linux:
        • /home/你的帳號名/
  • 複制~/.ssh/id_ed25519.pub內容

  • SSH-Keys設定位置

  • SSH金錀設定檔 ~/.ssh/config

    1
    2
    3
    4
    # 前往家目錄下的ssh設定資料夾
    $ cd ~/.ssh/
    # 編輯ssh config
    $ vi config

    可以為多個主機設置配置使用的金錀

    config內容如下:

    1
    2
    3
    4
    5
    # GitLab.com
    Host gitlab.com
    HostName gitlab.com
    User git
    #IdentityFile ~/.ssh/id_ed25519 # 金鑰如果使用預設路徑&檔名,可以不設定本行

    IdentityFile 指定ssh私鑰路徑,金鑰如果使用預設路徑&檔名,可以不設定本行

  • Git SSH連線金鑰
    • 使用指令ssh-keygen建立SSH認証金鑰對id_ed25519, id_ed25519.pub
    • id_ed25519是私鑰,存放於本機端
    • id_ed25519.pub是公鑰,存放於Git伺服器
    • 設定config指定連線參數與使用金鑰檔
    • 此時使用SSH連線到Git伺服器時,就不需再輸入帳號密碼,所以需要妥善保管好SSH私鑰

3.2.2 兩步驟驗證

  • 登入GitLab,設定兩步驟驗證

GitLab兩步驟驗證教學

3.3 GitHub

3.3.1 SSH金鑰認證

  • 產生SSH金錀

    1
    $ ssh-keygen -t ed25519 -C "your_email@example.com"
    • 注意: 如果您使用的是不支持ed25519算法的舊系統,請使用以下指令
      • $ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
    • your_email@example.com請替換為您的電子信箱,也可以不指定電子信箱
    • 產生ssh key時,如果有設定密碼,以後使用此ssh key時都要輸入密碼
    • 非共用電腦上使用的ssh key,密碼通常不設,自行保管好即可
    • 會在家目錄中的.ssh目錄下產生二個檔案
      • 私錀:id_ed25519
      • 公錀:id_ed25519.pub
    • 家目錄位置
      • Windows:
        • C:\Users\你的帳號名\
        • C:\使用者\你的帳號名\
      • Linux:
        • /home/你的帳號名/
  • 複制~/.ssh/id_ed25519.pub內容

  • SSH-Keys設定位置

  • SSH金錀設定檔 ~/.ssh/config

    1
    2
    3
    4
    # 前往家目錄下的ssh設定資料夾
    $ cd ~/.ssh/
    # 編輯ssh config
    $ vi config

    可以為多個主機設置配置使用的金錀

    config內容如下:

    1
    2
    3
    4
    5
    # Github.com
    Host github.com
    HostName github.com
    User git
    #IdentityFile ~/.ssh/id_ed25519 # 金鑰如果使用預設路徑&檔名,可以不設定本行

    IdentityFile 指定ssh私鑰路徑,金鑰如果使用預設路徑&檔名,可以不設定本行

  • Git SSH連線金鑰
    • 使用指令ssh-keygen建立SSH認証金鑰對id_ed25519, id_ed25519.pub
    • id_ed25519是私鑰,存放於本機端
    • id_ed25519.pub是公鑰,存放於Git伺服器
    • 設定config指定連線參數與使用金鑰檔
    • 此時使用SSH連線到Git伺服器時,就不需再輸入帳號密碼,所以需要妥善保管好SSH私鑰

3.3.2 兩步驟驗證

  • 登入GitHub,設定兩步驟驗證

GitHub兩步驟驗證教學

Composer安裝

一、說明

Composer是PHP套件管理器,將特定功能的函式庫/專案拆分成套件,透過套件管理器安裝使用,好處有:

  • 統一維護程式碼
  • 支援版本控制
  • 自動管理依賴函式庫

使用Composer管理套件,讓您的開發更方便順利。

二、安裝

2.1 Windows

  • 下載 Composer-Setup.exe 並安裝
    下載並執行 Composer-Setup.exe。 它會安裝最新版本的 Composer 並設置你的 PATH 以便你可以在命令列中的任何目錄呼叫 composer。

    注意: 關閉你的終端機。用新的終端機來測試: 這很重要,因為 PATH 只會在終端機啟動時載入。
    Composer依賴PHP,所以必需有安裝PHP

2.2 Linux(Ubuntu16.04TLS)

  • 安裝Composer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 下載安裝程式
    $ sudo php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"

    # 執行安裝
    $ sudo php composer-setup.php

    # 刪除安裝檔
    $ sudo php -r "unlink('composer-setup.php');"

    # 移動Composer命令至全域執行路徑
    $ sudo mv composer.phar /usr/local/bin/composer

    # 查看Composer版本
    $ composer --version
    Composer version 1.8.6 2019-06-11 15:03:05

    Composer依賴PHP,所以必需有安裝PHP

三、常用指令

  • 安裝新套件: composer require <packages>

    加入新套件使用本命令

  • 移除套件: composer remove <packages>
  • 列出可用套件: composer show

    已安裝套件資訊

  • 安裝依賴套件: composer install

    安裝composer.lock文件定義過的套件,如果沒有,則只會解析composer.json文件中列出的所有依賴項,並將其最新版本的文件下載到vendor項目的目錄中。

  • 將依賴套件更新為其最新版本: composer update [<packages>]

    如果你只想要安裝或升級單一依賴套件,可以指定套件名稱

  • 初始化: composer init

    首次使用時,自動建立composer.json檔

  • 更新Composer本身: composer self-update

四、參考

Git安裝

一、說明

版本控制在軟體開發中非常重要,有了版本控制不但可以記錄軟體變動脈絡,還能多線開發、合併、摘取、回遡、差異比對等功能,在團隊合作中尤其重要。

二、安裝

2.1 Windows

  • 下載並安裝 Git

    基本上一直按「下一步」到底就可以了

  • 下載Git GUI管理軟體 TortoiseGit

    下載主程式和語言包 (建議不裝中文,看習慣英文)

    基本上設定個人資訊前一直按「下一步」就可以了

2.2 Linux(Ubuntu16.04TLS)

  • Git安裝

    Ubuntu16.04TLS預設安裝的git版本太舊,需使用其他套件源

    • 安裝Git套件源

      1
      2
      $ sudo add-apt-repository ppa:git-core/ppa;
      $ sudo apt-get update;
    • 安裝/更新Git

      1
      $ sudo apt-get install git;
    • 查看Git版本

      1
      2
      $ git --version
      git version 2.22.0
  • 語系設定

    1
    2
    3
    4
    $ echo "export LESSCHARSET=utf-8;" | sudo tee -a ~/.bashrc
    $ echo "export LESSCHARSET=utf-8;" | sudo tee -a /etc/bash.bashrc
    $ echo "export LC_ALL=en_US.UTF-8;" | sudo tee -a ~/.bashrc
    $ echo "export LC_ALL=en_US.UTF-8;" | sudo tee -a /etc/bash.bashrc

    原因是 git log 預設是使用 less 這個工具來瀏覽,所以需要額外設定編碼。

  • Git參數設定

    1
    2
    3
    4
    5
    # 編輯git時使用vim編輯器
    $ git config --global core.editor vim
    # 設定Git操作者的名子與信箱
    $ git config --global user.name {我的名子}
    $ git config --global user.email {我的信箱}
    • **{我的名子}{我的信箱}**替換成自己的英文名子和電子信箱

三、常用指令

  • 下載repository: git clone <repo> (repo格式:git@xxx.xx:xxx/xxx.git)
  • 查看記錄: git log
  • 查看歷史記錄: git reflog

    如果誤操作時,可用此找查之前的記錄

  • 查看目前變動: git status
  • 收藏變動: git stash
  • 取出收藏: git stash pop
  • 暫存變動: git add <file>
  • 暫存全部變動: git add -u

    不含未處理過的檔案

  • 暫存全部變動: git add .

    含未處理過的檔案

  • 暫存路徑變動: git add <dir>

    含未處理過的檔案

  • 取消暫存變動: git reset HEAD <file>

    取消不小心 git add 的檔案

  • 提交: git commit
  • 推送至遠端: git push

    強制推送: git push -f

  • 獲取遠端記錄: git fetch
  • 獲取並合併: git pull <branch>
  • 合併分支: git merge <commit>
  • 變基: git rebase -i <commit>
  • 切換分支: git checkout <branch>
  • 清除變動檔案: git checkout <file>
  • 清除分支所有變動: git checkout -- .
  • 變更工作目錄至commit: git reset <commit>

    只移動branch位置,不變動檔案

  • 變更工作目錄至commit: git reset --hard <commit>

    移動branch位置,並重置檔案

  • 建立新分支: git branch
  • 建立tag: git tag <tag>
  • 推送tag: git push origin <tag>

    同等: git push origin refs/tags/<tag>

  • 刪除tag: git tag -d <tag>
  • 刪除遠端tag: git push origin refs/tags/<tag>

    必需先把本地tag刪除(推送空tag)

四、參考