---
# System prepended metadata

title: Laravel
tags: [Laravel, artisan, PHP, 全端]

---

Laravel
===
###### tags: `PHP` `Laravel` `artisan` `全端`

# 雜記
-  composer create-project laravel/laravel pjname --prefer-dist

- Laravel Engine: Blade

- PSR-4 
  PHP-FIG 建議規範

- 讓使用者輸入錯誤後不用重新輸入一次
  {{ old('name', 'default') }}

# Laravel 資料夾結構
## resources 
靜態的東西都在這
    
### views
此資料夾內放置主要畫面樣板
只要副檔名 blade.php 的東西
就會用 blade engine 解析
再丟進 php interpreter
    
## routes
路徑的管理
    
### web.php
用 GET POST 等 method 以及 URL 來做網頁導向

- 基本使用方式
    e.g.
    ```php=
    Route::get('/user/{name?}', function($name = 'Nobody') {
        return 'Hi, '.strtoupper($name);
    });
    ```
    假設使用者 Get URL /user/Ljp
    會回傳 'Hi, LJP'
    假設使用者 Get URL /user
    會回傳 'Hi, NOBODY'
    
    Route::get 的第一個參數中的 \{\} 表示這是 URL 中的參數
    它會傳給 Route::get 中第二個參數的 function
    若 \{\} 尾巴有一個 '?' 表示此參數可有可無

- 配合 Controller
    - web.php
    ```php
    Route::get('/', 'HomeController@index');
    ```
    - HomeController.php
    ```php
    class HomeController extends Controller
    {
        public function index () {
            return view('welcome');
        }
    }
    ```

    這個意思是將 URL '/' 導向到 HomeController 中的 index function
    
- 幫 Route 取名
    取名字後, 在要用到 `{{ route(...) }}`的時候可以直接用這個名字就好, 不用整個路徑打出來
    - web.php
    ```php
    Route::get('/', 'HomeController@index')->name('home.index');
    ```
    - edit.blade.php
    ```php
    ...
        <a href="{{ route('home.index') }}">test</a>
    ...
    ```

# Routing
## Controller
```shell
artisan make:controller NameOfController
```
若加上參數 --resource
會創造已經寫好
- index()
- create()
- store()
- show()
- edit()
- update()
- destroy()
的 Controller

## 設定 route
在 route/web.php 設定
語法大致上為
```php
<?php
Route::method('url', 'Controller@function')->name('在其他 html 中, 連結的別名');
```

e.g.
```php
<?php
Route::get('products', 'ProductController@index')->name('products.index');
```

# Laravel With DB
## database.php & .env
- 先創好 database
- 設定檔案
- 在 database.php 中 env(...) 都是去 .env 找參數

設定好後, 敲下指令
```shell
artisan migrate:install
```
用你現在的設定檔, 連上資料庫後, 在資料庫建立資料表 migration, 用來測試是否連得上

## migration
### 產生
```shell
artisan make:migration {action}_{table}_table

artisan make:migration create_products_table
```
此指令會在 database migrations 中產生新的 migration

### 執行資料庫變更
寫完 migration 後, 要執行
```shell
artisan migrate
```
資料庫變更才會真的被套用

用爛了就
```shell
artisan migrate:rollback
```

## Model
參考: https://laravel.com/docs/5.7/eloquent

跟資料庫讀寫有關的動作, 可透過Model類別來操作

所以 check 好已經連動到資料庫, 就可以實作 Model 來與程式碼連動

```shell
artisan make:model {name}

artisan make:model Task
```

之後設定 `$table` 來設定此 Model 跟哪張 table 連動
```php
<?php
...
class Product extends Model
{
    protected $table = 'products';
    protected $fillable = [
        'Blablabal',
        'abcabc'
    ];
}
```

### 方法
- create()
- update()
- ModelName::orderBy()
  - get()
- save()
f
## Seeding
定義用 artisan 執行 db 時會如何操作資料庫

### 用 artisan 產生 seeder 檔案
```shell
artisan make:seeder ProductsTableSeeder
```
### 撰寫 seeder 檔案內容
用建立一個紀錄來作例子, ProductsTableSeeder.php:
```php
<?php
...
class ProductsTableSeeder extends Seeder
{
    ...
    public function run()
    {
        Product::create([
            'name' => 'Ming',
            'price' => '87'
        ]);    
    }
}
```

### 設定 DatabaseSeeder.php 的執行緒
database/seeds/DatabaseSeeder.php:
```php
<?php
...
class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(ProductsTableSeeder::class);
    }
}
```

### 用 artisan 執行 db
```shell
artisan db:seed
```

## 應用
若
`web.php` route 中設定的變數 跟 `controller` 裡的 function parameter 設定一樣
且
`controller` parameter 有給他 `model` class
那
去那 `model` 代表的 table, 用此 parameter 當 primary key 去撈資料

## Relationship
兩個 model 都要先定義彼此的關係

- Product.php
```php=
<?php
...
public function category()
    {
        return $this->belongsTo(Category::class, 'category_id', 'id');
    }
...
```

- Category.php
```php=
<?php
...
public function products() {
        return $this->hasMany(Product::class, 'category_id');
    }
...
```
建立關係語法:
[`自己之於對方的關係`](`要建立關係的 Table`, `自己要綁定的 foreign key`, `要綁定對方的 foreign key`)

https://laravel.com/docs/5.7/eloquent-relationships#querying-relations

# Git 問題

## composer
就像 npm 之於 Node.js
composer 是 php 的模組管理套件

## 合作問題
pull 別人的專案下來後, 因為 .gitignore 的關係
會缺一點東西
以下指令把需要的補回來

### /vendor
補回 /vendor 
```shell
composer install
```

### .env
執行設定於 composer.json 中的腳本
產生 .env
```shell
composer run-script post-root-package-install
```

### app key
產生 app key
```shell
composer run-script post-create-project-cmd
```


# Validation
分三類
- Validation Facade
- Controller Validation
- Form Request Validation

## Controller Validation
Controller 本身有 validate 這個方法

格式像是
xxxxController.php
```php
<?php
...
    public function test() 
    {
        ...
        $this->validate(要檢驗物件, 規則, 錯誤訊息);
        ...
    }
...
```

實際例子, 請看`參考4`
```php
<?php
$rule = [
            'name' => 'required',
            'price' => 'required',
            'unit' => 'required',
            'description' => 'required'
];

$messages = [
    'name.required' => '名字沒寫QQ'
];

$this->validate($request, $rule, $messages);
```

在 laravel 的 views 中自動有加 MessageBag $errors 參數
顯示在 html 上請看`參考2`

參考: 
1. https://laravel.com/docs/5.7/validation#available-validation-rules
2. https://laravel.com/docs/5.7/validation#quick-displaying-the-validation-errors
3. https://laravel.com/docs/5.7/validation#custom-error-messages
4. https://stackoverflow.com/questions/45007905/custom-message-laravel-validation



# Auth
安裝, 會自動跑出登入登出忘記密碼三小的 view, 並自動把 route 寫好
```shell
artisan make:auth
```

在 route 中可以這樣保護 URL
```php=
<?php
...
Route::middleware('auth')->group(function () {
    Route::get('/', 'DashboardController@index')->name('dashboard.index');
    Route::get('/products', 'ProductController@index')->name('products.index');
    Route::get('/products/create', 'ProductController@create')->name('products.create');
    Route::post('/products', 'ProductController@store')->name('products.store');
    Route::get('/products/{product}/edit', 'ProductController@edit')->name('products.edit');
    Route::patch('/products/{product}', 'ProductController@update')->name('products.update');
    Route::delete('/products/{product}', 'ProductController@delete')->name('products.destroy');
});
...
```

也可以
```php=
<?php
Route::get('/', 'DashboardController@index')->name('dashboard.index')->middleware('auth');
```
# API
## Html 狀態碼
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status


## api.php
若是用網頁互動, route 是設定在 web.php
而 api 則是在 api.php
差別在於 middleware
kernel.php 設定更多

## controller
```php
<?php
...
public function index()
    {
        $products = Product::all();

        return response()->json($products);
    }
```
之後 URL 打 api/...
就可以看到 json 編碼過後的 products

**Chrome 套件**
- json formatter

## CORS(Cross-Origin Resource Sharing)
如何讓API可以正確地被跨網域呼叫?
設定 Header

**套件**
https://github.com/spatie/laravel-cors#laravel

## Resource
```shell
artisan make:resource ProductResource
```
原先 controller 從資料庫撈到資料後就直接丟出去
可是一來欄位洩漏出去, 二來有時候也不用全丟
這時就可以用 Resource 來定義要丟出去的資源

## Auth

### 安裝套件
https://jwt-auth.readthedocs.io/en/develop/laravel-installation/
```shell
composer require tymon/jwt-auth:1.0.0-rc.3
artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
```

### 設定 User Model
https://jwt-auth.readthedocs.io/en/develop/quick-start/#update-your-user-model

### 設定 config/auth.php
https://jwt-auth.readthedocs.io/en/develop/quick-start/#configure-auth-guard

### 保護 API
- api.php
```php
<?php
...
Route::middleware('auth:api')->group(function () {
    Route::post('orders', 'Api\OrderController@store');
    Route::post('logout', 'Api\AuthController@logout');
});
...
```

### Controller
https://jwt-auth.readthedocs.io/en/develop/quick-start/#create-the-authcontroller

AppDataService
NoWrapping