###### 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/)