

Laravel API Resource Patterns - ยกระดับการจัดการ Response ให้ทรงพลัง
เปลี่ยน Resource ธรรมดาให้กลายเป็น Layer ที่ชัดเจน สวยงาม และยืดหยุ่น
หากเราต้องการเขียน API ด้วย Laravel ตัว framework ของ Laravel เองก็มีเครื่องมืออำนวยความสะดวกให้เราใช้อยู่แล้วนั่นก็คือ API Resource ซึ่งทำให้เราสามารถจัดการกับ API response ได้อย่างง่ายดาย
แต่เมื่อระบบของเราใหญ่ขึ้น มีความซับซ้อนขึ้น รวมถึงความสัมพันธ์ระหว่าง Model เองก็ตาม การใช้ Laravel API Resource โดยปกติอาจจะไม่เพียงพอต่อความต้องการ จนทำให้โค้ดเกิดความซับซ้อนขึ้นได้
บทความนี้จะพาคุณมาทำความรู้จักกับการใช้งาน API Resource ร่วมกับ design pattern อื่น ๆ ที่มักพบในการเขียน Laravel เพื่อให้ API response ของเรามีความยืดหยุ่นและทรงพลังมากยิ่งขึ้น
🎨 Presenter / Transformer ตัวช่วยแปลงค่า แยก logic ให้ชัดเจน#
บางครั้งใน API Resource ที่เขียนอาจไม่ได้มีแค่การส่งค่าเพียงอย่างเดียว แต่ยังมี business logic แฝงอยู่ใน response ที่ส่งค่ากลับไปด้วย หากเรานำ business logic เหล่านี้เขียนไว้ใน Resource ตรง ๆ ก็อาจจะส่งผลเสียให้ business logic เหล่านี้ไม่สามารถ reuse ได้ และอาจเกิด duplicate code ในกรณีที่มีการส่งค่าลักษณะคล้ายกันซ้ำ ๆ
ในกรณีนี้เราจะใช้ Layer ของ Presenter / Transformer มาช่วยในการแปลงค่าบางอย่างของ model หรือ collection ก่อนที่จะส่งค่ากลับ เช่น
class UserPresenter {
public function transform(User $user): array {
return [
'id' => $user->id,
'full_name' => "{$user->first_name} {$user->last_name}",
'status' => $user->is_active ? 'active' : 'inactive',
];
}
}
class UserResource extends JsonResource {
public function toArray($request) {
return (new UserPresenter())->transform($this->resource);
}
}phpจากตัวอย่างจะเห็นว่าเราสามารถแยก business logic บางอย่างออกมาจาก Resource ได้ และยังสามารถนำ logic เหล่านี้ไป reuse ใช้กับส่วนอื่น ๆ ในโปรเจกต์ได้อีกด้วย
🧩 Contextual Resource เปลี่ยน Resource ไปตาม context#
บางครั้งเราอาจต้องการ API ที่มีลักษณะคล้ายกันหรือดึงข้อมูลแบบเดียวกัน แต่อาจมีค่าบางอย่างเปลี่ยนไปตาม context ของระบบที่เรียกใช้ เช่น permission ของ user หรือ module ที่เรียกใช้งาน เป็นต้น การแสดงผลเหล่านี้แม้จะมีความใกล้เคียงกันแต่ก็อาจมีบางส่วนแตกต่างกันตาม context เราจึงสามารถประยุกต์ใช้ API Resource ของเราได้ ดังนี้
class UserResource extends JsonResource {
protected $context;
public function __construct($resource, $context = []) {
parent::__construct($resource);
$this->context = $context;
}
public function toArray($request) {
$data = [
'id' => $this->id,
'name' => $this->name,
];
if ($this->context['role'] === 'admin') {
$data['email'] = $this->email;
$data['last_login'] = $this->last_login;
}
return $data;
}
}phpจะเห็นว่าเมื่อ context เปลี่ยน ค่าบางอย่างของ API Resource จะเปลี่ยนไป แต่ค่าหลัก ๆ ยังคงอยู่เหมือนเดิม ทำให้ผลลัพธ์มีความยืดหยุ่นมากยิ่งขึ้น
📦 Response Envelope Pattern หุ้มทุก Response ด้วยรูปแบบเดียวกัน#
กรณีที่ระบบที่พัฒนามีนักพัฒนาหลายคน และเป็นระบบใหญ่ การที่ทุก API Resource จะมีมาตรฐานเดียวกันเป็นสิ่งที่ดี แต่หากทุกคนต้องมาคอยเขียน response structure ให้เหมือนกัน แม้จะมีเอกสารกลางคอยควบคุมแต่ก็คงเป็นเรื่องยุ่งยากอยู่ดี การมีตัวช่วยห่อหุ้ม response ไว้จะช่วยให้การจัดการง่ายขึ้นอย่างมาก
สมมุติว่าในโปรเจกต์ของเราต้องการ response structure ที่มีโครงสร้างแบบนี้
{
"status": "success",
"data": {...},
"meta": {
"request_id": "xxx",
"timestamp": 123456
}
}jsonสิ่งที่เราต้องการคือ BaseResource ที่ช่วยห่อหุ้มโครงสร้างดังกล่าวและส่งข้อมูลเพียงแค่ data ที่ต้องการ ดังนี้
class BaseResource {
public static function success($data, $meta = []) {
return response()->json([
'status' => 'success',
'data' => $data,
'meta' => array_merge($meta, [
'request_id' => Str::uuid(),
'timestamp' => now()->timestamp,
]),
]);
}
}phpจากนั้นเมื่อเรียกใช้งาน BaseResource ที่ตั้งค่าไว้ก็จะทำให้ response ของเรามีโครงสร้างพื้นฐานตามที่กำหนด
📝 Polymorphic Resource เมื่อ Resource เปลี่ยนตาม Model#
แม้ Resource ของ Laravel โดยปกติจะแยกตาม Model อยู่แล้ว แต่เราก็สามารถออกแบบโครงสร้างของ Resource ใหม่ให้สามารถใช้ Resource ร่วมกัน แต่แบ่งหน้าที่การจัดการ format ของ Model ไปที่ Presenter ได้ เช่น
class ContentResource extends JsonResource {
public function toArray($request) {
return match (get_class($this->resource)) {
Post::class => (new PostPresenter())->transform($this->resource),
Video::class => (new VideoPresenter())->transform($this->resource),
Podcast::class => (new PodcastPresenter())->transform($this->resource),
};
}
}phpจากตัวอย่างจะเห็นว่า ทั้ง Post, Video และ Podcast เป็น Resource ที่อยู่ในกลุ่มเดียวกัน และมักจะมี logic ในการ response ที่คล้ายกัน แต่ก็อาจจะมีบาง attributes ที่ต้องการรายละเอียดต่างกัน ในจุดนี้เราสามารถประยุกต์ใช้ Presenter เข้ามาช่วยได้ขึ้นอยู่กับสถานการณ์
🚀 Caching Layer ให้ response ได้เร็วขึ้น#
บางครั้ง API ที่เรียกไม่ได้เปลี่ยนแปลงข้อมูลบ่อย การใช้ cache เข้ามาช่วยเป็นเทคนิคพื้นฐานที่ช่วยให้ Laravel สามารถรองรับ traffic ที่สูงขึ้นได้ และในกรณีนี้เราสามารถนำ cache มาใช้ได้ตั้งแต่ Resource Layer เลย
class CachedResource extends JsonResource {
public function toArray($request) {
return Cache::remember("resource:{$this->id}", 60, function() {
return parent::toArray($request);
});
}
}phpเพียงเท่านี้ API ของเราก็สามารถรองรับ traffic ที่มากขึ้นได้แล้ว
🛠️ Repository Pattern Integration จัดการ Model อย่างมีประสิทธิภาพ#
แม้ตัวของ Resource เองจะจัดการ Model relations ได้อยู่แล้ว แต่เมื่อระบบใหญ่ขึ้น เรามักจะใช้ Repository Pattern เข้ามาช่วยจัดการโครงสร้างของ Model อยู่แล้ว
เราจึงสามารถนำ Repository Pattern นี้มาต่อยอดใช้กับ API Resource ได้เลยโดยที่ไม่ต้องให้ Resource จัดการ Model relations เอง
$users = $this->userRepository->getWithRelations();
return UserResource::collection($users);phpด้วย Pattern นี้จะทำให้เราสามารถนำ Repository Pattern มา reuse ใช้ได้อย่างมีประสิทธิภาพมากยิ่งขึ้น
🎯 บทสรุป#
แม้ Laravel Resource จะเป็นเครื่องมือที่ทรงพลังในตัวเองอยู่แล้ว แต่จะเห็นได้ว่าเรายังสามารถประยุกต์และแก้ไขเพิ่มเติมบางอย่าง เพื่อให้การพัฒนาสามารถทำได้ง่ายและยืดหยุ่นมากยิ่งขึ้น อีกทั้งยังช่วยเพิ่ม performance ให้แก่ระบบอีกด้วย
ซึ่งสิ่งที่นำมาประยุกต์ใช้นั้น ก็ล้วนแล้วแต่เป็นพื้นฐานในการเขียน Laravel ทั้งสิ้น ตัวแอดมินเองหวังว่าบทความนี้จะช่วยให้ผู้อ่านเห็นประโยชน์ของ การออกแบบโค้ดในเชิงพื้นฐานและการประยุกต์ใช้เพื่อต่อยอดให้เกิดประโยชน์สูงที่สุด ☕
ขอให้มีความสุขกับการเขียนโค้ด 🥰