###### tags: `Laravel`
# Laravel
**for macOS**
[Laravel 安裝教學](https://simonallen.coderbridge.io/2020/04/06/install-laravel/)
**for ubuntu**
[Laravel 安裝教學 1](https://www.howtoforge.com/tutorial/install-laravel-on-ubuntu-for-apache/)
[Laravel 安裝教學 2](https://blog.gtwang.org/linux/ubuntu-linux-laravel-nginx-mariadb-installation-tutorial/)
**教學**
[Laravel Documentation](https://laravel.com/docs/8.x)
[Laravel 教學影片](https://www.youtube.com/playlist?list=PLBd8JGCAcUAFtnWuuqd0tzMwYsVAN4es_)
step1: install php
```
sudo apt install libapache2-mod-php php php-common php-xml php-gd php-opcache php-mbstring php-tokenizer php-json php-bcmath php-zip unzip
```
check php version
php -v
step2: install composer
```
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
```
check composer version
composer --version
step3: install Laravel 8.x
```
useradd -m -s /bin/bash wallet
passwd wallet
su - wallet
composer global require laravel/installer
vim ~/.bashrc
```
step4: 更改 /.bashrc 檔案內容
(Paste the following configuration to the end of the line.)
```
export PATH="$HOME/.config/composer/vendor/bin:$PATH"
```
step5:重新執行剛剛修改的文件 (source)
```
source ~/.bashrc
echo $PATH
```
source 重新值ㄒㄧㄥ
~/ 根目錄
## ubuntu use root to be the user
sudo su
su -wallet
you need to understand your password
## 建立 Laravel 專案
[Download PhpStorm](https://www.jetbrains.com/phpstorm/promo/?gclid=Cj0KCQjw-LOEBhDCARIsABrC0TlCSAhK8vE-UkIk-MgF5veXoMiH9d_ieKezROPocvwr6fVlr9eOIOEaAgW-EALw_wcB)
```
composer create-project laravel/laravel example-app
cd example-app
php artisan serve
```
**可以用 visual studio 開啟專案**
## Laravel 框架
維護 PHP 的框架
預設 port = 8000(已被使用會自動換一個)
terminal : php artisan serve (啟動開發用伺服器)
terminal : php artisan (查詢指令)
terminal : php artisan | grep xxx (用於文件的搜尋)
ex. php artisan | grep route
grep : 使用正則表達式對文件內容進行搜索
```
Laravel development server started: http://127.0.0.1:8000
[Fri Mar 19 12:45:49 2021] Failed to listen on 127.0.0.1:8000 (reason: Address already in use)
Laravel development server started: http://127.0.0.1:8001
```
### Route file
管理進入網頁的路徑(讓網址美觀)
routes/web.php
```php=1
#第一條路徑
Route::get('/', function () {
return view('welcome');
});
#第二條路徑
Route::get('/about.html', function () {
return 'hi';
});
#第三條路徑 http://localhost:8001/cats/4
Route::get('/cats/{id}', function ($id) {
return "我是第" . $id . "隻貓";
});
#第四條路徑(帶你到某個contoller)//尚未建立 Controller 會失敗
Route::get('/about','WelcomeController@about') ;
```
return 的東西是
views/welcome.blade.php
路徑名稱可以自行更改(/about.html)
/ ==> 根目錄
$ ==> php 設定變數的 keyword
. ==> php 連接字串的方式
get ==> 要傳送的資料會放在網址上
### Public file
這個 file 內的資料不需要 route 就可以顯示
且會先執行 public 內的 file(route 後執行)
適合放:靜態頁面、不常更改的頁面
ex. 版權內容
### Controller
建立 controller
(app/Http/Contollers)
terminal:php artisan make:controller WelcomeController
從 router 讀取指向 controller 的網址
use App\Http\Controllers\WelcomeController;
Route::get('/about', [WelcomeController::class, 'index']);
接下來就到 WelcomeContoller 使用 index function
public function index()
{
return view("about"); //resource -> view -> about.blade.php
//return view("pages.about"); // pages file 中的 about.blade.php
//return "hi";
}
### 參數傳遞
Controller to View
public function index(){
$name = "wallet"
return view("index",['name' => $name])
}
go to php
{{ $name }}
頁面會出現 wallet
### blade
blade 是 laravel 處理 view 的樣板引擎
```
Route::get('/about', function () {
return view('about', ['name' => 'John']);
});
```
當網址在 http://127.0.0.1:8001/about 這個目錄時,會展示 about.blade.php 這個 view,並且傳遞 name 這個參數(內容為John)
這個參數會傳遞到網頁上,但必須套過指令呈現這個參數內容。
```
Hello, {{ $name }}.
```
* {{ }}
blade 的一種表達方式 PHP's htmlspecialchars function
htmlspecialchars 將 html 的特殊字元做轉換(過濾輸入)
可以避免被惡意使用者將程式碼注入到網頁上,其他使用者在觀看網頁時就會受到影響
ex. 製作表單給使用者填寫接收html,預設情況下訪客可以毫無阻攔地在評論中加入惡意的 script tag
```
<script>alert('hi')</script>
```
* {!! !!}
表示不要過濾輸入
* if statements
```
@if (true)
123
@elseif (...)
456
@else
789
@endif
```
* unless (if not)
```
@unless (true)
123
@endunless
```
上面的程式碼表示當判斷式為 false 時印出 123
* for statements
```
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
<p>This is user {{ $user }}</p>
@endforeach
```
* loop variable
```
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
<p>This is user {{ $user }}</p>
@endforeach
```
$loop 在迴圈裡是內建的
loop->first 表示 $users[0]
loop->last 表示 $users[-1]
* Comments
```
{{-- This comment will not be present in the rendered HTML --}}
```
### layout
避免做重複的事
在 resource file 底下建 xxx file(layouts) ,並建立一個 ooo.blade.php 可以用來做成各個網頁的樣板(固定格式)讓其他網頁繼承這個樣板格式
主要用法在 xxx.blade.php 放入大部分要繼承網頁的固定樣版,接著挖洞給其他繼承的網頁把漏洞補齊
* 挖洞的方法
```
@yield('qqq')
```
* 填坑的方法
```
1. 繼承要使用的 layout
@extends('xxx.ooo')
檢查原始頁面 可以看到繼承的原始碼
2. 填坑 把...的內容填入剛剛挖的同名洞裡
@section('qqq')
...
@endsection
// 一行搞定不需要 @endsection
@section('title', 'Page Title')
```
### resource
由以下指令建立的 controller 和之前的不同
* 多了一些函式
* 可以很快建立網址路徑
```
terminal : php artisan make:controller PostsController --resource
```
PostsController 已預先設計好 7 個 function,原先每個 function 都要設置 1 條路徑
使用 Route::resource 可以 1 條路徑代替 7 條路徑
並且會自動連接預設的 controller action
```
Route::resource('posts',PostsController::class);
Route::resource('posts',PostsController::class)->only(['index','show']);
```
only([]) 選擇你要開放的路徑
except([]) 選擇你不要開放的路徑
查詢路徑
```
terminal : php artisan route:list
```
### Migrations
[Download SQLite](https://sqlitebrowser.org/dl/)
可以幫助資料庫做版本控制(可以回到某一個修改時間去還原資料)
在一個團隊裡可以一起編輯資料庫不會有環境不同的問題
擁有 database 歷史的存檔紀錄(新增刪除欄位等等)
migration 不只是資料表還有存檔紀錄 (類似 git)
* 建立 database 描述檔
```
php artisan make:migration mydata --create="posts"
```
* 刪除 database 描述檔
```
rm database/migrations/2021_05_02_031620_mydata.php
```
config/database.php 內有 mysql、sqlsrv、sqlite、pgsql 等資料庫
在 .env 檔做更換 預設 mysql
* database 換成 sqlite
```
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database/my_data.sqlite
DB_USERNAME=root
DB_PASSWORD=
```
* 查看 database 更改的紀錄(migration 描述檔)
```
php artisan migrate:status
```
* 回到 database 過去的紀錄
```
php artisan migrate:rollback
```
* 執行 database 描述檔
```
php artisan migrate
```
* update 資料表內容
先新增一個新的 migration
資料表名稱要相同
```
php artisan make:migration mydata1 --table="posts"
```
```
Schema::table('posts', function (Blueprint $table) {
$table->string('subtitle') ->nullable();
});
```
Difference between String and Text
```
$table->string('title'); // VARCHAR 型態
$table->text('content'); // TEXT 型態
```
### 與 Model 相關的名稱解釋
Eloquent、ORM、ActiveRecord、Model
* ActiveRecord
* Active Record 是一種 ORM 的框架
* 可以定義資料表之間的關聯性 (作者id、作者的個人資料)
* 是一種設計模式,將資料表中一筆資料包成一個物件,加入額外的邏輯讓我們使用這個物件(商業邏輯)
* 給予物件很多方法可以操作使用
![](https://i.imgur.com/BMMaIgW.png)
* Model
* 是根據 Active Record 創造出來的產物
* 屬於設計者和資料表之間的中介,model 將設計者傳達的訊息轉換成 SQL 語言給資料表
* ORM (物件關聯對映)
* 讓你使用物件導向語法來操作關聯式資料庫
* 介於資料庫和 model 之間
* 幫助使用者更簡便、安全的去從資料庫讀取資料
* 優點:可避免網路攻擊 SQL injection、 簡化性、通用性(穿梭在不同資料庫)
* 缺點:效能、複雜的搜索反而讓程式不易撰寫
* SQL injection
* 讀取到駭客傳來的 SQL 指令(delete),可以任意刪除資料庫內的資料
* 商業邏輯(和商業行為無關)
* 在寫這支程式時,是為了處理"什麼"
* 處理事務的流程、公式與規則
* 留言版的目的就是 "留言" ,所以在這支程式的 "商業邏輯" 就是指"處理留言"的邏輯。包括留言如何編號、留言資料(留言者IP or ID)如何儲存、留言顯示的規則(排序 or 分頁規則)....等,就是留言板程式的商業邏輯。
### Model
建立 model
```
php artisan make:model Post
```
建立 model 與 資料表
```
php artisan make:model Post -m
```
表示建立 Post(model) 會直接產生 posts(migration)
model v.s table (命名慣例)
CoC(convention over Configuration)
慣例優於設定 (按照框架可以寫短短的程式碼)
![](https://i.imgur.com/PhCTbYI.png)
但也可以設定
```
//Post.php(原 model 應為 UserPost.php)
protected $table = "user_posts"
```
### CRUD(create read updata delete)
* 和 app(database) 互動的指令
```
php artisan tinker
```
* tinker
tinker 算是一個介面:輸入 Book::count()
SQL 語言的 select count(*) from Book 指令
計算 Book 資料表內共有幾筆資料
```
>>> Book::count()
[!] Aliasing 'Book' to 'App\Models\Book' for this Tinker session.
=> 0
```
#### Create
* 建立一筆資料
```
>>> $b = new Book
=> App\Models\Book {#4286}
>>> $b
=> App\Models\Book {#4286}
>>> $b->title = "投資最重要的事"
=> "投資最重要的事"
>>> $b->price = 100
=> 100
>>> $b->description = "教導讀者投資心法"
=> "教導讀者投資心法"
>>> $b
=> App\Models\Book {#4286
title: "投資最重要的事",
price: 100,
description: "教導讀者投資心法",
}
```
* 將資料丟入資料表
```
>>> $b->save()
```
* 另一種建立資料的方法
一開始會顯示錯誤訊息,因為 laravel 不允許一次丟入大筆的資訊
```
>>> $data = ["title" => "python 網路爬蟲","price" => 200,"description" => "網路 爬蟲的教學課程"]
=> [
"title" => "python 網路爬蟲",
"price" => 200,
"description" => "網路爬蟲的教學課程",
]
>>> Book::create($data)
Illuminate\Database\Eloquent\MassAssignmentException with message 'Add [title] to fillable property to allow mass assignment on [App\Models\Book].'
```
解決方法:在 model(Book.php)加入下方指令
```
//Book.php
protected $fillable = ["title","description","price","available"];
```
$fillable ==> 表示允許大批資料加入的欄位(白名單)
並重新開啟 php artisan tinker 即可加入資料表
#### Read
* 讀取第一筆資料
```
>>> Book::first()
```
* 找第 n 筆資料
```
>>> Book::find(n)
>>> Book::findOrFail(n) //會出現 exception
```
* 尋找某個條件的資料
```
>>> Book::where("price",100)->get()
>>> Book::where("title","like","投資%")->get()
```
回傳值類似 array(可以用foreach 一一讀取)
like 是 sql 的模糊查詢
* 資料排序
```
//降冪排序
>>> Book::where("title","like","網路爬蟲%")->orderBy('id','desc')->get()
```
* 對數值的資料做運算
```
>>> Book::sum('price')
>>> Book::avg('price')
>>> Book::max('price')
>>> Book::min('price')
```
## Model 關聯性
**一對一 (單向)**
hasOne()
* 實作 1 Store 對應到 1 Book
we need to 2 model (Store & Book)
基於 CoC (慣例優於設定) 會產生 2 table (stores & books)
兩個 model 要產生關聯性
Book model 的 books 資料表需要 store_id 來作為連接的 key
store_id 的建立要開一個 migration
```
terminal(tinker):
$s1 = Store::first() //第一個商店
$b1 = Book::first() //第一本書
$s1->book() //HasOne
$s1->book()->save($b1)
```
**一對一 (雙向)**
hasOne()
belongsTo()
```
terminal(tinker):
$s1 = Store::first() //第一個商店
$b1 = Book::first() //第一本書
$b1->store() //BelongsTo
$b1->store()->title //store 名稱
```
資料表雙向連接表示 Store 可以查到 Book、 Book 可以查到 Store
**一對多**
hasMany()
belongsTo()
**多對多**
hasMany()
belongsToMany()
需要第三方表格的協助
ex.每一個 book 有很多 author,每一個 author 又有很多 book
此時需創建一個新的資料表 author_book 來存放兩者之間的關係
內部資料有 book_id、author_id
1. author_book 資料表建立
php artisan make:migration create_author_book
2. 建立內部資料 book_id、author_id
3. 資料連結
```
terminal(tinker):
$a1 = Author::first() //第一個作者
$b1 = Book::first() //第一本書
$b1->authors() //BelongsToMany
$b1->authors()->save($a1) // $b1 作者是 $a1 在 author_book 資料表呈現
```
## Laravel 工廠模式
快速生成資料
工廠模式:在 laravel 中使用 Faker(PHP 函式庫)來製造假資料,像工廠一樣生產
```
terminal:php artisan make:factory BookFactory --model=Book
```
--model = 對應的 model 名稱
加入想要被生產的資料 $this->faker->...
```
public function definition()
{
return [
"title" => $this->faker->name(),
"description" => $this->faker->text(),
"price" => $this->faker->numberBetween(100,500)
];
}
```
到 php artisan tinker 測試工廠生產的資料
**製造不等於放入 database**
* 製造一筆資料
```
Book::factory()->make()
```
* 製造多筆資料 ex. 3 筆
```
Book::factory()->count(3)->make()
```
* 寫入 database
```
Book::factory()->create()
```
* 指定某一欄位,其餘隨機
```
Book::factory()->make(["title" => "視覺 SLAM 十四講"])
```
### 正向建立 vs 反向建立
hasMany ==> has , belongsTo ==> for
* 建立一對多的資料
```
Store::factory()->has(Book::factory()->count(3))->create()
```
在這個隨機生成的書店裡,生成三本隨機的書,並放入資料表中
## 種子資料 (seeder)
也可稱為初始資料
可以搭配工廠模式,讓資料的建立不局限於 tinker
預設的 seeder ==> database\seeders\DatabaseSeeder.php
* 建立 seeder
```
terminal: php artisan make:seeder StoreSeeder
```
在 seeder 內的 run 函式加入工廠模式,並記得引用 model
ex. 對 Store model 使用工廠模式(引用 Store)
```
use App\Models\Store;
```
# Login ui
[Laravel 用戶驗證](https://www.youtube.com/watch?v=aKGvUnF5xWc)
```
terminal:composer require laravel/ui
terminal:php artisan ui vue --auth
```
將會增加 login、register 等等介面
進入 login 網站
```
http://127.0.0.1:8000/api/user
```
會自動切換到
```
http://127.0.0.1:8000/login
```
一開始網頁看請來會很簡陋
因為沒有載入 javascript 的相關套件
* NPM(Node Package Manager)是一個線上套件庫,可以下載各式各樣的 Javascript 套件來使用
```
npm install
```
沒有 npm 就要先[下載 Node.js](https://nodejs.org/en/)
```
node -v
npm -v
```
**在專案底下輸入**
```
npm init
```
![](https://i.imgur.com/pn6lJrS.png)
照著()內的文字輸入,無括號則留空字串""
```
npm install
npm run watch
```
完成上述步驟即可取得 css 樣式
原本的網站
![](https://i.imgur.com/cIOFsoM.png)
加入 js 相關套件後的網站
![](https://i.imgur.com/cxcnlvt.png)
## register
註冊後按註冊鍵
可能會有誤(database does not exist)
解決方法到 .env 將 DB_DATABASE 的路徑改成絕對路徑
## Authenticate 權限
一條路徑用中介層(Middleware)判定是否在權限內
在權限內 ==> 跟著路徑導引到頁面
不在權限內 ==> redirect 到另一個頁面去取得權限
* 實作 middleware
存放在 app/Http/Middleware
```
php artisan make:middleware filename
```
對 routes 加入中介層
```
Route::resource('posts',PostsController::class)->only(['index','show'])->middleware('auth');
```
ex.
登入才能進入 /posts 路徑的頁面,因此在該路徑上加入
->middleware('auth')
而 middleware 管理 auth 的 php 指定未登入的使用者會被 redirect到 login 介面
# 網頁功能:建立表單
在 controller 的 function (create) 當進入到這個頁面
可以使用的功能提交表單(title & content)
**Code**
```
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<h2>Create Post</h2>
<form action="{{ route('posts.store') }}" method="post">
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" id="title" name="title">
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea class="form-control" id="content" name="content" rows="5"></textarea>
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
</div>
</div>
@endsection
```
**web**
![](https://i.imgur.com/SLGkcx7.png)
按下 create 鍵 (會導向 posts.store)
此時會產生以下的錯誤(因為 laravel 有一個防止跨網站提交表單的機制)
![](https://i.imgur.com/fIBTbnL.png)
**解決方法**
在表單一開始加入
```
@csrf
```
功能:為表單加入 token 確保我們收到的表單是來自同一個網站(自己的網站)
![](https://i.imgur.com/MbAtGW2.png)
## 使用 css 樣式改變網頁內容
在 resource\sass\app.scss 更改樣式
```
.card{
margin-bottom:20px;
}
```
更改 class=card 的樣式
可能會沒有作用
修改 .scss 檔後需要在 terminal執行下列指示指示
```
npm run dev
```
## 加入超連結
```
<a href="{{ route('posts.edit',[$post->id]) }}">(Edit)</a>
```
表示點擊(Edit)時,路徑會被指向 /posts/{id}/edit (已預設的路徑)
## 傳送資料到其他的 view
with & compact
```
return view('post.edit')->with('post',$post);
return view('post.edit',compact('post'));
```
再從 edit.blade.php 取得資料
```
{{ $post->content }}
```
也可以使用
```
session('...')
```
## validation
可以用來避免網頁產生錯誤,改成用警告語提醒使用者
類似 try catch 例外處理
```
$request->validate([
'title' => 'required|unique:posts|max:255',
'content' => 'required',
]);
```
表示 title 欄位必須符合 required、unique、max
* required ==> 一定要有值
* unique:posts ==> 在 posts資料表必須唯一
* max ==> 限制 size
* 用|隔開表示前面的錯,就不會驗證後面的
(Stopping On First Validation Failure)
```
@if( $errors -> any())
<div class= "alert alert-danger">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
```
**$errors**
自動檢查 session 內的錯誤資料
如果錯誤存在的話,會自動綁定這些錯誤訊息到 view
每個 view 都有一個 $errors 讓使用者方便使用
## 方法欺騙 (Method Spoofing)
使用原因:HTML5 表單,僅支援 GET 與 POST 方法
放在表單的隱藏層
通常是利用 <input> 標籤 hidden type 看不見的特性,
提供一個 隱藏的 _method 欄位,並在 value 屬性中 設置「真正」要用的方法名稱
不受支援的有 put、delete
```
<form action="https://echo.paw.cloud" method="post">
<input type="hidden" name="_method" value="PUT">
</form>
```
## PHP 換行
```
{!! nl2br(...) !!}
```
## 隱藏未有權限使用者的功能
```
@auth
<a href="{{ route('posts.edit',[$post->id]) }}">(Edit)</a>
@endauth
```
---
## Bootstrap
Bootstrap 是一組用於網站和網路應用程式開發的開源前端框架
其包括 HTML、CSS、JavaScript 的框架
### alert
[alert 相關用法](https://getbootstrap.com/docs/4.0/components/alerts/)
alert-danger 呈現紅色警示列
```
<div class="alert alert-danger" role="alert">
This is a danger alert—check it out!
</div>
```
### button
[button 相關用法](https://getbootstrap.com/docs/4.0/components/buttons/)
btn-primary 呈現藍色按鈕
```
<button type="button" class="btn btn-primary">Primary</button>
```
---
## 自製 login
```
<h1>User Login</h1>
<form action="user" method="POST">
@csrf
<input type="text" name="user" placeholder="Enter user name"><br><br>
<input type="password" name="password" placeholder="enter user password"><br><br>
<button type="submit">Login</button>
</form>
```
## laravel connect with phpmyadmin(MariaDB)
[UBUNTU 16.04 LTS 搭建APACHE, MARIADB PHP7 (LAMP)、PHPMYADMIN](http://blog.twbryce.com/ubuntu-16-04-lts-%E6%90%AD%E5%BB%BAapache-mariadb-php7-lamp%E3%80%81phpmyadmin/)
adapt to ubuntu 20.04
[Laravel connect phpmyadmin](https://blog.gtwang.org/linux/ubuntu-linux-laravel-nginx-mariadb-installation-tutorial/)