Laravel Series เดินทางมาถึง EP6 ซึ่งถ้าใครยังไม่ได้อ่านบทความ EP 1 – 5 สามารถเข้าได้ที่ลิ้งใต้ล่างนี้
- Laravel เริ่มติดตั้งและสร้าง route ง่าย ๆ EP1
- Laravel แนะนำโครงสร้างภายในโปรเจค อธิบายแต่ละส่วนการทำงาน EP2
- Laravel ใช้งาน views blade template engine EP3
- Laravel รู้จัก Routes และ การรับค่าจาก HttpRequest EP4
- Laravel สร้าง RESTful API ง่าย ๆ ด้วย Resource Routes Controller EP5
บทความนี้จะมาทำให้ application ของเราต่อกับฐานข้อมูล (database) ของ MySQL และเมื่อต่อได้แล้วจะมาใช้งาน migrate และมาทำระบบ CRUD ของข้อมูล Photos กัน
สร้าง Schema และ Migrate Table ด้วย artisan migrate
- สร้าง schema ด้วยคำสั่ง
CREATE SCHEMA `db_poolsawat` DEFAULT CHARACTER SET utf8 ;2. แก้ไขไฟล์ .env
...
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db_poolsawat
DB_USERNAME=root
DB_PASSWORD=p@ssw0rd
...
3. ทดสอบการต่อ connection ทำตามขั้นตอนข้างล่าง ถ้าขึ้นแบบนี้แสดงว่าต่อสำเร็จ
$ php artisan tink
// Psy Shell v0.10.4 (PHP 7.3.7 — cli) by Justin Hileman
$ DB::connection()->getPdo();
/* 
PDO {#4182
     inTransaction: false,
     attributes: {
       CASE: NATURAL,
       ERRMODE: EXCEPTION,
       AUTOCOMMIT: 1,
       PERSISTENT: false,
       DRIVER_NAME: "mysql",
       SERVER_INFO: "Uptime: 433  Threads: 4  Questions: 152  Slow queries: 0  Opens: 209  Flush tables: 3  Open tables: 130  Queries per second avg: 0.351",
       ORACLE_NULLS: NATURAL,
       CLIENT_VERSION: "mysqlnd 5.0.12-dev - 20150407 - $Id: 7cc7cc96e675f6d72e5cf0f267f48e167c2abb23 $",
       SERVER_VERSION: "8.0.21",
       STATEMENT_CLASS: [
         "PDOStatement",
       ],
       EMULATE_PREPARES: 0,
       CONNECTION_STATUS: "127.0.0.1 via TCP/IP",
       DEFAULT_FETCH_MODE: BOTH,
     },
   }
*/4. generate migrations file ด้วยคำสั่งเหล่านี้
php artisan make:migration create_photos_table4.1 ตรวจสอบไฟล์ที่ถูกสร้างขึ้นมาที่ database/migrations/*_create_photos_table.php (* คือวันที่ generate file) จากนั้นเปิดไฟล์ขึ้นมาแก้ไข ตามนี้
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePhotosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('photos', function (Blueprint $table) {
            $table->id();
            $table->string('photo_name');
            $table->integer('photo_size');
            $table->string('photo_url');
            $table->enum('photo_status', ['active', 'inactive']);
            $table->dateTime('photo_date', 0);
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('photos');
    }
}
ในการกำหนด datatype ของ column ตารางมีรูปแบบที่หลากหลาย มี function รองรับอยู่แล้ว สำหรับการกำหนดสร้าง สามารถดูเพิ่มเติมได้จากตารางนี้
Available Column Types
The schema builder contains a variety of column types that you may specify when building your tables:
| Command | Description | 
|---|---|
| $table->id(); | Alias of $table->bigIncrements('id'). | 
| $table->foreignId('user_id'); | Alias of $table->unsignedBigInteger('user_id'). | 
| $table->bigIncrements('id'); | Auto-incrementing UNSIGNED BIGINT (primary key) equivalent column. | 
| $table->bigInteger('votes'); | BIGINT equivalent column. | 
| $table->binary('data'); | BLOB equivalent column. | 
| $table->boolean('confirmed'); | BOOLEAN equivalent column. | 
| $table->char('name', 100); | CHAR equivalent column with a length. | 
| $table->date('created_at'); | DATE equivalent column. | 
| $table->dateTime('created_at', 0); | DATETIME equivalent column with precision (total digits). | 
| $table->dateTimeTz('created_at', 0); | DATETIME (with timezone) equivalent column with precision (total digits). | 
| $table->decimal('amount', 8, 2); | DECIMAL equivalent column with precision (total digits) and scale (decimal digits). | 
| $table->double('amount', 8, 2); | DOUBLE equivalent column with precision (total digits) and scale (decimal digits). | 
| $table->enum('level', ['easy', 'hard']); | ENUM equivalent column. | 
| $table->float('amount', 8, 2); | FLOAT equivalent column with a precision (total digits) and scale (decimal digits). | 
| $table->geometry('positions'); | GEOMETRY equivalent column. | 
| $table->geometryCollection('positions'); | GEOMETRYCOLLECTION equivalent column. | 
| $table->increments('id'); | Auto-incrementing UNSIGNED INTEGER (primary key) equivalent column. | 
| $table->integer('votes'); | INTEGER equivalent column. | 
| $table->ipAddress('visitor'); | IP address equivalent column. | 
| $table->json('options'); | JSON equivalent column. | 
| $table->jsonb('options'); | JSONB equivalent column. | 
| $table->lineString('positions'); | LINESTRING equivalent column. | 
| $table->longText('description'); | LONGTEXT equivalent column. | 
| $table->macAddress('device'); | MAC address equivalent column. | 
| $table->mediumIncrements('id'); | Auto-incrementing UNSIGNED MEDIUMINT (primary key) equivalent column. | 
| $table->mediumInteger('votes'); | MEDIUMINT equivalent column. | 
| $table->mediumText('description'); | MEDIUMTEXT equivalent column. | 
| $table->morphs('taggable'); | Adds taggable_idUNSIGNED BIGINT andtaggable_typeVARCHAR equivalent columns. | 
| $table->uuidMorphs('taggable'); | Adds taggable_idCHAR(36) andtaggable_typeVARCHAR(255) UUID equivalent columns. | 
| $table->multiLineString('positions'); | MULTILINESTRING equivalent column. | 
| $table->multiPoint('positions'); | MULTIPOINT equivalent column. | 
| $table->multiPolygon('positions'); | MULTIPOLYGON equivalent column. | 
| $table->nullableMorphs('taggable'); | Adds nullable versions of morphs()columns. | 
| $table->nullableUuidMorphs('taggable'); | Adds nullable versions of uuidMorphs()columns. | 
| $table->nullableTimestamps(0); | Alias of timestamps()method. | 
| $table->point('position'); | POINT equivalent column. | 
| $table->polygon('positions'); | POLYGON equivalent column. | 
| $table->rememberToken(); | Adds a nullable remember_tokenVARCHAR(100) equivalent column. | 
| $table->set('flavors', ['strawberry', 'vanilla']); | SET equivalent column. | 
| $table->smallIncrements('id'); | Auto-incrementing UNSIGNED SMALLINT (primary key) equivalent column. | 
| $table->smallInteger('votes'); | SMALLINT equivalent column. | 
| $table->softDeletes('deleted_at', 0); | Adds a nullable deleted_atTIMESTAMP equivalent column for soft deletes with precision (total digits). | 
| $table->softDeletesTz('deleted_at', 0); | Adds a nullable deleted_atTIMESTAMP (with timezone) equivalent column for soft deletes with precision (total digits). | 
| $table->string('name', 100); | VARCHAR equivalent column with a length. | 
| $table->text('description'); | TEXT equivalent column. | 
| $table->time('sunrise', 0); | TIME equivalent column with precision (total digits). | 
| $table->timeTz('sunrise', 0); | TIME (with timezone) equivalent column with precision (total digits). | 
| $table->timestamp('added_on', 0); | TIMESTAMP equivalent column with precision (total digits). | 
| $table->timestampTz('added_on', 0); | TIMESTAMP (with timezone) equivalent column with precision (total digits). | 
| $table->timestamps(0); | Adds nullable created_atandupdated_atTIMESTAMP equivalent columns with precision (total digits). | 
| $table->timestampsTz(0); | Adds nullable created_atandupdated_atTIMESTAMP (with timezone) equivalent columns with precision (total digits). | 
| $table->tinyIncrements('id'); | Auto-incrementing UNSIGNED TINYINT (primary key) equivalent column. | 
| $table->tinyInteger('votes'); | TINYINT equivalent column. | 
| $table->unsignedBigInteger('votes'); | UNSIGNED BIGINT equivalent column. | 
| $table->unsignedDecimal('amount', 8, 2); | UNSIGNED DECIMAL equivalent column with a precision (total digits) and scale (decimal digits). | 
| $table->unsignedInteger('votes'); | UNSIGNED INTEGER equivalent column. | 
| $table->unsignedMediumInteger('votes'); | UNSIGNED MEDIUMINT equivalent column. | 
| $table->unsignedSmallInteger('votes'); | UNSIGNED SMALLINT equivalent column. | 
| $table->unsignedTinyInteger('votes'); | UNSIGNED TINYINT equivalent column. | 
| $table->uuid('id'); | UUID equivalent column. | 
| $table->year('birth_year'); | YEAR equivalent column. | 
4.2 หลังจากที่แก้ไขไฟล์ *_create_photos_table.php เป็นที่เรียบร้อยแล้ว ให้ รันคำสั่งต่อไปนี้เพิ่มเพิ่ม migrate table (artisan จะทำการไป create table ให้ที่ database )
php artisan migrateเปรียบเหมือนว่าเรากำลังใช้คำสั่งชุดนี้เพื่อสร้างตาราง
CREATE TABLE `photos` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `photo_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `photo_size` int NOT NULL,
  `photo_url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `photo_status` enum('active','inactive') COLLATE utf8mb4_unicode_ci NOT NULL,
  `photo_date` datetime NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci5. สร้าง Model class ด้วย artisan cli
php artisan make:model Photoตรวจสอบไฟล์ที่ app/Models/Photo.php
5.1 แก้ไขไฟล์ 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 $fillable = [
    'created_at', 'id', 'photo_date', 'photo_name', 'photo_size',
    'photo_status', 'photo_url', 'updated_at'
  ];
}
6. สร้าง Controller class ด้วย artisan cli
php artisan make:controller PhotoController --resourceตรวจสอบไฟล์ที่ app/Http/Controller/PhotoController.php
6.1 แก้ไขไฟล์ app/Http/Controller/PhotoController.php ตามข้างล่างนี้ ส่วนนี้จะได้มี function RESTful API template ที่ได้จากการ artisan cli แล้ว บทความ “Laravel สร้าง RESTful API ง่าย ๆ ด้วย Resource Routes Controller EP5” ได้อธิบายไว้บ้างแล้ว บทความนี้จะขอข้ามส่วนนี้ไป
<?php
namespace App\Http\Controllers;
use App\Models\Models\Photo as ModelsPhoto;
use App\Models\Photo;
use Carbon\Carbon;
use Illuminate\Http\Request;
class PhotoController extends Controller
{
  /**
   * Display a listing of the resource.
   *
   * @return \Illuminate\Http\Response
   */
  public function index()
  {
    return response()->json(['name' => 'index', 'photos' => Photo::all()]);
  }
  /**
   * Show the form for creating a new resource.
   *
   * @return \Illuminate\Http\Response
   */
  public function create()
  {
    $photo = new Photo();
    $photo->photo_name = 'laravel';
    $photo->photo_size = 1024;
    $photo->photo_url = 'https://laravel.com/img/logotype.min.svg';
    $photo->photo_status = 'active';
    $photo->photo_date = Carbon::now();
    return response()->json(['name' => 'create', 'status' => $photo->save()]);
  }
  /**
   * Store a newly created resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return \Illuminate\Http\Response
   */
  public function store(Request $request)
  {
    $photo = new Photo($request->all());
    $photo->photo_date = Carbon::now();
    return response()->json(['name' => 'store', 'payload' => $request->all(), 'status' => $photo->save()]);
  }
  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function show($id)
  {
    $photo = Photo::find($id);
    return response()->json(['name' => 'show', 'id' => $id, 'photo' => $photo]);
  }
  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function edit($id)
  {
    $photo = Photo::find($id);
    return response()->json(['name' => 'edit', 'id' => $id, 'photo' => $photo]);
  }
  /**
   * Update the specified resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function update(Request $request, $id)
  {
    $photo = Photo::find($id);
    $photo->photo_name = $request->photo_name;
    $photo->photo_size = $request->photo_size;
    $photo->photo_url = $request->photo_url;
    $photo->photo_status = $request->photo_status;
    $photo->photo_date = Carbon::now();
    return response()->json(['name' => 'update', 'status' => $photo->save(),  'payload' => $request->all(), 'id' => $id]);
  }
  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function destroy($id)
  {
    $photo = Photo::find($id);
    return response()->json(['name' => 'destroy', 'status' => $photo->delete(), 'id' => $id]);
  }
}
7. แก้ไขไฟล์ routes/api.php เพิ่มโค๊ด 1 บรรทัด
...
Route::resource('photos', 'App\Http\Controllers\PhotoController');
...8. ทดสอบ RESTful API
ก่อนที่จะทดสอบติดตั้ง npm i -g json เพื่อ pritty json response เพื่อความสวยงาม
$ curl localhost:8000/api/photos | json
 % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    28    0    28    0     0     65      0 --:--:-- --:--:-- --:--:--    65
{
  "name": "index",
  "photos": []
}
/* fetch photos all */$ curl localhost:8000/api/photos/create | json
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    31    0    31    0     0     73      0 --:--:-- --:--:-- --:--:--    73
{
  "name": "create",
  "status": true
}
/* create photo */$ curl -X POST -H "Content-Type: application/json" -d "{\"photo_name\":\"laravel\",\"photo_size\":1024,\"photo_url\":\"https://laravel.com/img/logotype.min.svg\",\"photo_status\":\"active\",\"photo_date\":\"2020-10-06 10:24:01\"}" http://127.0.0.1:8000/api/photos | json
 % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   357    0   201  100   156   1116    866 --:--:-- --:--:-- --:--:--  1994
{
  "name": "store",
  "payload": {
    "photo_name": "laravel",
    "photo_size": 1024,
    "photo_url": "https://laravel.com/img/logotype.min.svg",
    "photo_status": "active",
    "photo_date": "2020-10-06 10:24:01"
  },
  "status": true
}
/* create photo with payload */$ curl localhost:8000/api/photos/1 | json
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   286    0   286    0     0    722      0 --:--:-- --:--:-- --:--:--   722
{
  "name": "show",
  "id": "1",
  "photo": {
    "id": 1,
    "photo_name": "laravel",
    "photo_size": 1024,
    "photo_url": "https://laravel.com/img/logotype.min.svg",
    "photo_status": "active",
    "photo_date": "2020-10-06 10:32:24",
    "created_at": "2020-10-06T10:32:24.000000Z",
    "updated_at": "2020-10-06T10:32:24.000000Z"
  }
}
/* fetch photo by id */$ curl localhost:8000/api/photos/1/edit | json
 % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   286    0   286    0     0    709      0 --:--:-- --:--:-- --:--:--   709
{
  "name": "edit",
  "id": "1",
  "photo": {
    "id": 1,
    "photo_name": "laravel",
    "photo_size": 1024,
    "photo_url": "https://laravel.com/img/logotype.min.svg",
    "photo_status": "active",
    "photo_date": "2020-10-06 10:32:24",
    "created_at": "2020-10-06T10:32:24.000000Z",
    "updated_at": "2020-10-06T10:32:24.000000Z"
  }
}
/* fetch photo by id for edit */$ curl -X PATCH -H "Content-Type: application/json" -d "{\"id\":1,\"photo_name\":\"laravel patch\",\"photo_size\":2048,\"photo_url\":\"https://laravel.com/img/logotype.min.svg\",\"photo_status\":\"active\",\"photo_date\":\"2020-10-06 10:24:01\"}" http://127.0.0.1:8000/api/photos/1 | json
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   393    0   224  100   169   1108    836 --:--:-- --:--:-- --:--:--  1945
{
  "name": "update",
  "status": true,
  "payload": {
    "id": 1,
    "photo_name": "laravel patch",
    "photo_size": 2048,
    "photo_url": "https://laravel.com/img/logotype.min.svg",
    "photo_status": "active",
    "photo_date": "2020-10-06 10:24:01"
  },
  "id": "1"
}
/* patch photo with payload */
$ curl localhost:8000/api/photos/1 | json
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   292    0   292    0     0    707      0 --:--:-- --:--:-- --:--:--   705
{
  "name": "show",
  "id": "1",
  "photo": {
    "id": 1,
    "photo_name": "laravel patch",
    "photo_size": 2048,
    "photo_url": "https://laravel.com/img/logotype.min.svg",
    "photo_status": "active",
    "photo_date": "2020-10-06 10:36:22",
    "created_at": "2020-10-06T10:32:24.000000Z",
    "updated_at": "2020-10-06T10:36:22.000000Z"
  }
}
curl -X DELETE http://127.0.0.1:8000/api/photos/1 | json
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    41    0    41    0     0    221      0 --:--:-- --:--:-- --:--:--   221
{
  "name": "destroy",
  "status": true,
  "id": "1"
}
/* delete photo by id */สรุปท้ายบทความ
บทความนี้ต่อยอดความสามารถของบทความ “Laravel สร้าง RESTful API ง่าย ๆ ด้วย Resource Routes Controller EP5” ที่ RESTful template แต่บทความนี้จะสอนเพิ่มเติมการต่อ MySQL เพื่อ connect database ใช้ข้อมูลภายใน เพิ่มเติมด้วยความสามารถของ migration ที่จะ create table schema ให้เองตาม code CreatePhotosTable ที่ได้ทำการสร้างไว้จาก code PHP
บทความหน้าจะมาพูดถึงเรื่องอะไรค่อยติดตามกันนะครับ ขอบคุณที่ติดตามครับ