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