ตุลาคม 2020

Spring Boot สร้าง RESTful API พร้อม Unit Tests แบบเข้าใจง่าย และรวดเร็ว EP2

การพัฒนาแอพพลิเคชั่น ข้อแนะนำคือควรทำ Unit Tests มองการพัฒนาในระยะยาว หากต้องมีการแก้ไข issue เล็กน้อย แต่ไม่แน่ใจว่าจะเกิด defect กับระบบเดิมหรือไม่ การมี unit tests ช่วยประหยัดเวลาการ regression tests ได้เยอะมาก และคุณจะเห็นประโยชน์ของการทำ Unit tests อย่างแน่นอน

รู้จัก @RestController annotation สร้าง routes RESTful API

annotation @RestController implement มาจาก @Controller ของ spring web เพื่อใช้งานสำหรับการสร้าง RESTful API (ไม่ใช่ static view) เพิ่มความสะดวกในการพัฒนา RESTful API ให้ง่ายยิ่งขึ้น

ตัวอย่างการสร้าง Route (text/plain)

package com.poolsawat.starter.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
	
	@RequestMapping("/")
	public String index() {
		return "Greetings from Spring Boot!";
	}
	
}

ทดสอบ http://localhost:8080/

Greetings from Spring Boot!

เพิ่ม Route (application/json)

... 
	@RequestMapping("/domain")
	public String[] json() {		
		return new String[] {"www",".","poolsawat",".","com"};
	}
...

restart server จากนั้น ทดสอบ http://localhost:8080/domain

[
  "www",
  ".",
  "poolsawat",
  ".",
  "com"
]

เพิ่ม Test Dependencies ใน POM

ก่อนที่จะเริ่มเขียน Unit Tests จำเป็นต้องเพิ่ม “spring-boot-starter-test” เข้าไปที่ไฟล์ POM (ไฟล์ pom.xml) มาเพิ่มกันเลย

...
<dependencies>
	...
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	...
</dependencies>
...

สร้าง src/test/java/com/poolsawat/starter/controller/HelloControllerTest.java

package com.poolsawat.starter.controller;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
	@Autowired
	private MockMvc mvc;

	@Test
	public void getHello() throws Exception {
		mvc.perform(MockMvcRequestBuilders.get("/")
				.accept(MediaType.APPLICATION_JSON))
				.andExpect(status().isOk())
				.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
	}
	@Test
	public void getDomain() throws Exception {
		mvc.perform(MockMvcRequestBuilders.get("/domain")
				.accept(MediaType.APPLICATION_JSON))
				.andExpect(status().isOk())
				.andExpect(content().json("[\"www\",\".\",\"poolsawat\",\".\",\"com\"]"));
	}
}

อธิบายการทำงาน

  • @SpringBootTest inject annotation Test เพื่อเริมการ Test
  • @AutoConfigureMockMvc inject annotation AutoConfiMockMVC (MockMVC มีหน้าที่สำหรับทำ Unit Tests มี feature mockup data)
  • @Autowired เป็นการ auto inject MockMVC bean ก่อนการจะเรียกใช้งานใน Testcase
  • @Test ระบุที method บอกให้รู้ว่า รันเคส method นี้
  • mvn.perform mockup route ที่ต้องการทดสอบ ระบุ MockMvcRequestBuilders.get(“/”) หมายถึง (“localhost:8080/”) ตรวจสอบ content-type คือต้องเป็น “MediaType.APPLICATION_JSON” กำหนดให้เป็น application/json เท่านั้น
  • andExpect
    • .andExpect(status().isOk()) expect htto status code OK (status code 200) หากไม่ใช่จะ fail ทันที
    • .andExpect(content().string(equalTo(“Greetings from Spring Boot!”))) expect content body ต้องเป็นคำว่า “Greetings from Spring Boot!” หากไม่ใช่จะ fail ทันที

จากนั้นสร้าง maven test build

apply -> run

ตรวจสอบที่ console panel

[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.poolsawat.starter.controller.HelloControllerTest
...
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.606 s - in com.poolsawat.starter.controller.HelloControllerTest
2020-10-15 10:57:54.262  INFO 5864 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.231 s
[INFO] Finished at: 2020-10-15T10:57:54+07:00
[INFO] Final Memory: 18M/220M
[INFO] ------------------------------------------------------------------------

ในทุกครั้งที่มีการแก้ไข java code จำเป็นต้องมีการ restart web server spring boot มี hot reload (การ auto restart web server เมื่อมี code change)

เพิ่ม “spring-boot-devtools”

...
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<optional>true</optional>
</dependency>
...

จากนั้นทำการ restart อีกครั้ง ต่อไปเวลาแก้ไข code ก็จะไม่ต้อง restart เองแล้ว

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

การพัฒนาแอพพลิเคชั่นที่มี Test ถือว่าเป็นแอพพลิเคชั่นที่ดี ข้อดีของการทำ Test มีเยอะมากช่วยลดเวลาการ Regression Test ได้เยอะ ถ้าแอพพลิเคชั่นของคุณยังไม่มี test เริ่มทำได้แล้ว เพราะถ้าไม่ทำคุณจะคุยกับเขา (ทีมพัฒนา แอพพลิเคชั่นที่มี test) ไม่รู้เรื่อง บทความต่อไปจะเป็นเรื่องเกี่ยวกับอะไร คอยติดตามกันนะครับ

Spring Boot ทำความรู้จัก เริ่มติดตั้งและเริ่มใช้งานเบื้องต้น EP1

Spring boot คืออะไร

spring boot เป็น framework package ที่ต่อยอดมาจาก spring framework ช่วยให้เราสามารสร้างแอพพลิเคชั่น (application) แบบ stand-alone แบบรวดเร็ว และง่ายดาย

บ่อยครั้งที่ต้องมานั่งปวดหัวกับเรื่องการ configuration project ในขั้นตอนการ setup เริ่มต้นในการสร้างแอพพลิเคชั่น ปัญหาเหล่านี้จะถูกทำให้เสร็จเรียบร้อยแล้ว เมื่อมาใช้งาน spring boot

Features ต่าง ๆ ที่มีมาใน Spring boot

  • การสร้างแอพพลิเคชั่นแบบ stand-alone
  • embed Tomcat ,Jetty web server ยอดนิยมมาให้เรียบร้อยแล้ว หรือแม้แต่ Underflow ก็มีมาให้แล้ว
  • มีการกำหนด dependencies starter (POM) สำหรับพัฒนาไม่ต้องเพิ่มหลาย dependencies เหมือน spring framework แบบแต่ก่อน
  • มี auto configuration มาให้พร้อม ไม่ต้องยุ่งวุ้นวายกับการ config project เหมือนแต่ก่อน (ดีม๊วก ๆ )
  • ให้ feature monitoring ,metrics, health checks มาพร้อมแล้วเรียกใช้งานได้เลย
  • ไม่มีการสร้าง XML configuration file อีกต่อไปเพราะทุกอย่างจะ code บน java (kotlin) ทั้งหมดเลย

จะเริ่มเขียน Spring boot ต้องเตรียมตัวอย่างไรบ้าง

สำหรับมือใหม่ หลาย ๆ คนที่กำลังจะเรียนรู้ spring framework สั่งที่ต้องทราบก่อนที่จะเริ่มพัฒนาแอพพลิเคชั่นด้วย Java Framework อย่าง Spring Boot ต้องรู้เกี่ยวกับอะไร จะอธิบายย่อย ๆ ตามนี้

  • IDE (Integrated Development Environment) เครื่องมือสำหรับพัฒนาแอพพลิเคชั่น ที่ดัง ๆ สำหรับพัฒนา Java ก็พวก Eclipse ,IntelliJ IDEA เป็นต้น
  • Maven (Project Management Tools) สำหรับ Java Developer ทำงานได้ง่ายยิ่งขึ้น การที่จะต้องคอยหา JAR หรือบางครั้งเราจะเรียกว่า lib (dependencies) ที่มีมากมายซะเหลือเกิน อีกทั้งยังต้องมานั่งตรวจสอบเรื่องเวอชั่นของ JAR อีกละก็ ไม่สนุกแน่ ๆ สำหรับ Developer Java ปัญหาเหล่านี้จะถูกแก้ไขด้วย Maven อีกทั้งยังมีความสามารถในการทำกระบวนการ Build ,Compile , Test ,Deploy ,Documentation ได้อีกมากมาย อีกทั้งยังมี plugins ให้เรียกใช้งานอีกเพียบเลย ถือว่าดีม๊วก ๆ
  • Spring IO หน่วยงานพัฒนา Java Framework ที่โด่งดัง และยังคงได้รับความนิยมจนถึงยุคปัจจุบันด้วยความที่ไม่หยุดพัฒนา จึงทำให้ framework ได้รับความนิยมอยู่จนถึงปัจจุบัน

เริ่มติดตั้ง และสร้าง Hello World กัน

การ initializr Spring Boot Project มีด้วยกันหลายวิธี ผมขอยกตัวอย่างวิธีนี้

  1. เข้าไป generate spring boot project ที่ https://start.spring.io/
    1. Project ให้เลือก Maven Project (Gradle Project) เป็นเครื่องมืออีกแบบ ฝั่ง mobile dev ชอบใช้กัน เพราะมีมาใน android project)
    2. Language ให้เลือก Java (แล้วแต่ความชำนาญ ของผู้พัฒนาจะเลือกเป็น Kotlin หรือ Groovy ก็ได้)
    3. Spring Boot ให้เลือก version 2.3.4 เป็นเวอร์ชั่น ที่ stable แล้ว
    4. Project Metadata
      1. Group ใส่เป็น com.poolsawat (กำหนดเป็น domain ขององค์กร)
      2. Artifact ใส่เป็น Poolsawat (ชื่อสิ่งประดิษฐ์กำหนดเป็นชื่อของแอพพลิเคชั่น )
      3. Name ใส่เป็น Poolsawat (ชื่อแอพพลิเคชั่น)
      4. Description ใส่ Demo project for Spring Boot หรือจะใส่อธิบาย destination ของแอพพลิเคชั่น สั้น ๆ
      5. Package name ใส่ com.poolsawat.starter ใส่เป็น java package สำหรับเก็บ source code จะกำหนดภายใต้ src/com/poolsawat/starter/**
      6. Packaging เลือก Jar เพราะจะเลือกใช้ stand-lone application ที่รันด้วย embed Tomcat web server
      7. Java เลือก 8 (จะเลือก 10 , 15 แล้วแต่ JDK ในเครื่องที่ติดตั้งไว้)
      8. Dependencies เลือก “web”
      9. กด “GENERATE” downloadลงเครื่อง จะได้ไฟล์ .zip มา รอทำในขั้นถัดไป

2. เปิด IDE ตัวอย่างจะใช้ Eclipse IDE
        1. Unzip Poolsawat.zip ย้ายไฟล์ทั้งหมดไปยัง workspace ของ Eclipse ที่สร้างไว้
        2. หน้า Eclipse เลือก File -> Import… -> Maven -> Poolsawat

        3. จะมี Poolsawat เข้ามาที่ Project Explorer ทางซ้ายมือของ Eclipse IDE

3. แก้ไข Code ไฟล์ PoolsawatApplication.java ตามนี้

package com.poolsawat.starter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class PoolsawatApplication {

	public static void main(String[] args) {
		SpringApplication.run(PoolsawatApplication.class, args);
	}

	@GetMapping("/hello")
	public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
		return String.format("Hello %s!", name);
	}

}

4. สร้าง Maven build เลือกเมนู Run -> Run Configurations… -> Maven Build -> New Launch configuration -> ใส่ค่าตามภาพนี้

Apply -> Run

5. รอ… maven download dependencies สักครู่…..

...

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.4.RELEASE)

2020-10-15 00:06:06.708  INFO 4832 --- [           main] c.p.starter.PoolsawatApplication         : Starting PoolsawatApplication on PoolPC-PC with PID 4832 (D:\workspace-blog\Poolsawat\target\classes started by PoolPC in D:\workspace-blog\Poolsawat)
2020-10-15 00:06:06.711  INFO 4832 --- [           main] c.p.starter.PoolsawatApplication         : No active profile set, falling back to default profiles: default
2020-10-15 00:06:07.773  INFO 4832 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-10-15 00:06:07.781  INFO 4832 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-10-15 00:06:07.782  INFO 4832 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.38]
2020-10-15 00:06:07.848  INFO 4832 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-10-15 00:06:07.848  INFO 4832 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1078 ms
2020-10-15 00:06:08.018  INFO 4832 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-10-15 00:06:08.174  INFO 4832 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-10-15 00:06:08.191  INFO 4832 --- [           main] c.p.starter.PoolsawatApplication         : Started PoolsawatApplication in 1.823 seconds (JVM running for 2.231)

6. ทดสอบเข้าลิ้ง http://localhost:8080/hello

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

การพัฒนาเว็บด้วย Spring boot นั้นทำได้ไม่ยากเลย ค่อนข้างง่ายกว่าวิธีการเดิมของ Spring Framework ที่ผ่าน ๆ มา ไม่ต้อง configuration ให้ยุ่งยาก ไม่ต้องลำคาญกับการต้องมา control version ของ dependencies ที่มากมายซะเหลือเกิน เลยทำให้ผมชอบที่จะใช้งาน spring boot กับทุก ๆ project ของผมเลยครับ บทความหน้าจะเป็นเรื่องอะไร คอยติดตามกันนะครับ

Laravel connect MySQL Database ทำ RESTful API CRUD [เพิ่ม(create) ,อ่าน(read) , แก้ไข(update), ลบ(delete)] EP6

Laravel Series เดินทางมาถึง EP6 ซึ่งถ้าใครยังไม่ได้อ่านบทความ EP 1 – 5 สามารถเข้าได้ที่ลิ้งใต้ล่างนี้

  1. Laravel เริ่มติดตั้งและสร้าง route ง่าย ๆ EP1
  2. Laravel แนะนำโครงสร้างภายในโปรเจค อธิบายแต่ละส่วนการทำงาน EP2
  3. Laravel ใช้งาน views blade template engine EP3
  4. Laravel รู้จัก Routes และ การรับค่าจาก HttpRequest EP4
  5. Laravel สร้าง RESTful API ง่าย ๆ ด้วย Resource Routes Controller EP5

บทความนี้จะมาทำให้ application ของเราต่อกับฐานข้อมูล (database) ของ MySQL และเมื่อต่อได้แล้วจะมาใช้งาน migrate และมาทำระบบ CRUD ของข้อมูล Photos กัน

สร้าง Schema และ Migrate Table ด้วย artisan migrate

  1. สร้าง 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
[email protected]
...

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_table

4.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:

CommandDescription
$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_id UNSIGNED BIGINT and taggable_type VARCHAR equivalent columns.
$table->uuidMorphs('taggable');Adds taggable_id CHAR(36) and taggable_type VARCHAR(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_token VARCHAR(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_at TIMESTAMP equivalent column for soft deletes with precision (total digits).
$table->softDeletesTz('deleted_at', 0);Adds a nullable deleted_at TIMESTAMP (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_at and updated_at TIMESTAMP equivalent columns with precision (total digits).
$table->timestampsTz(0);Adds nullable created_at and updated_at TIMESTAMP (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.
https://laravel.com/docs/8.x/migrations

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_ci

5. สร้าง 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

บทความหน้าจะมาพูดถึงเรื่องอะไรค่อยติดตามกันนะครับ ขอบคุณที่ติดตามครับ

Laravel สร้าง RESTful API ง่าย ๆ ด้วย Resource Routes Controller EP5

รูปแบบลักษณะงานเว็บไซต์ในปัจจุบัน เกิน 80 – 90 % ต้องมีการทำระบบ RESTful API เพื่อให้บริการข้อมูลกับ Application platform รูปแบบต่าง ๆ ไม่ว่าจะเป็น mobile และ web เพราะเป็นรูปแบบการพัฒนาที่ได้รับความนิยมมากในปัจจุบัน กล่าวคือการเชื่อมโยง application ด้วยข้อมูลจากแหล่งเดียวกัน นั้นเอง

การพัฒนา RESTful API Service สามารถทำได้กับหลาย ๆ ภาษาโปรแกรมมิ่งทั่วไป (ถ้าภาษาโปรแกรมมิ่งมี feature http ต้องสามารถทำ RESTful API ได้อย่างแน่นอน) แต่สิ่งที่จะทำให้การพัฒนา RESTful นั้นง่ายและสะดวกยิ่งขึ้นก็คือเครื่องมือ หรือ cli (command line user interface ) สำหรับช่วยทำให้การเขียนโปรแกรมของเราง่าย และสะดวกมากยิ่งขึ้นไปอีก

laravel มี artisan cli (symfony class console) ช่วยทำให้งาน develop สะดวกและง่ายยิ่งขึ้น บทความนี้จะพามาทำความรู้จัก cli นี้กัน

สร้าง Resource Controller RESTful API แรกกัน

laravel มี feature Resource Controllers ที่จะช่วยสร้าง RESTful function template controller ขึ้นมาให้ เราเพียงแค่ implement function ตาม RESTful action เท่านั้นก็สามารถเรียกใช้งานได้เลย โดยมีวิธีการเรียกใช้งานตามตัวอย่าง ข้างล่างนี้

$ php artisan make:controller PhotoController --resource

laravel สร้าง generate /app/Http/Controllers/PhotoController.php ภายในไฟล์จะมีการสร้าง function RESTful template มาให้

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PhotoController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

มาทำความรู้จักการทำงานแต่ละ function มามีการทำงานอย่างไร ตามการอธิบายจากตารางนี้

Actions Handled By Resource Controller

VerbURIActionRoute Name
GET/photosindexphotos.index
GET/photos/createcreatephotos.create
POST/photosstorephotos.store
GET/photos/{photo}showphotos.show
GET/photos/{photo}/editeditphotos.edit
PUT/PATCH/photos/{photo}updatephotos.update
DELETE/photos/{photo}destroyphotos.destroy

เพิ่ม routes mapping ที่ไฟล์ /routes/api.php

Route::resource('photos', 'App\Http\Controllers\PhotoController');

เพิ่ม route:resource เป็น static function สำหรับการทำ RESTful routes เพิ่มเพียงบรรทัดเดียวก็สามารถเรียก http methods ต่าง ๆ เหล่านี้ GET ,POST ,PUT ,DELETE ,PATCH ได้เลย ง่ายมาก ๆ

implement PhotoController ตาม function การทำงานเพื่อใช้ตรวจสอบการ call จาก REST client

ผมจะเพิ่ม function response()->json(); เพื่อทำการ return http response คืนค่ากลับไปในรูปแบบ application/json format

<?php

namespace App\Http\Controllers;

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']);
  }

  /**
   * Show the form for creating a new resource.
   *
   * @return \Illuminate\Http\Response
   */
  public function create()
  {
    return response()->json(['name' => 'create']);
  }

  /**
   * Store a newly created resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return \Illuminate\Http\Response
   */
  public function store(Request $request)
  {
    return response()->json(['name' => 'store', 'payload' => $request->all()]);
  }

  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function show($id)
  {
    return response()->json(['name' => 'show', 'id' => $id]);
  }

  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function edit($id)
  {
    return response()->json(['name' => 'edit', 'id' => $id]);
  }

  /**
   * Update the specified resource in storage.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function update(Request $request, $id)
  {
    return response()->json(['name' => 'update', 'payload' => $request->all(), 'id' => $id]);
  }

  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return \Illuminate\Http\Response
   */
  public function destroy($id)
  {
    return response()->json(['name' => 'destroy', 'id' => $id]);
  }
}

หลังจากปรับแก้ PhotoController.php เรียบร้อย ทำการ รันคำสั่ง

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

ทดสอบ RESTful API

curl http://127.0.0.1:8000/api/photos

{“name”:”index”}

curl http://127.0.0.1:8000/api/photos/create

{“name”:”create”}

curl -X POST -H "Content-Type: application/json" -d "{\"name\":\"poolsawat.com\"}" http://127.0.0.1:8000/api/photos

{“name”:”store”,”payload”:{“name”:”poolsawat.com”}}

curl http://127.0.0.1:8000/api/photos/9999

{“name”:”show”,”id”:”9999″}

curl http://127.0.0.1:8000/api/photos/9999/edit

{“name”:”edit”,”id”:”9999″}

curl -X PUT -H "Content-Type: application/json" -d "{\"name\":\"poolsawat.com\"}" http://127.0.0.1:8000/api/photos/99999

{“name”:”update”,”payload”:{“name”:”poolsawat.com”},”id”:”99999″}

curl -X DELETE http://127.0.0.1:8000/api/photos/9999

{“name”:”destroy”,”id”:”9999″}

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

การทำ RESTful API ด้วย Laravel จากที่ได้ใช้งานมาได้สักระยะ จากประสบการณ์ค่อนข้างชอบ เพราะด้วยตัวภาษาโปรแกรมมิ่ง เวลาจะ compile ไม่จำเป็นต้อง restart server เพราะเป็นภาษาคริป เลยถือเป็นข้อดีอย่างนึงสำหรับทำ prototype api ง่าย ๆ เพื่อนำเสนอลูกค้าหรือใช้งานกันเองภายในบริษัท ต้องการศึกษาเพิ่มเติมเกี่ยวกับการทำ Resource Controllers ของ Laravel ศึกษาได้ที่ลิ้งนี้ Resource Controllers

บทความต่อไปจะเป็นเรื่องอะไร คอยติดตามกันนะครับ

Laravel รู้จัก Routes และ การรับค่าจาก HttpRequest EP4

การพัฒาน web application ข้อที่ควรคำนึงถึงคือเรื่อง url (endpoints) ต่าง ๆ ภายใน application ของเรา อาจจะเป็น url แบบเดียวกันได้เพียงแต่กำหนด http method ไม่เหมือนกัน เช่น

  • [GET] /api/v1/user สร้างเพื่อ provide data ให้กับ client
  • [POST] /api/v1/user สร้างเพื่อกำหนดไว้สำหรับ create data ใหม่

จุดสั่งเกตุที่เห็นได้ชัดคือ ความเหมือนกันของ url แต่ต่างกันในเรื่อง method เท่านั้น ทางฝั่ง application เราเอง (กำหนด route) จะ mapping route ให้ตรงกับการ request เรียกเข้ามา request เรียกมาแบบ [GET] ก็จะเข้าไปทำงานในส่วนของ GET method , เรียกมาแบบ [POST] จะเข้าไปทำงานในส่งของ POST method เรื่องเหล่านี้ต้องมาทำความเข้าใจกัน บทความนี้จะมาอธิบาย

การกำหนด Methods มีกี่แบบ Laravel จะต้องเขียนอย่างไร

  • GET
    • มีการเก็บ cached การ request ได้
    • ไม่ควรอย่างยิ่งสำหรับการส่ง sensitive data มากับ url
    • มีข้อจำกัดเรื่องความยาวของ url + parameters
    • ใช้สำหรับขอข้อมูลเท่านั้น ไม่เหมาะกับการแก้ไขข้อมูล
  • POST
    • ไม่ถูกเก็บ cached เอาไว้ (request body) จะไม่ถูกเก็บไว้
    • ไม่อยู่ในประวัติของ browser
    • request body สามารถกำหนดได้ยาว ขนาดใหญ่
  • PUT
    • เป็นลักษณะการสร้าง (created) หรือ อัพเดท (update) ข้อมูลฝั่ง serve
  • HEAD
    • คล้ายกันกับ GET แต่ไม่มีการคืนค่ากลับ
  • DELETE
    • การลบข้อมูลที่เป็น unique โดยส่วนใหญ่จะเป็น uuid หรือ id ที่ระบุเป็นเฉพาะเจาะจง
  • PATCH
    • เพื่อปรับเปลี่ยนบางอย่างของข้อมูลฝั่ง server
  • OPTIONS
    • เพื่ออธิบายการทำงานของ data ฝั่ง server

Laravel มีการกำหนด http methods Laravel Routing จะถูกสร้างไว้ที่ไฟล์ /routes/web.php และ /routes/api.php

[GET] สำหรับ inline function
Route::get('foo', function () {
    return 'Hello World';
});
[GET] สำหรับ use controller route อื่น ๆ
Route::get('/user', '[email protected]');
[*] ลักษณะการกำหนด methods อื่น ๆ ทำตาามนี้
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

การกำหนด parameters ให้ทำ route ในรูปแบบต่าง ๆ

[required] การกำหนด parameters บังคับ ในรูแบบ required parameters จะกำหนดภายใต้ {} 
Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

[non required] กำหนด parameters แบบไม่บังคับให้ส่งเข้ามา
Route::get('user/{name?}', function ($name = null) {
    return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

[regular] กำหนด parameter ด้วย rules ตาม pattern ด้วย regular expression
Route::get('user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

ศึกษาเพิ่มเติมเรื่อง Routes การทำ Routes Grouping จากลิ้งนี้ Laravel Routes

การรับค่าจาก HttpRequest ในรูปแบบต่าง ๆ

การที่จะได้มาของ parameters data ที่ได้จากการ request client นั้นมีได้หลายรูปแบบ ตามตัวอย่าง

การเข้าถึงค่าของ url information ที่ถูก request เข้ามา

[url path]
$uri = $request->path();

// Without Query String...
$url = $request->url();

// With Query String...
$url = $request->fullUrl();

การรับค่า (request body data) จาก HttpRequest (Payload Request Body)

[all] จะได้ค่าเป็น object ของ request data
$input = $request->all();

[name] การเข้าถึง value ด้วยการกำหนดจาก name ของ parameter
$name = $request->input('name');

[empty then value] กำหนดค่า default หากไม่พบค่าจาก parameters นั้น ๆ 
$name = $request->input('name', 'Sally');

[json array] การเข้าถึง request ที่เป็นลักาณะ raw json array ก็ทำได้
$name = $request->input('products.0.name');
$names = $request->input('products.*.name');

การรับค่า (request body data) จาก HttpRequest (Query Parameters)

[?name=hello] การเข้าถึง queryString จาก parameters
$name = $request->query('name');

[empty then value]
$name = $request->query('name', 'Helen');

ตรวจสอบค่าจาก parameters ด้วย name

[checking] ตรวจสอบ parameter ชื่อ name หากมีจะเข้าเงื่อนไขการทำงาน
if ($request->has('name')) {
    //
}
if ($request->has(['name', 'email'])) {
    //
}
if ($request->hasAny(['name', 'email'])) {
    //
}

[not empty] ตรวจสอบค่า parameter ต้องไม่เป็นค่าว่าง จะใช้ function filled ตรวจสอบ
if ($request->filled('name')) {
    //
}

รูปแบบการใช้งาน Routes และ HttpRequest ใน Laravel มีเนื้อหาเพิ่มเติมอีกมากมาย สามารถศึกษาเพิ่มเติมได้ที่ Laravel Route ,Laravel Request

ขอบคุณที่ติดตามบทความครับ