Difference between revisions of "Basic Laravel Training (for CV)"
Jump to navigation
Jump to search
(12 intermediate revisions by the same user not shown) | |||
Line 103: | Line 103: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== View (Blade Template) ==== | ==== View (Blade Template) ==== | ||
− | * <meta name="csrf-token" content="{{ csrf_token() }}"> | + | * <code><meta name="csrf-token" content="{{ csrf_token() }}"></code> |
− | * {{ config('app.name', 'Laravel') }} | + | * <code>{{ config('app.name', 'Laravel') }}</code> |
* @include('inc.messages') | * @include('inc.messages') | ||
* @yield('content') | * @yield('content') | ||
Line 138: | Line 138: | ||
* php artisan make:model MODEL_NAME --migration | * php artisan make:model MODEL_NAME --migration | ||
* php artisan make:migration MIGRATION_NAME | * php artisan make:migration MIGRATION_NAME | ||
+ | * Sample | ||
+ | *: <!--syntaxhighlight lang="php"> | ||
+ | <?php | ||
+ | public function up() | ||
+ | { | ||
+ | Schema::enableForeignKeyConstraints(); | ||
+ | Schema::create('cvdrive_comments', function (Blueprint $table) { | ||
+ | $table->increments('id'); | ||
+ | $table->text('comment')->nullable($value = true); | ||
+ | $table->integer('cvdrive_item_id')->unsigned(); | ||
+ | $table->integer('user_id')->unsigned(); | ||
+ | $table->integer('cvdrive_comment_id')->unsigned()->nullable($value = true); | ||
+ | $table->timestamps(); | ||
+ | |||
+ | $table->foreign('cvdrive_item_id')->references('id')->on('cvdrive_items')->onDelete('cascade'); | ||
+ | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); | ||
+ | $table->foreign('cvdrive_comment_id')->references('id')->on('cvdrive_comments')->onDelete('cascade'); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | public function down() | ||
+ | { | ||
+ | Schema::dropIfExists('cvdrive_comments'); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight--> | ||
==== Eloquent ==== | ==== Eloquent ==== | ||
+ | * php artisan tinker | ||
* new item | * new item | ||
*: <syntaxhighlight lang="php"> | *: <syntaxhighlight lang="php"> | ||
Line 149: | Line 177: | ||
$course->save(); | $course->save(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | * duplicate item : $newcourse2 = $newcourse->replicate(); | ||
* fetch item | * fetch item | ||
*: <syntaxhighlight lang="php"> | *: <syntaxhighlight lang="php"> | ||
Line 223: | Line 252: | ||
=== Authentication === | === Authentication === | ||
+ | ==== Preparation ==== | ||
+ | * php artisan make:auth | ||
+ | |||
==== User System ==== | ==== User System ==== | ||
+ | * $user = Auth::user(); | ||
+ | * $id = Auth::id(); | ||
+ | * Protect route | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | // Protect in route | ||
+ | Route::get('index','homecontroller@index')->middleware('auth'); | ||
+ | |||
+ | // Protect in constructor | ||
+ | public __construct{ | ||
+ | $this->middleware('auth', ['except' => ['getActivate', 'anotherMethod']]); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Check if the user is loggedin | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | if (Auth::check()) { | ||
+ | // The user is logged in... | ||
+ | } | ||
+ | </syntaxhighlight> | ||
==== Access Control ==== | ==== Access Control ==== | ||
+ | * Using Policy | ||
+ | * php artisan make:policy POLICY_NAME | ||
+ | * php artisan make:policy POLICY_NAME --model=MODEL // Create with basic CRUD | ||
+ | * Register Policy in <code>App/Providers/Authserviceprovider.php</code> | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | protected $policies = [ | ||
+ | Post::class => PostPolicy::class, | ||
+ | ]; | ||
+ | </syntaxhighlight> | ||
+ | * Writing Policy | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | public function update(User $user, Post $post){ | ||
+ | return $user->id === $post->user_id; | ||
+ | } | ||
+ | |||
+ | // Check Policy | ||
+ | if ($user->can('update', $post)) { | ||
+ | // | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Writing Policy without model | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | public function create(User $user){ | ||
+ | // | ||
+ | } | ||
+ | |||
+ | // Check Policy | ||
+ | if ($user->can('create', Post::class)) { // ต้องบอก class เพื่อจะได้รู้ว่าจะไปเรียกจาก policy ของ class ไหน | ||
+ | // | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Writing Policy in View | ||
+ | *: <syntaxhighlight lang="php"> | ||
+ | @can('update', $post) | ||
+ | <!-- The Current User Can Update The Post --> | ||
+ | @elsecan('create', App\Post::class) | ||
+ | <!-- The Current User Can Create New Post --> | ||
+ | @endcan | ||
+ | |||
+ | |||
+ | @if (Auth::user()->can('update', $post)) | ||
+ | <!-- The Current User Can Update The Post --> | ||
+ | @endif | ||
+ | |||
+ | |||
+ | @unless (Auth::user()->can('update', $post)) | ||
+ | <!-- The Current User Can't Update The Post --> | ||
+ | @endunless | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Testing == | ||
+ | * Make test <syntaxhighlight> | ||
+ | // Create a test in the tests/Feature directory... | ||
+ | php artisan make:test UserTest | ||
+ | |||
+ | // Create a test in the tests/Unit directory... | ||
+ | php artisan make:test UserTest --unit | ||
+ | </syntaxhighlight> | ||
+ | * Run test : In your project root run <pre>vendor/bin/phpunit</pre> | ||
+ | * The CSRF middleware is automatically disabled when running tests | ||
+ | * Use model factory to generate fake object | ||
+ | ** Factory is at <code> database/factories/UserFactory.php</code> | ||
+ | ** In testing <syntaxhighlight lang="php"> | ||
+ | $user = factory(User::class)->create(); | ||
+ | $response = $this->actingAs($user)->withSession(['foo' => 'bar'])->get('/'); | ||
+ | </syntaxhighlight> | ||
+ | * Browser Test [[https://laravel.com/docs/5.8/dusk https://laravel.com/docs/5.8/dusk]] | ||
+ | * And more ... in Laravel Doc | ||
+ | |||
+ | == More and More == | ||
+ | === Middleware === | ||
+ | * อยู่ระหว่าง routing กับ controller | ||
+ | ** มีอยู่แล้วคือ Auth | ||
+ | ** อาจมีอื่นๆ เช่น CORS middleware, Log middleware | ||
+ | * การสร้าง Middleware | ||
+ | ** <code>php artisan make:middleware CheckAge</code> | ||
+ | ** File จะอยู่ที่ <code> app/Http/Middleware</code> | ||
+ | ** ตัวอย่างนี้ เป็น middleware ที่เช็ค age | ||
+ | *** ถ้า age <= 200 จะไม่อนุญาตให้ไปต่อ โดยจะ redirect ไปที่ home | ||
+ | *** ถ้า age > 200 จะไปต่อ โดยเรียก $next($request) $next คือ function ที่มันควรจะเข้าจริงๆ | ||
+ | ** <syntaxhighlight lang="php"> | ||
+ | <?php | ||
+ | |||
+ | namespace App\Http\Middleware; | ||
+ | |||
+ | use Closure; | ||
+ | |||
+ | class CheckAge | ||
+ | { | ||
+ | /** | ||
+ | * Handle an incoming request. | ||
+ | * | ||
+ | * @param \Illuminate\Http\Request $request | ||
+ | * @param \Closure $next | ||
+ | * @return mixed | ||
+ | */ | ||
+ | public function handle($request, Closure $next) | ||
+ | { | ||
+ | if ($request->age <= 200) { | ||
+ | return redirect('home'); | ||
+ | } | ||
+ | |||
+ | return $next($request); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ** Middleware อาจจะทำก่อน หริือ หลัง request ก็ได้ | ||
+ | *** ทำก่อน (ตัวอย่างกรณีข้างบน) | ||
+ | *** ทำหลัง <syntaxhighlight lang="php"> | ||
+ | public function handle($request, Closure $next) | ||
+ | { | ||
+ | $response = $next($request); | ||
+ | |||
+ | // Perform action | ||
+ | |||
+ | return $response; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * หลังจากสร้างแล้ว จะต้องเอาไป register ไว้ที่ <code> app/Http/Kernel.php</code> | ||
+ | * การนำไปใช้ | ||
+ | ** จะเอาไปผูกกับ route <syntaxhighlight lang="php"> | ||
+ | Route::get('admin/profile', function () { | ||
+ | // | ||
+ | })->middleware('auth', 'second_middleware'); | ||
+ | </syntaxhighlight> | ||
+ | * Middleware group (Assign ทีเดียวหลายตัวเป็น group) | ||
+ | * Middleware parameters | ||
+ | ** ตัวอย่างการเช็ค role <syntaxhighlight lang="php"> | ||
+ | <?php | ||
+ | |||
+ | namespace App\Http\Middleware; | ||
+ | |||
+ | use Closure; | ||
+ | |||
+ | class CheckRole | ||
+ | { | ||
+ | /** | ||
+ | * Handle the incoming request. | ||
+ | * | ||
+ | * @param \Illuminate\Http\Request $request | ||
+ | * @param \Closure $next | ||
+ | * @param string $role | ||
+ | * @return mixed | ||
+ | */ | ||
+ | public function handle($request, Closure $next, $role) | ||
+ | { | ||
+ | if (! $request->user()->hasRole($role)) { | ||
+ | // Redirect... | ||
+ | } | ||
+ | |||
+ | return $next($request); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ** การนำไปใช้ กรณีนี้จะตรวจสอบว่า route นี้จะเข้าได้เฉพาะคนที่มี role เป็น editor เท่านั้น <syntaxhighlight lang="php"> | ||
+ | Route::put('post/{id}', function ($id) { | ||
+ | // | ||
+ | })->middleware('role:editor'); | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === OAUTH === | ||
+ | * [[https://laravel.com/docs/5.8/passport Laravel Passport (Library for OAUTH Server)]] | ||
+ | |||
+ | == More References == | ||
+ | * [[https://github.com/alexeymezenin/laravel-best-practices#follow-laravel-naming-conventions Laravel Best Practices]] |
Latest revision as of 00:13, 26 April 2019
Contents
Reference
- Laravel Documentation
https://laravel.com/docs/5.8
Installation
Requirements
- PHP >= 7.1.3
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
- Tokenizer PHP Extension
- XML PHP Extension
- Ctype PHP Extension
- JSON PHP Extension
- BCMath PHP Extension
Steps
- Install LAMP Stack (ie. XAMPP) อย่าลืมจำ mysql root password!!
- Install Composer
- https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos
- หมายเหตุ: เวลาเรียกใช้งาน composer อาจจะต้องใช้
./composer.phar
แทนที่composer
ถ้าทำการ Install composer เป็น local
- สร้าง Laravel Project โดยใช้ composer
composer create-project --prefer-dist laravel/laravel PROJECT_NAME
- Run Local Development Server
php artisan serve # for running on localhost:8000
- or
php artisan serve --port=8888 # for running on localhost:8888
Basic Tutorial
Important directory / file
app
Where the models areHttp
Controllers
Middleware
config
database
public
Where the public files (ie. css, js)resources
views
routes
api.php
web.php
vendor
Where the additional libraries store.env
all environment variables (ie. DB username/password)
Intro
Controller & Route
- Let's make a controller: page_controller
php artisan make:controller CONTROLLER_NAME
- จะมีไฟล์
app/Http/Controllers/page_controller.php
ปรากฎ
- ในไฟล์จะมี code เหล่านี้
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use Illuminate\Http\Request; 6 7 class page_controller extends Controller 8 { 9 // 10 }
- ลองสร้าง method index
1 public function index(){ 2 $menu = array("Home", "My course", "Setting"); 3 return $menu; 4 }
- เพิ่ม Route ใน
routes/web.php
1 Route::get('home','page_controller@index');
- ลองทดสอบเรียก url
http://localhost:8000/home
- ปกติแล้วสิ่งที่ controller คืนมักจะไม่ใช่ array หรือ object ธรรมดา ยกเว้นกรณีที่กำลังเขียน api .. หากเป็นกรณี webpage มักจะคืนเป็น view
- ลองสร้าง view (การสร้าง view จะไม่สามารถเรียกผ่าน artisan ได้ จะต้องสร้างด้วยตัวเอง ใน
resources/views
โดยให้สร้าง view ชื่อว่าhome.blade.php
- ใน controller method index จะเปลี่ยนแปลงการ return ให้ return view แทน
1 public function index(){ 2 $menu = array("Home", "My course", "Setting"); 3 return view("home")->with('menu',$menu); 4 }
- ใน
home.blade.php
ให้ลองเขียน html เพื่อแสดง menu เป็น unordered list1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 </head> 9 <body> 10 <ul> 11 @foreach($menu as $eachmenu) 12 <li>{{$eachmenu}}</li> 13 @endforeach 14 </ul> 15 </body> 16 </html>
View (Blade Template)
<meta name="csrf-token" content="Template:Csrf token()">
Template:Config('app.name', 'Laravel')
- @include('inc.messages')
- @yield('content')
- @extends('layouts.app')
- @section('content')
@endsection - @if
- @for
- @foreach
- @csrf
- {{}}
- {!! !!}
Database
Preparation
- สร้าง database (สมมติว่าชื่อ laravel_training)
- แก้ไข
.env
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel_training DB_USERNAME=USERNAME DB_PASSWORD=PASSWORD
- แก้ไข file
app/Providers/AppServiceProvider.php
ข้างบนสุดเพิ่มuse Illuminate\Support\Facades\Schema;
- แก้ไข file
app/Providers/AppServiceProvider.php
function boot เพิ่มSchema::defaultStringLength(191);
- ลอง migrate default script
php artisan migrate
Model & DB
- php artisan make:model MODEL_NAME --migration
- php artisan make:migration MIGRATION_NAME
- Sample
Eloquent
- php artisan tinker
- new item
$course = new course(); $course->number = "2110221"; $course->title = "Computer Engineering Essentials"; $course->year = 2018; $course->semester = 2; $course->save();
- duplicate item : $newcourse2 = $newcourse->replicate();
- fetch item
$allcourses = course::all(); $findcourse = course::find(1); $findcourse = course::where(number,"2110221")->get(); // ->where("title","like","%".$keyword."%") // ->orderBy('created_at','desc') // ->paginate(10); // In view show page numbers: {{$items->links()}}
- edit item
$course = course::find(1); $course->title = "NEW NAME"; $course->save();
- delete item
$course = course::find(1); $course->delete();
Handle CRUD
- php artisan make:controller CONTROLLER_NAME --resource
- method: get, post, patch, delete
- Route::resource('ROUTE', 'CONTROLLER');
- php artisan route:list
- Handle request
$name = $request->input('name', 'Default'); $name = $request->name;
- Validation --> See Doc
$request->validate([ 'title' => 'required|max:255', 'body' => 'required', ]); // Frontend display error @if ($errors->any()) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif
Relationship
one to one
- Defining relationship
- return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
- Defining inverse relationship
- return $this->belongsTo('App\User', 'foreign_key', 'other_key');
one to many
- Defining relationship
- return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
- Defining inverse relationship
- return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
many to many
- Defining relationship
- return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
- return $this->belongsToMany('Model','Intermediate table name','fk of current model','fk of another model')
- Defining inverse relationship
- return $this->belongsToMany('App\User', 'role_user', 'role_id', 'user_id');
- return $this->belongsToMany('Model','Intermediate table name','fk of current model','fk of another model')
Authentication
Preparation
- php artisan make:auth
User System
- $user = Auth::user();
- $id = Auth::id();
- Protect route
// Protect in route Route::get('index','homecontroller@index')->middleware('auth'); // Protect in constructor public __construct{ $this->middleware('auth', ['except' => ['getActivate', 'anotherMethod']]); }
- Check if the user is loggedin
if (Auth::check()) { // The user is logged in... }
Access Control
- Using Policy
- php artisan make:policy POLICY_NAME
- php artisan make:policy POLICY_NAME --model=MODEL // Create with basic CRUD
- Register Policy in
App/Providers/Authserviceprovider.php
protected $policies = [ Post::class => PostPolicy::class, ];
- Writing Policy
public function update(User $user, Post $post){ return $user->id === $post->user_id; } // Check Policy if ($user->can('update', $post)) { // }
- Writing Policy without model
public function create(User $user){ // } // Check Policy if ($user->can('create', Post::class)) { // ต้องบอก class เพื่อจะได้รู้ว่าจะไปเรียกจาก policy ของ class ไหน // }
- Writing Policy in View
@can('update', $post) <!-- The Current User Can Update The Post --> @elsecan('create', App\Post::class) <!-- The Current User Can Create New Post --> @endcan @if (Auth::user()->can('update', $post)) <!-- The Current User Can Update The Post --> @endif @unless (Auth::user()->can('update', $post)) <!-- The Current User Can't Update The Post --> @endunless
Testing
- Make test
// Create a test in the tests/Feature directory... php artisan make:test UserTest // Create a test in the tests/Unit directory... php artisan make:test UserTest --unit
- Run test : In your project root run
vendor/bin/phpunit
- The CSRF middleware is automatically disabled when running tests
- Use model factory to generate fake object
- Factory is at
database/factories/UserFactory.php
- In testing
$user = factory(User::class)->create(); $response = $this->actingAs($user)->withSession(['foo' => 'bar'])->get('/');
- Factory is at
- Browser Test [https://laravel.com/docs/5.8/dusk]
- And more ... in Laravel Doc
More and More
Middleware
- อยู่ระหว่าง routing กับ controller
- มีอยู่แล้วคือ Auth
- อาจมีอื่นๆ เช่น CORS middleware, Log middleware
- การสร้าง Middleware
php artisan make:middleware CheckAge
- File จะอยู่ที่
app/Http/Middleware
- ตัวอย่างนี้ เป็น middleware ที่เช็ค age
- ถ้า age <= 200 จะไม่อนุญาตให้ไปต่อ โดยจะ redirect ไปที่ home
- ถ้า age > 200 จะไปต่อ โดยเรียก $next($request) $next คือ function ที่มันควรจะเข้าจริงๆ
<?php namespace App\Http\Middleware; use Closure; class CheckAge { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if ($request->age <= 200) { return redirect('home'); } return $next($request); } }
- Middleware อาจจะทำก่อน หริือ หลัง request ก็ได้
- ทำก่อน (ตัวอย่างกรณีข้างบน)
- ทำหลัง
public function handle($request, Closure $next) { $response = $next($request); // Perform action return $response; }
- หลังจากสร้างแล้ว จะต้องเอาไป register ไว้ที่
app/Http/Kernel.php
- การนำไปใช้
- จะเอาไปผูกกับ route
Route::get('admin/profile', function () { // })->middleware('auth', 'second_middleware');
- จะเอาไปผูกกับ route
- Middleware group (Assign ทีเดียวหลายตัวเป็น group)
- Middleware parameters
- ตัวอย่างการเช็ค role
<?php namespace App\Http\Middleware; use Closure; class CheckRole { /** * Handle the incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed */ public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { // Redirect... } return $next($request); } }
- การนำไปใช้ กรณีนี้จะตรวจสอบว่า route นี้จะเข้าได้เฉพาะคนที่มี role เป็น editor เท่านั้น
Route::put('post/{id}', function ($id) { // })->middleware('role:editor');
- ตัวอย่างการเช็ค role