Laravel

เรื่องราว ต่าง ๆ กับผู้เริ่มต้นใช้งาน Laravel

Laravel Upload File to local storage and saved to database with Postman upload client UI EP8

File Storage เป็นเรื่องนึงที่ Laravel จัดการมาให้เรียบร้อยพร้อมใช้งาน ประหยัดเวลานักพัฒนาไปได้มากเลยทีเดียว ไม่ว่าจะเป็นการดึงไฟล์ (Retrieving Files) ,อัพโหลดไฟล์ (Storing Files) ,ดาวห์โหลดไฟล์ (Downloading Files) ,ลบไฟล์ (Delete Files) เหล่านี้เป็นเรื่องง่ายมาก ๆ ถ้าแอพของเราพัฒนามาด้วย Laravel

บทความนี้จะพาทุกคนมาลองเขียนโค๊ดง่าย ๆ ทดสอบการอัพโหลดไฟล์ด้วยเครื่องมือ Postman client ที่รองรับการอัพโหลดไฟล์ (แทนการเขียนหน้าฟอร์มอัพโหลดเอง) ทำการอัพโหลดไฟล์รูปภาพไปที่ Laravel Project ที่เขียนโค๊ดการอัพโหลดไฟล์ไว้ ทำหน้าที่รับ request ที่ส่ง binary data ระบบจะทำการ Save File ลง local drive และ Save Data ลง Database

เริ่มโค๊ดเลยดีกว่า

สร้าง Table photos เพื่อเอาไว้เก็บข้อมูล photos ที่อัพโหลดเข้าระบบ

CREATE TABLE `photos` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `photo_origin_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `photo_new_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `photo_temp_path` varchar(255) NOT NULL,
  `photo_extension` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `photo_status` enum('active','inactive') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `photo_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

สร้าง Class Model Photo (Photo.php)

php artisan make:Model Photo

แก้ไขไฟล์ app\Models\Photo.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Photo extends Model
{
  use HasFactory;
  protected $table = 'photos';
  protected $fillable = [
    'id', 'photo_origin_name', 'photo_new_name',
    'photo_extension', 'photo_status', 'photo_date', 'photo_temp_path'
  ];
  public $timestamps = false;
}

สร้าง Class Controller UploadFileController (UploadFileController.php)

<?php

namespace App\Http\Controllers;

use App\Models\Photo;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadFileController extends Controller
{

  public function simpleUplaod(Request $request)
  {
    try {
      if ($request->file('photo')->isValid()) {
        $path = $request->photo->path();
        $extension = $request->photo->extension();
        $clientOriginalName = $request->photo->getClientOriginalName();
        $newFileName = time() . $clientOriginalName;
        $uploadedFile = $request->file('photo');

        // Save File to local drive
        Storage::putFileAs('photos', $uploadedFile, $newFileName);

        //Save File to Photo table
        $photo = new Photo();
        $photo->photo_temp_path = $path;
        $photo->photo_origin_name = $clientOriginalName;
        $photo->photo_new_name = $newFileName;
        $photo->photo_extension = $extension;
        $photo->photo_status = 'ACTIVE';
        $photo->photo_date = Carbon::now();
        $photo->save();

        return [
          'path' => $path,
          'extension' => $extension,
          'clientOriginalName' => $clientOriginalName,
          'newFileName' => $newFileName
        ];
      }
    } catch (\Throwable $th) {
      return $th->getMessage();
    }
  }
}

แก้ไขไฟล์ routes.php เพิ่ม /upload

Route::post('upload', 'App\Http\Controllers\[email protected]');

เนื่องด้วยการ request ส่ง data ไปที่ laravel โดยต้องมีการส่งจำเป็นต้องส่ง @csrf ไม่เช่นนั้น ระบบจะ response (419 PAGE EXPIRED) ให้เพิ่ม ignore path “/upload” App\Http\Middleware\VerifyCsrfToken

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
  /**
   * The URIs that should be excluded from CSRF verification.
   *
   * @var array
   */
  protected $addHttpCookie = true;

  protected $except = [
    'upload'
  ];
}

start server

php artisan serve

เปิดโปรแกรม Postman ขึ้นมา กำหนดตามนี้ จากนั้นกด Send

POST : http://127.0.0.1:8000/upload 
Body : form-data
        KEY : photo (File)
        VALUE : Chose File

ตรวจสอบข้อมูลที่ Table photos

ตรวจสอบไฟล์ที่ storage/app/photos/1608200088upload-1.PNG

สรุปท้ายบทความ

จากที่ได้ทดสอบการสร้างระบบ upload ไม่ได้ยุ่งยากอย่างที่คิด ตัวอย่างโค๊ดเป็นเพียงการ Push File ลง local Storage ความสามารถของ Laravel กับเรื่อง File System สามารถศึกษาเพิ่มเติมได้ที่ลิ้งนี้ https://laravel.com/docs/7.x/filesystem หากผู้อ่านท่านใดลองทำตามแล้วไม่ได้ หรือ ติดปัญหาสามารถ ทักมาสอบถามกันได้ที่ https://www.facebook.com/poolsawat.apin/ สำหรับบทความนี้ขอจบเนื้อหาเพียงเท่านี้ครับ

Laravel ทำ JWT Authentication มีโค๊ดตัวอย่าง (Laravel 7.x) EP7

JWT คืออะไร บทความนี้ไม่ขอกล่าว แต่แนะนำบทความนี้ อธิบายได้ละเอียดเลย “JSON Web Token มาตรฐานใหม่ ในการทำ Authentication” บทความที่ผมจะเขียนนี้จะพาทำ workshop ง่ายๆ แทบเหมือนว่า copy code จาก web “JSON Web Token Authentication for Laravel & Lumen” นี้มาเลยก็ว่าได้

ติดตั้ง Dependencies และ Config Project

  1. ต้องทำการติดตั้ง dependency ที่ชื่อว่า tymon/jwt-auth ด้วยคำสั่ง
$composer require tymon/jwt-auth

Using version ^1.0 for tymon/jwt-auth
./composer.json has been updated
Running composer update tymon/jwt-auth
Loading composer repositories with package information
Updating dependencies
...
[32mPackage manifest generated successfully.[39m
2 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

2. รันคำสั่ง Publish the config เพื่อสร้างไฟล์ jwt.php config ไฟล์ ที่ config/jwt.php ให้อัตโนมัติเองหลังจากรันคำสั่ง

$php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Copied File [\vendor\tymon\jwt-auth\config\config.php] To [\config\jwt.php]
Publishing complete.

3. รันคำสั่ง Generate secret key เพื่อ generate jwt key ในไฟล์ .env JWT_SECRET=? เพื่อเป็น key ตั้งต้นให้การ Generate Token ทำ authenticate token หลังรันคำสั่งตรวจสอบ secret key ที่ .env

$php artisan jwt:secret

jwt-auth secret [Wwt4F********************************************UNvQ14f] set successfully.

4. แก้ไข class User.php เพิ่ม implement JWTSubject

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

5. แก้ไข gaurds driver ของ api จากเดิม passport ไปเป็น jwt
– driver => jwt (jwt auth)
– provider => users (model auth user)

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

...
 'providers' => [
    'users' => [
      'driver' => 'eloquent',
      'model' => App\Models\User::class,
    ],

6. create table users

CREATE TABLE `users` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email_verified_at` timestamp NULL DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

7. เพิ่ม basic authenticate route ที่ไฟล์ routes/api.php
– ‘middleware’ => ‘api’ (ประตูดัก request , response ของ route ที่มี pattern: /api/*)
– ‘prefix’ => ‘auth’ (prefix url ex: /api/auth/login )

Route::group([

    'middleware' => 'api',
    'prefix' => 'auth'

], function ($router) {


    Route::post('register', 'App\Http\Controllers\[email protected]r');
    Route::post('login', 'App\Http\Controllers\[email protected]');
    Route::post('logout', 'App\Http\Controllers\[email protected]');
    Route::post('refresh', 'App\Http\Controllers\[email protected]');
    Route::post('me', 'App\Http\Controllers\[email protected]');

});

8. สร้าง AuthController สำหรับใส่ Auth Logic (login , logout , register , info)

$php artisan make:controller AuthController
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
  /**
   * Create a new AuthController instance.
   *
   * @return void
   */
  public function __construct()
  {
    $this->middleware('auth:api', ['except' => ['login', 'register']]);
  }

  public function register(Request $request)
  {
    $user = User::create([
      'name' => $request->name,
      'email' => $request->email,
      'password' => bcrypt($request->password),
    ]);

    $token = auth()->login($user);

    return $this->respondWithToken($token);
  }

  /**
   * Get a JWT via given credentials.
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function login()
  {
    $credentials = request(['email', 'password']);
    //return response()->json($credentials);
    if (!$token = auth()->attempt($credentials)) {
      return response()->json(['error' => 'Unauthorized'], 401);
    }

    return $this->respondWithToken($token);
  }

  /**
   * Get the authenticated User.
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function me()
  {
    return response()->json(auth()->user());
  }

  /**
   * Log the user out (Invalidate the token).
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function logout()
  {
    auth()->logout();

    return response()->json(['message' => 'Successfully logged out']);
  }

  /**
   * Refresh a token.
   *
   * @return \Illuminate\Http\JsonResponse
   */
  public function refresh()
  {
    return $this->respondWithToken(auth()->refresh());
  }

  /**
   * Get the token array structure.
   *
   * @param  string $token
   *
   * @return \Illuminate\Http\JsonResponse
   */
  protected function respondWithToken($token)
  {
    return response()->json([
      'access_token' => $token,
      'token_type' => 'bearer',
      'expires_in' => auth()->factory()->getTTL() * 60
    ]);
  }
}

9. ทดสอบระบบ
– [POST] http://127.0.0.1:8000/api/auth/register (ระบบจะสร้าง user ใน table users )
– [POST] http://127.0.0.1:8000/api/auth/login (ระบบเช็ค user ใน table users)
– [POST] http://127.0.0.1:8000/api/auth/me (ระบบแสดง user จาก bearer token )
– [POST] http://127.0.0.1:8000/api/auth/logout (ระบบ expired token ออกจากระบบ)

http://127.0.0.1:8000/api/auth/register
http://127.0.0.1:8000/api/auth/login
http://127.0.0.1:8000/api/auth/me (Bearer Token ด้วย)
http://127.0.0.1:8000/api/auth/logout (Bearer Token ด้วย)

สรุปท้ายบทความ

JWT เป็นเพียงการ Encode Data ที่จะกอบไปด้วย 3 ส่วนหลัก ๆ คือ Headers (เข้ารหัสแบบไหนอยู่ (เช่น SHA256, RSA)) ,Payload เป็นข้อมูลจริง ,Signature เอาไว้เช็คกับ Payload เป็นเพียงมาตรฐานเปิด (RFC 7519) ที่ช่วยเข้ามาแก้ไขโดยมีข้อดีหลัก ๆ 2 ข้อ คือขนาดไม่ใหญ่มาก ,ลักษณะการเก็บข้อมูลในตัวเอง

หากผู้อ่านท่านใดสงสัยหรืออยากที่จะแนะนำบทความ สามารถคอมเม้นทิ้งท้ายไว้ได้เลยครับ ^^

Laravel ใช้งาน views blade template engine EP3

การพัฒนาเว็บไซต์ด้วยชุด framework ต่าง ๆ จะให้ได้รับความนิยมของ framework นั้น ๆ ต้องมีความพร้อมในเรื่องการจัดการ view ก็คือเครื่องมือการพัฒนาในส่วนของงานการแสดงผลของ data ต่าง ๆ การที่ laravel มาพร้อมกับ blade template engine นี้ถือว่าครบเพียบพร้อม เหมาะแก่การใช้งานเป็นอย่างมาก บทความนี้จะพาไปรู้จักการใช้งาน blade template engine นี้กันว่ามีการทำงานร่วมกับ laravel อย่างไรได้บ้าง

การใช้งาน Blade เพื่อ displaying data จาก controller

การที่จะนำค่าที่ผ่านกระบวนการทำงานจาก controller ไปที่ views (*.blade.php) โดยไฟล์ *.blade.php ทั้งหมดจะถูกจัดเก็บไว้ที่ resources/views/*

  • สร้างไฟล์ {viewname}.blade.php ตัวอย่าง views/example/datatype.blade.php
<html>

<body>
  <h1>DataType</h1>
  <h3>String</h3>
  <p>{{$string}}</p>
  <hr />
  <h3>Boolean</h3>
  <p>{{$boolean}}</p>
  <hr />
  <h3>Integer</h3>
  <p>{{$integer}}</p>
  <hr />
  <h3>Object</h3>
  <p>{{$object['content']}}</p>
  <hr />
  <h3>Array</h3>
  <p>
    <ol>
      @foreach($array as $index=> $item)
      <li>{{$item['content']}}</li>
      @endforeach
    </ol>
  </p>
  <hr />
</body>

</html>
  • แก้ไขไฟล์ routes/web.php กำหนด view(‘datatype’,[?]) กำหนด parameter data ที่จะส่งไปที่ view ใน arg ที่ 2 [?]
Route::get('/datatype', function () {
  return view('/example/datatype', [
    'string' => 'Laravel ใช้งาน view (blade template), json ,pdf',
    'boolean' => true,
    'integer' => 9999,
    'object' => [
      'content' => 'Laravel ใช้งาน view (blade template), json ,pdf EP3',
      'author' => 'poolsawat'
    ],
    'array' => [
      ['content' => 'Laravel เริ่มติดตั้งและสร้าง route ง่าย ๆ EP1'],
      ['content' => 'Laravel แนะนำโครงสร้างภายในโปรเจค อธิบายแต่ละส่วนการทำงาน EP2'],
      ['content' => 'Laravel ใช้งาน view (blade template), json ,pdf EP3'],
    ]
  ]);
});

การใช้งาน blade ในกรณีต้องการใช้งาน control structures (IF ,ELSE ,SWITCH ,LOOP (for ,foreach ,while) ต่าง ๆ เหล่านี้ ทำอย่างไร

ในบางครั้ง บางกรณีที่ต้องการตะเช็คเงื่อนไขในการที่จะแสดงผลของ data หรืออยากที่จะแสดงผลค่าที่เป็นในลักษณะชุดข้อมูล (array) ทาง blade ก็มีคำสั่งมาให้พร้อมแล้ว โดยการที่จะเรียกคำสั่งนั้น จะมี tag @ ตามด้วย name เช่น @if ,@for เป็นต้น

  • สร้างไฟล์ views/example/control.blade.php
<!DOCTYPE html>
<html lang="en">

<body>
  <h1>Control Structures</h1>
  <p>IF ,ELSEIF ,ELSE ,LOOP (FOR,FOREACH,WHILE) ,SWITCH</p>
</body>
<h1>IF</h1>
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
<hr />
<h1>SWITCH</h1>
@switch($case)
@case(1)
First case...
@break

@case(2)
Second case...
@break

@default
Default case...
@endswitch
<hr />
<h1>LOOP</h1>
<h3>FOR</h3>
@for ($i = 0; $i
< 4; $i++) <br />The current value is {{ $i }} @endfor
<h3>FOREACH</h3>
@foreach ($records as $rec)
<p>This is record {{ $rec }}</p>
@endforeach
<hr />
<h3>FORELSE</h3>
@forelse ($records as $rec)
<li>{{ $rec }}</li>
@empty
<p>No users</p>
@endforelse
<hr />
<h3>WHILE</h3>
@php
$i = 0
@endphp
@while ($i < 4) <p>I'm looping forever.</p>
  @php
  $i++
  @endphp
  @endwhile

</html>
  • แก้ไขไฟล์ routes/web.php โดยเพิ่ม route “control-structures” เข้าไป
Route::get('/control-structures', function () {
  return view('example/control', [
    'records' => [1, 2, 3, 4],
    'case' => 2
  ]);
});

การใช้งาน blade กับการทำ component แยกส่วน

framework ที่ได้รับความนิยมในปัจจุบัน React , Angular ,Vue มีจุดเด่นเรื่องการทำ Component management โดยทั้งสิ้น Laravel เองก็มี concept นี้ด้วยเช่นกัน (ทีมเดียวกับที่พัฒนา Vue) ถือเป็นข้อดีอีกอย่างที่อยากแนะนำให้ใช้งาน laravel

  • สร้างไฟล์ views/example/component.blade.php , views/components/alert.blade.php ,views/components/message.blade.php ,views/components/message-slot.blade.php ตาลำดับ
<!-- views/example/component.blade.php -->
<!DOCTYPE html>
<html lang="en">

<body>
  <h1>Component</h1>
  <h3>empty data</h3>
  @component('components/message',[])
  @endcomponent
  <hr />
  <h3>has data</h3>
  @component('components/message',['name' => 'poolsawat.com'])
  @endcomponent
  <hr />
  <h3>append slot</h3>
  @component('components/message-slot',[])
  <div>element slot 1</div>
  <div>element slot 2</div>
  @endcomponent
  <hr />
  <x-alert />
</body>

</html>
<!-- views/components/alert.blade.php -->
<h1>x-{component}</h1>
<!-- views/components/message.blade.php -->
<h2>
  @if (empty($name))
  component message ...
  @else
  component message {{$name}}
  @endif
</h2>
<!-- views/components/message-slot.blade.php -->
<h2>
  component message append slot
  {{ $slot }}
</h2>
  • แก้ไขไฟล์ routes/web.php เพิ่ม route “components”
Route::get('/components', function () {
  return view('example/component', []);
});
  • ดูผลลัพธ์ที่
  • components ของ laravel มีรูปแบบการเรียกได้ 2 วิธี
    1. @component(‘components/alert’,[]) @endcomponent สามารถเรียกได้แบบนี้จากหน้า views
    2. <x-alert /> สามารถเรียกได้แต่ต้องมีไฟล์ app/View/Components/Alert.php โดย genarate Component ได้ด้วยคำสั่ง artisan package นี้ php artisan make:component Alert
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.alert');
    }
}

เหล่านี้คือการเรียกใช้งาน blade template engine ง่าย ๆ ยังมีความสามารถอีกเยอะมาก ศึกษาเพิ่มเติ่มที่นี่