Go學習筆記(2): make a game

來到學習Go 的第二篇文章了,來學習一些控制流以及一些基礎東西吧!

條件式

在 Golang 寫條件式很簡單,就是寫個if 加個大括號就好

// 這裡請記得 "{" 和 if 要同一行,之前自由慣了常常被這件事情雷到!
if true {
    ......
} else if false {
    .....
}

// 或者你可以使用 if  更短的初始化陳述句
if count := 5; count>4 {
   fmt.Println("count is ",count)
}

另外有一個比較特別的是,if 區塊裡面的區域變數(使用到:=)是不能使用在if 外面的喔~

請讓我用以下程式碼來做進一步陳述

詳細可以去看 這裏:https://github.com/r567tw/go-practice/blob/master/HelloWorld/if.go

package main

import (
  "fmt"
)

// var x = 999 // go 裡面也可以宣告一個超越main範圍以外的超全域變數, 但不建議這麼做

func main() {
	fmt.Println("Hello World")
	x := 10

	if x > 0 {
		y := 10
		// x := 100 // 這個等同在裡面宣告新的位置x , 所以外面的x仍然是10
		x = 100 // 這個會污染外面宣告的x , 所以外面的x 會等於 100
		fmt.Printf("x = %d\n", x) // x =100
		fmt.Printf("%d in if statement\n", y) //10 in if statement
	}

	// fmt.Sprintf("%d out of if statement", y) // error
	fmt.Printf("x = %d out of if statement\n", x) // x = 100 out of if statement
}

值得再拿出來說嘴的是,x變數在外面的宣告,如果在if 陳述句範圍裡面使用單純的= ,在main 範圍內的x 是會被更動的,可是如果使用:= , main 裡面的x 不會被影響。

if number,err := strconv.ParseFloat("3.14",64); err != nil{
    log.Fatal(err)
}

fmt.Println(number) // <= 這裡會出現錯誤,因為number 屬於if 區塊裡面的範圍,go 語言的if 裡面變數不得共享

Switch 陳述句

switch rand.Intn(3)+1 {
    case 1 : ...
    case 2 : ...
    case 3 : ...
    default : ...
}

迴圈

話不多說, show you the code

for x:= 0; x<=6; x++ {
    ......
}

// 或者只是條件式得處理
x:=0
for x<=6 {
    x++
}

// 迴圈裡面也可以使用 continue 和 break ...

和 if 一樣,使用到:= 所宣告的變數是沒辦法使用在for 外面的。

function

package main

import (
	"fmt"
)

type bigger = func(int) bool // function 也可以作為型態的一種!

func main() {
	handleFn()

	anonymous := func() {
		fmt.Println("anonymous function")
	} // go 也支援匿名funciton
	anonymous()

	origin := []int{1, 2, 3, 4, 5}
	change := filter(origin, func(el int) bool {
		return el > 3
	})
	fmt.Println(change)
}

func handleFn() {
	fmt.Println("test function")
}

func filter(data []int, big bigger) []int {
	filtered := []int{}
	for _, element := range data {
		if big(element) {
			filtered = append(filtered, element)
		}
	}
	return filtered
}

Make A Game !

接下來,讓我們試著應用以上這些東西,來寫個簡單的猜數字遊戲吧!

package main

import (
	"bufio"
	"fmt"
	"os"
	"math/rand"
	"strconv"
	"strings"
	"time"
)

func main() {
	r := rand.New(rand.NewSource(time.Now().UnixNano())) // 這一行帶入現在的時間,好讓每一次遊戲隨機產生的數字都不一樣, 原來亂數的原理其實是有一個小技巧和規則的
	result := r.Intn(100) // 其實這裡可以隨機產生數字
	
	loop := true // 設定遊戲開始的條件
	for (loop) {
		fmt.Printf("Please Enter a number(1-100): ")
		reader := bufio.NewReader(os.Stdin) // 其實這裏就是類似python 的input 而已
		input, _ := reader.ReadString('\n')
		number,_ := strconv.Atoi(strings.TrimSpace(input)) // 一定要用trimspace, 否則 strconv轉出來的數字不一定取得出來
		switch {
			case (result < number):
				fmt.Printf("smaller than %d\n",number)
			case (result > number):
				fmt.Printf("bigger than %d\n",number)
			case (result == number):
				fmt.Println("Bingo")
				fmt.Printf("result is %d\n",result)
				loop = false
				break
			default:
				loop = true
		}
	}

	fmt.Println("Game is over!")
}

有興趣看程式碼的可以來這裡:https://github.com/r567tw/go-practice/blob/master/makeGame/main.go

小君曰:猜數字遊戲好像可以作為每個程式語言的入門磚,相對於前端的Todo list 呵呵

Go學習筆記(1): HelloWorld

在之前寫到今年的計劃當中,我就說到我想要學Go 語言。同時我自己也買了一本有關於Go 的書:深入淺出Go , 希望藉此督促自己有個比較完整性的學習……

以下是我讀這本書以及學習的心得與筆記,可能有點無聊,高手請跳過、不過如果有看到錯的也請不吝指正!

首先,你要先去安裝好Go…….

程式組成

Go 語言的組成通常有三個部分:
1. 套件子句(package main)
2. import 相關陳述句 (import "fmt")
3. 主要的程式碼 (func main(){......})

Hello World 程式!

讓我們先建立一個名為hello.go 的檔案… 然後在裡面寫這些東西…

package main
import "fmt"

func main(){
	fmt.Println("Hello World")
	// 這裡請務必使用 " 否則很容易跳出 invalid character literal (more than one character) 的問題
}

接下來讓我們對這個檔案做go run hello.go 就可以看到 Hello World 的字眼啦!

Go 的資料型態類別

  1. 字串:用雙引號所框起來的任意數量字元
  2. 符文(runes) : 用單引號所匡著的單一字元, ex. ‘A’ , ‘B’
  3. boolean (bool)
  4. numbers
    1. float32 , float64
    2. int8 ,int16 ,int32 ,int64
    3. uint
    4. uint8 , uint16 ,uint32 ,uint64
  5. byte (檔案專用)

tips: 可以透過 reflect 這個套件裡面的TypeOf 方法得知資料的型別

package main

import (
  "fmt"
  "reflect"
)

func main() {
	fmt.Println(reflect.TypeOf("Hello World")) //string
	fmt.Println(reflect.TypeOf(true)) // bool
}

宣告變數

  • var q int
  • var q int = 4
  • p,q = 4,5 (居然有像python 一樣的多重賦值!)
  • p :=4 (快速寫法, 連型別都不用!)

命名規則

  1. 開頭必須是字母
  2. 如果開頭字母是大寫,表示他是可以被匯出的

陣列

在Go 裡面要宣告陣列,請用以下的code 得形式

// 第一種
var todos [2]string
todos[0]= "learning go !"
todos[1]= "use go to write an app"
// 第二種
var grades [3]int = [3]int{90,98,93}

// 第三種
heights := [3]int{90,98,93}

// go 裡面的foreach , 陣列/map資料型態都適用!
for index,note := range notes{
    fmt.Println(index,note)
}

切片

這是我在深入淺出Go 這本書裡面的某一個章節,他裡面寫道Go 宣告切片就像是宣告陣列變數, 只是不需要指定大小!for example:

var mySlice []int
mySlice = make([]int,7) // 設定七個數字的切片
fmt.Printf("%v",mySlice) //[0 0 0 0 0 0 0]
// 增加
newSlice := append(mySlice, 5,9) // 回傳新的, 增加完的切片
fmt.Printf("%v",newSlice) //[0 0 0 0 0 0 0 5 9]

錯誤處理

說真的,最近在學著Go 都覺得他的語言調性和其他語言差很多,像是if/for 的區域範圍變數無法用在if/for 後面(但之前的宣告可以使用)、陣列的宣告是很獨樹一幟的他也不像傳統程式語言那種try…catch 的敘述,而是你要分成你自己去處理或者直接error 中斷給你看這樣

像是這樣, 你必須用參數去接下可能會error 的地方,然後用if 去判斷, 控制壞掉之後的流程這樣。詳細你可以參考此連結:https://michaelchen.tech/golang-programming/error-handling/

或者你也可以使用 panic 這個關鍵字 或 用 recover 這個關鍵字讓他從panic 的狀態中恢復, 另外也筆記一下 defer 可以添加到任何地方,用來暫緩該調用直到目前程式結束

詳細可以參考此網址:https://openhome.cc/Gossip/Go/DeferPanicRecover.html

 file, err := os.Open("file.txt")         
 if err != nil {                         
     ....         
 }  
defer file.Close()                                    

Maps

有點像是python 裡的 dictionary , 或者 php 的 association array……

var myMap map[string]float64 // 宣告出以字串型態為index, float64型態的值
myMap = make(map[string]float64)

// 又或者可以做更簡單的宣告
myMap := make(map[string]float64)


// 如何把東西放進去...
myMap["Jimmy"] = 12.4
myMap["Bob"] = 15.3

// 如果已經知道要建立怎麼樣的map
myMap := map[string]float64{"Jimmy":12.4 , "Bob":15.3}

// 另外如果取得一個沒有被指派的index 會根據型態而回傳不同的值
// 數字:0
// 字串:""
// * 如果沒有make 它則會是一個nil 的 map, 而nil 的 map 無法被指派值
// 可以視情況給予第二個參數,好讓map 可以判斷是否有這個index
var value string
var exist bool
value , exist = myMap["Andy"] // return 0, false

// 移除
myMap["Jim"] = 999
delete(myMap,"Jim")

結構 struct

var myStruct struct {
    name string
    grade int
}
myStruct.name = "Jimmy"
myStruct.grade = 10

// 自訂型別, 前面使用type 這個關鍵字
type myStruct struct {
    name string
    grade int
}

// 自訂型別也能加入method
func (m myStruct) hello() string {
    return "hello"
}
// 自訂型別也可以加入getter /setter 封裝裡面的資料結構
func (m *myStruct) SetName(name string){
   m.name = name
}

func (m *myStruct) Name() string {
   return m.name
}


// 透過指標存取結構
func applyDiscount(s *subscriber){
    s.rate = 4.99
}

func main() {
    var s subscriber
    applyDiscount(&s)
}

結構裡面可以有另外一個結構,而Go另外還支援了匿名結構欄位,不用特別設定名稱也可以直接帶進去結構裡

type Employee struct {
    Name string
    Salary float64
    Address
}

type Address struct {
     ......
}

介面 interface

在 Go 裡面也有介面的概念,定義某些特定得值與某些特定的行為。

type myInterface interface {
   methodOne()
   methodTwo(float64)
   method() string
}

測試

在Go 裡面,我們可以使用testing 這個套件,首先是要讓我們在同樣的套件底下建立一個_test 結尾的Go 檔案,話不多說,show you some code !

func TestFunction(t *testing.T){
    ......(略)
    if ...(略)
    t.Errorf("......")
}

function programming

在Go 裡面, function 本身也能夠被視為變數, 型別處理,像是

// 此範例來自深入淺出Go p.439
func callFunction(passedFunction func()){
  passedFunction()
}

func callTwice(passedFunction func()){
  passedFunction()
  passedFunction()
}

func callWithArguments(passedFunction func(string, bool)){
  passedFunction("this sentence is",false)
}

func printReturnValue(passedFunction func() string){
  fmt.Println(passedfunction())
}

func functionA(){
  fmt.Println("function called")
}

func functionB() string{
    fmt.Println("function called")
    return "Returning from function"
}

func functionC(a string , b bool){
    fmt.Println("function called")
    fmt.Println(a,b)
}

func sayHi(){
  fmt.Println("Hi")
}

func main(){
    var myFunction = func()
    myFunction = sayHi
    myFunction()

    callFunction(functionA)
    callTwice(functionA)
    callWithArguments(functionC)
    printReturnValue(functionB)
}

相關指令

  • go build :編譯成二進位
  • go run :編譯及執行
  • go fmt :格式化原始碼
  • go version :go 的版本
小君曰:來學Go 啦, go !

Redis筆記

最近在玩Redis, 記錄起來才不會忘!

利用Docker 迅速起一個redis container

你可以去官網按照他的說明下載redis, 但我這裡選擇用docker 來起一個簡單的redis 服務首先我的dokcer-compose.yml 是這樣寫:

# ...(略)
### Redis Container #######################################

    redis:
        image: redis:alpine
        ports:
            - "6379:6379"
# ...(略)

然後使用 docker-compose up -d 就可以起一個簡單的redis server, 你可以使用Another Redis Desktop Manager 去連線它看看狀況

指令與相關說明

簡單來說,redis 就是一個key-value 的 in-memory 資料庫。
首先,我們需要進去一下redis cli…只要`docker exec -it {你docker-composer.yml 所在的資料夾名稱}_redis_1 sh`就可以進入redis container 裡面,之後在container 裡面執行`redis-cli`即可

GET/SET

最簡單你一定要學到的redis 指令就是:SETGET!

127.0.0.1:6379> SET name Jimmy
OK
127.0.0.1:6379> GET name
"Jimmy"

MGET/MSET

每次只能設定一個那怎麼行?你可以一次設定多個

127.0.0.1:6379> MSET first "Hello" second "World"
OK
127.0.0.1:6379> MGET second first
1) "World"
2) "Hello"

EXPIRE

既然是一個key-value 的 in-memory 資料庫,他就不像一般Relational database 一樣,只能永久保存資料,你可以設定這個key 的保留期間

127.0.0.1:6379> SET session "HelloWorld"
OK
127.0.0.1:6379> EXPIRE session 10
(integer) 1
127.0.0.1:6379> GET session
"HelloWorld"
127.0.0.1:6379> GET session
(nil)

EXIST / DEL

你也可以判斷這個key 存在不存在, 以及刪除那個key

127.0.0.1:6379> EXISTS name
(integer) 1
127.0.0.1:6379> DEL name
(integer) 1
127.0.0.1:6379> EXISTS name
(integer) 0

INCR / DECR

redis 還自帶遞增和遞減的指令。

127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> INCR counter
(integer) 101
127.0.0.1:6379> GET counter
"101"
127.0.0.1:6379> DECR counter
(integer) 100
127.0.0.1:6379> GET counter
"100"

INCRBY / DECRBY

每次只能遞增和遞減一個那怎麼可行?

127.0.0.1:6379> SET counter 100
OK
127.0.0.1:6379> INCRBY counter 100
(integer) 200
127.0.0.1:6379> GET counter
"200"
127.0.0.1:6379> DECRBY counter 100
(integer) 100
127.0.0.1:6379> GET counter
"100"

keys *

這個指令可以讓你看到目前設定在redis 裡面所有的key

127.0.0.1:6379> keys *
1) "first"
2) "counter"
3) "second"

Redis 的資料型態

redis 支援以下型態,上面已經充分示範出string 這個簡單的型態。接下來展示其他沒有在上面示範過的型態!

  1. string(字串)
  2. hash(雜湊)
  3. list(串列)
  4. set(群集)
  5. sorted set(有序群集)

Hash

就像HMSET 的說明一樣:HMSET key field value [field value ...]以底下的說明來說,就像產生了這樣的資料型態:{id: 45, name: Jimmy}

127.0.0.1:6379> HMSET user id 45 name "Jimmy"
OK
127.0.0.1:6379> HGET user id
"45"
127.0.0.1:6379> HGET user name
"Jimmy"
127.0.0.1:6379> HGETALL user
1) "id"
2) "45"
3) "name"
4) "Jimmy"

List

和javascript 的想法很像,有push 和pop , L代表左邊, R代表右邊啊那個如果要列出清單可以用LRANGE指令

127.0.0.1:6379> LPUSH mylist 10 "Hello"
(integer) 2
127.0.0.1:6379> LRANGE mylist 0 1
1) "Hello"
2) "10"
127.0.0.1:6379> RPUSH mylist "World" 20
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 3
1) "Hello"
2) "10"
3) "World"
4) "20"
127.0.0.1:6379> LPOP mylist
"Hello"
127.0.0.1:6379> LRANGE mylist 0 3
1) "10"
2) "World"
3) "20"
127.0.0.1:6379> RPOP mylist
"20"
127.0.0.1:6379> LRANGE mylist 0 3
1) "10"
2) "World"

Set & Sorted Set

1. set 沒有排序得權重,而且不可以增加重複的值

127.0.0.1:6379> SADD myset 1 2 3 4 5 
(integer) 5
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> SISMEMBER myset 5
(integer) 1
127.0.0.1:6379> SISMEMBER myset 50
(integer) 0
127.0.0.1:6379> SADD myset 1 
(integer) 0
127.0.0.1:6379> SADD myset 10
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "10"

2. sorted set 有排序得權重

127.0.0.1:6379> ZADD mysortedset 1 Jimmy
(integer) 1
127.0.0.1:6379> ZADD mysortedset 0 Jim
(integer) 1
127.0.0.1:6379> ZRANGE mysortedset 0 1
1) "Jim"
2) "Jimmy"
127.0.0.1:6379> ZRANK mysortedset Jimmy
(integer) 1

同場加映:pub/sub

laravel broadcast 如果你的driver 設定的是 redis , 你會看到文件會寫道使用redis 的pub/sub 來實現,但這個其實很難透過GUT 去看,首先,你需要開兩個redis-cli

A Redis cli

127.0.0.1:6379> SUBSCRIBE mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mychannel"
3) (integer) 1

B Redis Cli

在這裡你可以用PUBLISH 指令

127.0.0.1:6379> PUBLISH mychannel "HelloWorld"
(integer) 1

這時你回到A 來看,奇蹟發生了!底下多了message, mychannel, HelloWorld !

127.0.0.1:6379> SUBSCRIBE mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mychannel"
3) (integer) 1
1) "message"
2) "mychannel"
3) "HelloWorld"

有趣吧?

小君曰:redis 一日遊

Laravel 建置與執行及相關筆記

Laravel 建置與執行

1.為甚麼要使用Laravel

  • 為甚麼要使用框架
    • Laravel
    • Lumen
    • Slim
  • 歷史
    • Ruby on rails
    • CodeIgniter
    • Laravel
  • Laravel 的設計哲學
    • 使用與光有關的字眼
      • illuminate 照亮
      • spark 火花
    • 提升開發速度和開發者的幸福
  • Laravel 社群

2.設置Laravel 開發環境與介紹

  • 系統需求
  • Composer
  • 本地開發環境
    • Laravel Valet (mac)
    • Laravel Homestead
      • vagrant up
      • vagrant suspend
      • vagrant halt
      • vagrant destroy
      • vagrant provision
  • laravel new & composer create-project
  • Laravel 的目錄結構

3.路由與控制器

Route::get('/',function(){
    return 'Hello World';
    //return view('welcome'); #resources/views/welcome.blade.php
})

#可用正規表達式限制路由
Route::get('/{name}',function(){
    return 'Hello World';
})->where('name','[0-9]+')
  • 路由動詞
    • Get 觀看
    • Post 新增
    • Put/Patch 更新
    • Delete 刪除
    • any
      php= Route::any('/',function(){})
    • match
      php= Route::match(['get','post'],'/',function(){})
  • 中介層 middleware
    • 在進入controller 之前 過濾request 的條件和身分等
    • 有controller construct 和 route 定義兩種方式宣告
  • 使用視圖組件讓所有視圖共用變數
    view()->share('variable','variable);
  • Controller
方法名稱動詞URL
indexgettasks
showgettasks/{task}
creategettasks/create
storeposttasks
editgettasks/{task}/edit
updatepost (put or patch)tasks/{task}
destroypost (delete)tasks/{task}
  • 路由模型綁定

定義一個特定的參數名稱 如{task} 來指示路由解析器,他應該用那個的id 來尋找而不是傳統將id 傳入

  • 顯式與隱式
  • 路由快取
    壓榨載入時間的每毫秒
php artisan route:cache
php artisan route:clear
  • 方法欺騙: method_field() 或者 input-name:_method
  • CSRF
  • 轉址 redirect
  • 中止請求
    • abort(403)
    • abort_if(condition,403)
    • abort_unless(condition,403)
  • 回應 response
    • response()->make() 建立一個http 回應
    • response()->json()
    • response()->download()

4.Blade 模板

 @if 和 @endif
 {{ }}
 @unless @endunless
 @for @foreach @while
 @forelse ==>@foreach 在迭代的物件是空的時可寫應變機制
 @section/@show @yield
 @include 
 @extends
  • 自動blade 指令
  • 全域共用變數
# in App\Providers\AppServiceProvider.php
public function boot()
{
    ...
    view()->share('posts',Post::recent());
}

view()->composer('partials.sidebar',function($view){
    $view->with('posts',Post::recent());
})
#自訂指令
//AppServiceProvider
public function boot()
{
    Blade::directive('ifGuest',function(){
        return "<?php if (auth()->guest()): ?>"
    });
}

5.前端套件

  • laravel-mix

6.收集與處理用戶資料

  • Request
    • all()
    • except() or only()
    • has() or exist()
    • input() 提供第二個參數是預設值
    • json()
    • 路由資料
      • segment()
    • 上傳檔案
      • $request->hasfile() and $request->file()
      • guessExtension
      • getMimeType
      • move()
      • getClientOriginalName()
      • getClientOriginalExtension()
      • isValid()
      • …..其他(p.102)
  • 驗證
    • \$this->validate($request,[rules]);
    • 表單請求 php artisan make:request Arequest
      • authorize()
      • rule / message
    • withErrors 顯示錯誤訊息
    • 規則可以看文件
  • Eloquent 模型的大量賦值
    • $fillable in model
  • {{ … }} v.s {!! … !!}

7.Artisan 與 Tinker

php artisan make:command {action} 

php artisan make:model [name] -m -r ////可以同時生成model 和migration 和controller

8.資料庫與Eloquent

  • Laravel 的 ActiveRecord ORM
  • config/database.php
  • migration 資料庫遷移
    • 系統會依據日期執行他的up/down方法
    • 定義 migration
      • up() 作他的migration
      • down() 恢復他
    • 建立 migration
      • php artisan make:migration create_users_table
      • php artisan make:migration add_votes_to_users_table --table=users php artisan make:migration create_users_table --create=users
    • 撰寫 migration 請參考文件
    • seeder指令
      • php artisan migrate --seed
      • php artisan migrate:refresh --seed
      • php artisan db:seed
      • php artisan db:seed --class=VotesTableSeeder #分別執行
      • php artisan make:seeder ContactsTableSeeder
  • 查詢產生器
    • 任何一種資料庫的功能核心都是查詢產生器
    • 原生DB 靜態介面寫法 php= DB::statement('drop table users') DB::select('select * from student where id=?',[1]) $users=DB::table('users')->get() #....其他如join where insert 等 略
      • 限制方法
        • select()
        • where()
        • orwhere()
        • whereBetween()
        • whereIn()
        • whereNull() whereNotNull()
        • whereRaw()
        • whereExists()
        • distinct
      • 修改方法
        • orderBy groupbY having() havingRaw()
        • skip() take()
        • latest() oldest()
        • inRandomOrder() 隨機排序結果
      • 結束回傳方法
        • get()
        • first() firstOrFail()
        • value() 拉出某個欄位
        • count()
        • min() max()
        • sum() avg()
      • 聯集與聯結
        • join()
        • union()
      • 增加 更新 刪除
        • insert()
        • update()
        • delete()
      • json 的操作
        如果你的資料有json 格式可以這樣寫
        //查詢
        DB:table(‘users’)->where(‘options->isAdmin’,true)->get()
        //更新
        DB:table(‘users’)->update([‘options->isAdmin’,true])
  • Eloquent 模型請參考文件

Eloquent 雖有獨有all 方法,但本書建議不要使用~

  • Eloquent 模型序列化
    • 將某種複雜的東西轉換成字串 toArray() toJson()
    • https://laravel.com/docs/5.5/eloquent-serialization

9.用戶身分驗證與授權

  • php artisan make:auth
  • https://laravel.com/docs/5.5/authentication
  • 守衛
    • config/auth.php
    • auth()->guard(‘user’)->user()
  • 授權 ACL
    • in blade: can cannot allows denies
    • can
    • gete
    • policy

10.請求與回應

  • 請求生命週期
    • 每一個請求都會轉成 Illuminate Request 物件 經過middleware 過濾請求後進入controller 處理好後就產生 Illuminate Response
    • request
    • resonse
    • middleware
      • 每一個請求都會經過每一個middleware 最後進入應用程式,之後產生的回應會經過middleware回到用戶

11.容器

  • 相依注入
    • 每一個類別的相依關係都是從外面住入的 而不是在類別中實例化的
  • 控制反轉
  • app() 全域輔助函式
  • 將類別綁定容器
    • 告知容器如果有人要求一個Logger 實例,則執行這段程式

12.測試

  • Laravel 內建 PHPUnit、Behat、Mockery、Faker 等
  • 單元測試
    • 測試對象是小型、相對獨立的單位,通常是一個類別或方法
  • 整合測試
    • 測試各個單位合作及傳遞訊息的方式
  • 應用測試
    • 驗收或功能測試,測試整個應用程式的行為
  • 在根目錄執行 .\vendor\bin\phpunit
  • 文件
  • php artisan make:test GreetTest
    • 在feature 資料夾裡面增加一個GreetTest.php 繼承TestCase
    • phpunit 會跑feature 和unit 裡的每一個測試
    • $this->visit(url)
    • $this->call(method,url,params,cookies,file,server,content)
    • $this->get/post/put/patch/delete
    • $this->json
    • $this->followRedirects
  • 各種的asset 測試方法
  • 自我練習
  • Browser Tests (Laravel Dusk)
  • 測試特徵
    • withoutMiddleware
    • databasemigrations
    • databasetransactions

13.編寫API

  • 類REST JSON API 基礎
    • 一種用來建構API 的架構格式
    • URL 可以獨特的表示一種資源
    • 使用HTTP動詞來和資源互動
  • CRUD
  • Laravel Passport 作 API 身分驗證
    • https://laravel.com/docs/5.5/passport

composer require laravel/passport

在config/app.php providers 加入 Laravel\PassportServiceProvider::class

php artisan passport:install

將HasApiTokens 的trait 加入 App\user

  • http://www.itread01.com/content/1496308818.html’
  • http://codingweb.tw/2016/12/23/laravel-5-3-api-%E8%AA%8D%E8%AD%89-authentication-passport/

14.儲存與取出

  • 本地與雲端檔案管理器
  • config/filesystems.php
  • storage_path() 輔助函式

php artisan storage:link

  • 使用Storage 靜態介面
  • Storage::disk(‘s3’)->get(‘file.jpg’)
    • get()
    • put()
    • putFile(‘dir’,$file)
    • exists()
    • copy(old,new)
    • move(old,new)
    • prepend() 在前面加
    • append() 在後面加
    • delete()
    • deleteDirectory()
    • size()
    • lastModified
    • files(dir)
    • allFiles(dir) +子目錄內的
    • directories()
    • allDirectories()
  • Session
    • Laravel 的session 管理器支援:檔案、cookie、資料庫、Memcached、Redis
#取出
session()->get('key')
session('key')
#放入
session()->put('key','value')
session(['key','value'])
#session 是陣列時
session->push(key,value)
#全部
session()->all()
#移除
session()->forget(key) #移除一個
session()->flush() #移除每一個
  • Flash session 儲存
    • 只想要在下一個網頁載入時抹除它
session()->flash(key,value)

session()->reflash() / session()->keep(key)
#讓前一個網頁的flash session 繼續使用
  • 快取
  • 快取中的資料是為每一個應用程式儲存的;而session 中的資料是為每一個用戶儲存的
  • config/cache.php
  • https://laravel.com/docs/5.5/cache
    • cache()->get(key,fallbackvalue)
    • cache()->pull(key,fallbackvalue)
    • cache()->put(key,value,minutesOrExpiration)
    • cache()->add(key,value)
    • cache()->forever(key,value)
    • cache()->has()
    • cache()->remember(key,minutes,closure) / cache()->rememberForever(key,closure)
    • cache()->increment(key,amount) / cache()->decrement(key,amount)
    • cache()->forget() / cache()->flush()
  • Cookie
  • https://laravel.com/docs/5.5/requests#cookies
  • Laravel Scout 作全文搜尋
    • 可為Eloquent 模型加入全文搜尋
    • Algolia預設驅動程式
    • 要改成elasticsearch 請另外找辦法

composer require laravel/scout
加到config/app.php
php artisan vendor:publish
composer require algolia/algoliasearch-client-php

在模型內 加入Laravel\Scout\Searchable
toSearchableArray()
searchableAs()

Revie::search('Llew')->get();

config/scout.php 把queue 設為true

15.郵件與通知

  • 郵件
  • https://laravel.com/docs/5.5/mail
  • config/mail.php
    • classic
    • mailable
  • 傳統郵件 classic
Mail::send(view,data,closure)
# closure 裡面有寄件者 收件者 等等
  • Mailable

php artisan make:mail Assignment
會創造一個 Assignment class (extend mailable)
build 方法裡面 return $this->view(‘view.name’)

本地開發

  • log驅動程式
  • Mailtrap.io
  • Universal to
  • 通知 Notification
  • php artisan make:notification WorkoutAvailable

16.佇列、工作、事件、廣播與排程器

  • 佇列可以將昂貴或緩慢的程序移出任何同步呼叫 ex.傳送郵件
  • config/queue.php

php artisan make:job CrunchReports
dispatch(job)->onQueue(‘high’)
php artisan queue:work

  • 事件
  • event()

php artisan make:event UserSubscribed

  • 監聽事件
    • php artisan make:listener jobname –event=UserSubscribed
  • 將事件廣播到WebSocket 與 Laravel Echo
  • 排程器
    • cron job in linux
    • app\console\kernel.php
    • $schedule

php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1 (in linux:將唯一的cron 項目啟動)

17.輔助函式與集合

  • https://laravel.com/docs/5.5/helpers
  • https://laravel.com/docs/5.5/collections

Laravel 的中大型專案架構

1.Model : 僅當成 Eloquent class。
2.Repository : 輔助 model,處理資料庫邏輯,然後注入到 service。
3.Service : 輔助 controller,處理商業邏輯,然後注入到 controller。
4.Controller : 接收 HTTP request,調用其他 service。
5.Presenter : 處理顯示邏輯,然後注入到 view。
6.View : 使用 blade 將資料 binding 到 HTML。
7.Transformer :轉換顯示欄位(API)
8.Formatter: 格式的統一顯示格式(API)
9.Foundation: 獨立掛載功能

總結

  • 職責單一: 就是說每次修改都會有個地方變動而已,所以每個類別都只會有一種改變的理由
  • 跟夥伴們取得一個共識:讓他們知道這樣的設計和方法可以帶來時麼樣的好處,建立一個良好的溝通

Laravel 外掛整理

Debug

開發相關

驗證

權限

管理後台

  • 管理後台平台外掛 類似Django 自帶後台?
    • http://labs.infyom.com/laravelgenerator/
    • LARAVEL-ADMIN
      • http://www.ctolib.com/article/comments/21786
      • http://laravel-admin.org/

Ruby on rail Scaffold

  • Laravel 類似Ruby on rail 的scaffole 指令
    • https://github.com/appzcoder/crud-generator/

Office

  • laravel excel
  • laravel word

Notification 通知

  • laravel flash

Log

  • laravel5 log viewer

開發

  • laravelcollective HTML

PDF

  • laravel dom-pdf

資料庫備份

  • laravel-backup

Datatable

其它

Laravel 的Carbon 小技巧

1.本地化

# app/Providers/AppServiceProvider.php
public function boot()
{
    \Carbon\Carbon::setLocale('zh');
}
#$article->created_at->diffForHumans(); # 1 年前

2.七個小技巧
https://9iphp.com/web/php/less-known-useful-carbon-functions.html

  • isX :True/False
<?php
$dt->isWeekday();
$dt->isWeekend();
$dt->isYesterday();
$dt->isToday();
$dt->isTomorrow();
$dt->isFuture();
$dt->isPast();
$dt->isLeapYear();
$dt->isSameDay(Carbon::now());
  • isBirthday
<?php
$born = Carbon::createFromDate(1987, 4, 23);
$noCake = Carbon::createFromDate(2014, 9, 26);
$yesCake = Carbon::createFromDate(2014, 4, 23);
var_dump($born->isBirthday($noCake));      // bool(false)
var_dump($born->isBirthday($yesCake));
  • StartOfX 和 EndOfX 列表
<?php
$dt = Carbon::create(2012, 1, 31, 12, 0, 0);
echo $dt->startOfDay();     // 2012-01-31 00:00:00
echo $dt->endOfDay();       // 2012-01-31 23:59:59
echo $dt->startOfMonth();   // 2012-01-01 00:00:00
echo $dt->endOfMonth();     // 2012-01-31 23:59:59
echo $dt->startOfYear();    // 2012-01-01 00:00:00
echo $dt->endOfYear();      // 2012-12-31 23:59:59
echo $dt->startOfDecade();  // 2010-01-01 00:00:00
echo $dt->endOfDecade();    // 2019-12-31 23:59:59
echo $dt->startOfCentury(); // 2000-01-01 00:00:00
echo $dt->endOfCentury();   // 2099-12-31 23:59:59
echo $dt->startOfWeek();    // 2012-01-30 00:00:00
echo $dt->endOfWeek();      // 2012-02-05 23:59:59
  • Today, Tomorrow, Yesterday
<?php
$today = Carbon::today();  // assuming 2016-06-24
echo $today;                   // 2016-06-24 00:00:00
$tomorrow = Carbon::tomorrow();
echo $tomorrow;                      // 2016-06-25 00:00:00
$yesterday = Carbon::yesterday();
echo $yesterday;                   // 2016-06-23 00:00:00
  • DiffForHumans + 本地化
<?php
echo Carbon::now()->subDays(5)->diffForHumans()//5daysago
Carbon::setLocale('zh');
echo Carbon::now()->addYear()->diffForHumans(); //一年前
  • 改变 now() 为任意你想要的时间
<?php
$knownDate = Carbon::create(2001, 5, 21, 12);//建测试日期
Carbon::setTestNow($knownDate);    // set the mock 
echo Carbon::now();      // 2001-05-21 12:00:00
  • 星期常量
<?php
var_dump(Carbon::SUNDAY);            // int(0)
var_dump(Carbon::MONDAY);            // int(1)
var_dump(Carbon::TUESDAY);           // int(2)
var_dump(Carbon::WEDNESDAY);         // int(3)
var_dump(Carbon::THURSDAY);          // int(4)
var_dump(Carbon::FRIDAY);            // int(5)
var_dump(Carbon::SATURDAY);          // int(6)

Laravel 的 Eloquent 小技巧

1.同時產生migration 和model

php artisan make:model Post -m
2.scope

class Post extends Model
{
    public function scopePublished($query)
    {
        return $query->where('is_published', true);
    }
}
#Post::published()->get();
#相當於 Post::where('is_published', true)->get();

3.Accessors(访问器)
4.动态方法名称

// Post::where('published', 1)->get();
Post::wherePublished(1)->get();

// Post::where('category', null)->get();
Post::whereNull('category')->get();

// Post::where('category', '!=', null)->get();
Post::whereNotNull('category')->get();

5.扩展访问器

class User extends Model
{
    protected $appends = ['name'];
}

laravel 建立多國語言

https://www.myandroid.tw/bbs-topic-1615-0.sea

  • 先在 lang 下建立 zh_tw 資料夾,然後在 en 與 zh_tw 中加入語言定義檔
  • app()->setLocale(Session::get(‘locale’));

Laravel 實作資料庫讀寫分離

https://laravel-china.org/topics/1879/laravel-5-configuration-read-and-write-separation-and-source-analysis

  • Laravel5读写分离配置比较简单,只需修改config/database.php,下面以MySQL数据库为例,内容如下
'mysql' => [
    'read' => [
        'host' => '192.168.1.1'
    ],
    'write' => [
        'host' => '196.168.1.2'
    ],
    'driver'    => 'mysql',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
]

Laravel 與TDD

關於TDD-1
關於TDD-2

先寫規格在寫程式,而規格本身就是可執行的案例

1.Red – 先寫測試程式,此時因為還沒有實作功能,所以這個測試是會++失敗++
2.Green – 快速實作出功能,讓上述的測試++通過++
3.Refactor – 重構程式碼,去除多餘重複的代碼,提高程式碼品質,同時維持測試通過

注意事項

  • 一定是先寫一個不通過的單元測試,才開始實作功能
  • 每次只新加一個單元測試,只需要剛剛好不通過即可,不要一次加多個測試情境
  • 每次實作功能時,只需要剛剛好通過測試即可,不多也不少
    http://oomusou.io/laravel/laravel-service-provider/
    http://jellybook.me/articles/2017/04/laravel-view-count
    http://www.jianshu.com/p/445ef6820753
    https://my.oschina.net/imayanlong/blog/828855
    https://noob.tw/elasticsearch

詳解MVC 架構

https://hackmd.io/JwBgHAzAJgRsUFoYHZgGMEBYCGMBMCYArBMAhGhAKaZoBmAbBJlEUA==#

Laravel 佈署到 Azure

https://blogs.msdn.microsoft.com/ericsk/2014/01/13/laravel-php-windows-azure/

相關資源

現代PHP 讀書筆記

1.現代的php

  • php 是一個直譯式的伺服器端腳本語言
  • php3
  • php7
  • php 引擎是一個可以解析、直譯和執行php 程式碼的程式
    • Zend Engine
    • HipHop Virtual Machine
  • Hack 程式語言:建立在PHP 之上的新程式語言
    • 靜態型別
  • 動態型別在程式執行時期檢查;靜態型別再編譯時期被檢查(第十二章)

2.特點

  • 名稱空間 namespace
    • from php5.3.0
    • 解決class 名稱的衝突
    • 緊接<?php 標籤之後
    • 讓我們創造出程式碼並獨立運作於其他開發者的程式
  • Import and Alias
    • 不用輸入完整的namespace 落落長的名稱
#不利用import and alias
$response = new \Symfony\Component\HttpFoundation\Response('oops',400);
#利用import and alias
use \Symfony\Component\HttpFoundation\Response as Res
$r=Res('oops',400)
  • 多重匯入(可以好幾個use)
  • 單一檔案多重名稱空間
<?php
namespace foo{

}
namespace bar{

}
  • 全域名稱空間
    • 有些程式碼沒有名稱空間,將存在於全域名稱空間,例如exception
<?php
namespace My\App
class foo{
    public function do(){
        $exception = new \Exception()
    }
}
  • 程式碼作為介面
    • 一個介於兩個php 物件之中的合約,它讓一個物件瞭解另一個物件可以做甚麼,而不是另一個物件是甚麼
    • interface 的撰寫
  • 特徵機制
    • trait
    • from php5.4
    • 模組化實作方式可以被插入到不相關的類別中
<?php
trait mytrait{

}
#use trait
class myclass{
    use mytrait;
}
  • 產生器
    • from php 5.5
    • 簡單的迭代器
    • 文章參考
    • 如果一次把檔案讀入記憶體,會使用掉很大的資源,比較好的處理方式是用streaming的方式讀取,逐步處理讀取的內容。
    • 不會要求你的類別實作Iterator 介面,產生器只在需要的時候計算並產生迭代的數值
<?php
function makeRange($length){
    for ($i=0; $i<$length; $i++){
        yield $i;
    }
}

foreach (makeRange(10000000) as $i){
    echo $i,PHP_EOL;
}
#csv 產生器
function getRows($file){
    $handle = fopen($file,'rb');
    if ($handle ==== false)
    {
        throw new Exception();
    }
    while (feof($handle) === false){
        yield fgetcsv($handle);
    }
    fclose($handle);
}

foreach (getRows('data.csv') as $row){
    print_r($row);
}
  • 閉包
    • from php 5.3.0
$closure = function ($name){
    return sprintf('hello %s',$name);
}

echo $closure('josh') # output=> 'hello josh'

$numberplusone=array_map(function ($number){
    return $number+1;
},[1,2,3]);

print_r($numberplusone);
# output=>[2,3,4]
  • 繫結狀態
    • 閉包裡的閉包 closure in closure
    • 參考文章
    • bindTo and invoke
    • 利用use 關鍵字傳入多個參數到closure 可以利用逗號區分變數,如同其他任何php函式或方法的參數
<?php
function enclosePerson($name){
    return function($doCommand) use ($name){
        return sprintf('%s $s',$name,$doCommand);
    };
}

$clay=enclosePerson('Clay');
echo $clay('get me sweet tea!');
# output=> 'Clay get me sweet tea!'
  • Zend OPcache
  • 內建http 伺服器

php -S localhost:4000

3.標準

  • PHP-FIG 到 Rescue
  • 框架互通性
    • 介面
    • 自動載入
    • 風格
  • PSR
    • PHP 標準協議 PHP standards recommendation
  • PSR-0 已棄用,由PSR-4 取代
  • PSR-1:基本程式碼風格
    • PHP 標籤 你要用PHP 標籤 或 包覆PHP 程式碼
    • 編碼:必須以沒有位元順序標記的utf-8字元編碼
    • 目的:每個php檔案可以用來定義符號或執行一個有作用的動作
    • 自動載入: 你的名稱空間和類別必須支援psr-4 自動載入器標準
    • 類別:使用CamelCase 格式 ex. CoffeeGrinder
    • 常數名稱:必須使用大寫,並且必要時使用底線
    • 方法名稱:使用camelCase 格式,弟一個字要小寫,每個單字的第一個字元是大寫
  • PSR-2:嚴厲程式碼風格
    • 實做PSR-1
    • 縮排:建議表示四個空白字元縮排
    • 檔案和行距:使用LF 做為每一行結尾,檔案最後應該保有最後一行並且不能包含?> 每一行不應超越80個字元,如果不得以,不能超越120個字元,每行結尾不能有空白字元
    • 關鍵字:以小寫表示所有的關鍵字
    • 名稱空間:每一個名稱空間的宣告都必須接上一行空白行,同樣的在USE 區塊之後要接上一行空白行
    • 類別:建議表示起始括號在類別內容結束的下一行,extends 和implements 關鍵字必須和類別名稱同一行
    • 方法:方法定義的起始括號必須要在方法名稱下一行的開頭,結尾在方法內容結束後的下一行開頭,第一個參數不用以空白結尾,而最後一個括號不用預留前置的空白,方法的每一個參數(除了最後一個)都緊接著一個逗號和一個空白
    • 能見度:必須宣告能見度,public protected private,abstract 或finial 在能見度之前,static 在能見度之後
    • 控制流程:所有控制流成關鍵字必須接上一個空白字元,起始括號和控制流成關鍵字同一行,結尾則在新的一行
  • PSR-3:記錄器介面
    • 實做php 記錄器
  • PSR-4:自動載入器

4.元件

  • 框架
  • Packagist 可以尋找合適的元件
  • composer
    • 安裝
    • 元件安裝
  • 建立php 元件
    • src
    • tests
    • composer.json
    • README.md
    • CoONTRIBUTING.md
    • LICENSE
    • CHANGELOG.md

5.良好習慣

  • 消毒、驗證和跳脫
  • 日期時間與時區

6.寄存

  • 共享伺服器
  • 虛擬私有伺服器 VPS
  • 獨佔伺服器
  • PaaS

7.服務開通

vps 的開通

  • php-fpm

8.調校

  • php.ini
  • 記憶體
  • zend opcache
  • 檔案上傳
  • 最大運行時間
  • session 處理
  • 輸出緩衝區
  • 真實路徑快取

9.部屬

  • 版本控制
  • 自動化部屬
    • 保持簡單
    • 保持可預測
    • 保持可反悔
  • 工具
    • capistrano
    • deployer
    • magallanes
    • rocketeer

10.測試

  • 何時開始
    • 開發前
    • 開發當中
    • 開發結束
  • 如何測試
    • 單元測試
      • PHPUnit
    • 測試驅動開發TDD
    • 行為驅動開發BDD
  • 以Travis CI 持續測試
    • 測試應當自動化進行

11.剖析

  • 分析應用程式效能的一種方式
    • 用於開發階段
    • 用於產品階段
  • Xdebug
  • XHProf
  • New Relic 剖析器
  • Blackfire 剖析器

12.HHVM 和 HACK

  • HHVM
    • PHP 引擎
    • 已經最佳化應用程式但是需要更高效能的開法者
  • HACK 語言
<?hh
echo "I'm Hack";
  • 靜態型別
    • 編譯
  • 動態型別
    • 一直到執行階段才被檢查
  • HACK 同時實做了靜態與動態~

13.社群

  • 當地PUG
    • http://php.ug
  • 研討會
  • 導師
    • http://phpmentoring.org
  • 持續吸收新知

補充

PSR PHP標準建議

  • 文章連結
  • PHP標準建議編號1(PSR-1),基本編碼標準. 像是類別(Class)名稱必需宣告為StudlyCaps,方法(Method)的名稱需宣告為camelCase..等
  • PHP標準建議編號2(PSR-2),則是PSR-1的擴充,規範了PHP編碼風格,像是extends或是implements必需與class名稱同一行…等。
  • PHP標準建議編號4(PSR-4),則定義了自動載入(Autoloader標準)

讀軟技能-工程師該如何生活

本月讀了一本技術相關的書,其實也有點和技術打不上八竿子著……然後也順便該為我這個部落格除除草了…..

這本書的書名叫做軟技能,英文書名是:Soft Skills。沒有非常高的技術含量,但卻是作為技術人員可以被提醒的一本「生活指南」。在書中的一開始講到職涯的規劃、之後是如何面試、增加生產力、健身甚至是到理財…. 總之你生活上想到的應該這本書都有談到…..(包含愛情~不過也只有簡短的一章啦)

軟技能的作者非常鼓勵技術人員、工程師寫部落格….. 我個人也是非常同意…希望我也能好好維持這個寫部落格的習慣,將「自己行銷出去」!

分享我個人對於這本書比較印象深刻的地方:

  1. 我們必須將自己視為「企業」,好好規劃自己的職涯
  2. 面試簡單來說就是要獲得面試官的喜歡
  3. 關於職業倦怠:只要你能越過這道牆,到牆的另外一側,突然間事情都開始變得豁然開朗,又能重新拾回你的動機與興趣
  4. LDLT學習法 :Learn – Do – Learn – Teach
  5. 十步驟學習方法:
    • 綜觀全貌
    • 決定範圍
    • 定義成功
    • 尋找資源
    • 建立學習計畫
    • 篩選資源
    • 學習恰到好處的基礎知識(L)
    • 實作(D)
    • 學習足夠學以致用的知識(L)
    • 教導他人(T)
  6. 其中作者在寫理財篇的時候竟然最後談到「十一奉獻」…繼上次我看過C#技術書籍有作者大力向讀者傳福音之後又一個談到信仰XD 但這本書信仰含量不高啦,畢竟他是寫給工程師們的~

其實經歷一些歲月,這本書的內容都會認真的經歷與學習,我覺得並沒有說是很「值得收藏」的書,但或許是一本「當自己程式寫累的時候」翻翻看的書。我想重要的是:人生不能只有程式!

而我前陣子才真的有所體會:我一直過於focus 在提升自身技術能力,但其實技術只是「工具」,程式設計本質上是個「解決問題的藝術」! 願我之後能不斷的進步,能夠成為真正可以解決問題的工程師。

小君曰:好好學習如何生活!