การดำเนินการกับฐานข้อมูลของ ลาราเวล ใช้การต่อเชื่อมและทำงานด้วย PDO ซึ่งเป็นไลบรารีที่ทำงานกับฐานข้อมูลได้หลายชนิด และสนับสนุนการทำงานแบบออบเจ็กต์ที่สมบูรณ์ เมื่อใช้ PDO ก็ทำให้สนับสนุนการใช้งานตามมาตรฐานภาษา SQL นอกจากนี้ ลาราเวลเองยังเพิ่มเติมการทำงานกับฐานมูลในสองลักษณะและสามารถใช้ร่วมกันได้คือ
- Fluent query builder
- Eloquent ORM
ทั้งสองชนิดของการดำเนินการข้อมูลช่วยอำนวยความสะด้วยได้มาก แล้วจะได้พบว่าง่ายทั้งคู่ แต่ก่อนที่จะใช้ของง่ายขอยกตัวอย่างของยากกว่าแต่ก็ไม่มาก เพื่อให้เข้าใจว่าก่อนที่จะเป็นของง่ายเขาทำกันอย่างไง โดยใช้รูปแบบ Data Gateway ซึ่งเป็นรูปแบบการดำเนินการกับฐานข้อมูลพื้นฐานที่เข้าใจได้ง่ายและสร้างขึ้นได้เอง
รูปแบบ Data Gateway
จัดโครงสร้างคลาส ให้ง่ายต่อการนำไปใช้งาน ให้มี อินเทอร์เฟส Db มีเมทธอด connect( ) เป็นฟังก์ชันมาตรฐานในติดต่อกับฐานข้อมูลในรูปแบบ PDO มีคลาสอื่น ๆ ที่จะสืบทอดคุณสมบัติ เช่น Mysql ทำหน้าที่เชื่อมกับฐานข้อมูล MySql หรือ Sqlite ทำหน้าที่เชื่อมกับฐานข้อมูล SQLite ดังเขียนได้เป็น รูป 1

รูป 1 แผนภาพคลาส
เมื่อให้เป็นไปตามแผนภาพ เราก็สร้างอินเทอร์เฟส Db ก่อนให้มีเพียงฟังก์ชันเดียวคือ connect( ) ในที่นี้เลือกเป็นค่า static เพื่อให้ง่ายต่อการนำไปใช้ และถือว่ามีค่าที่เดียว
Code 1. database/datagateway/Db.php
<?php
namespace Database\Datagateway;
interface Db{
public static function connect();
}
ต่อมาสร้างคลาส Mysql เพื่อเป็นตัวแทนการสืบทอด Db และทำงานกับฐานข้อมูล MySql โดยใส่ค่าที่จำเป็นต่อการชื่อต่อกับฐานข้อมูล เช่น ชื่อฐานข้อมูล (laravel) ชื่อผู้ใช้ (root) และไม่มีรหัสผ่าน ภายในฟังก์ชันทำหน้าที่คืนค่า ออบเจ็กต์ PDO
Code 2. database/datagateway/Mysql.php
<?php
namespace Database\Datagateway;
class Mysql implements Db{
private static $dsn_mysql ="mysql:host=localhost;dbname=laravel;charset=utf8";
private static $user = "root";
private static $password = "";
public static function connect(){
try{
return new \PDO(self::$dsn_mysql, self::$user, self::$password);
} catch(\PDOException $e){
die('Cound not connection to database because:'. $e->getMessage());
}
}
}
และคลาสสุดท้ายในการทำงานกับฐานข้อมูลคือ คลาส CourseTable ซึ่งอยู่ในรูปแบบ Raw Data Gateway ที่คืนค่าข้อมูลดิบ ที่ยังไม่ปรุงแต่งแต่อย่างใด เวลาใช้กับต้องปรับให้เหมาะกับแต่ละงานเอง ในการสร้าง จะยกตัวอย่างเพียงฟังก์ชัน all( ) กับ insert( ) สำหรับฟังก์อื่น ๆ ก็เลียนแบบสองฟังก์นี้ได้เลย เพราะใช้งานคล้าย ๆ กัน
Code 3. database/datagateway/CourseTable.php
<?php
namespace Database\Datagateway;
class CourseTable{
private $pdo;
public function __construct($pdo){
$this->pdo =$pdo;
}
public function all(){
$sql = "select * from courses";
return $this->pdo->query($sql);
}
public function insert($model){
//id is auto-number
$name = $model['name'];
$description = $model['description'];
$on_off = $model['active'];
$active = ($on_off==='on')?1:0; //checkbox
$sql = "insert into courses(name, description, active) values(?,?,?)";
$stm = $this->pdo->prepare($sql);
$stm->bindParam(1, $name);
$stm->bindParam(2, $description);
$stm->bindParam(3, $active);
return $stm->execute();
}
public function update($model){}
public function delete($id){}
}
สำหรับการนำไปใช้ เช่นใช้ใน CourseControllerก็เรียกออบเจ็กต์ $pdo จาก Mysql::connect( ) แล้วฉีด $pdo ลงในคอนสตรักเตอร์ของ CourseTable($pdo ) หลังจากนั้นก็นำออบเจ็กต์ $courseTb ไปใช้งาน ตามฟังก์ชันที่ต้องการ เช่น ฟังก์ชัน all( ) ที่จะคืนค่าทุกอย่างในตาราง Course แล้วส่งรายการข้อมูลในชื่อตัวแปร couresไปยัง view ที่ชื่อ courses
Code 4. App/Http/Controllers/CourseController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
include("../database/datagateway/Db.php");
include('../database/datagateway/Mysql.php');
include('../database/datagateway/CourseTable.php');
use Database\datagateway\Mysql;
use Database\datagateway\CourseTable;
class CourseController extends Controller
{
public function __invoke(Request $request)
{
$pdo = Mysql::connect();
$courseTb = new CourseTable($pdo);
$courses = $courseTb->all();
$data = array();
foreach($courses as $c){
$id = $c['id'];
$name = $c['name'];
$description = $c['description'];
$active = $c['active'];
$data[] = array('id'=>$id,'name'=>$name,'description'=>$description, 'active'=>$active);
}
return view('courses', ['courses'=>json_encode($data)]);
}
}
จากตัวอย่างนี้จะเป็นพื้นฐานสำหรับงานที่ไม่ได้ใช้ Laravel Framework แต่ถ้าใช้ Laravel Framework ใช้ฐานข้อมูลในรูปแบบของ Laravel จะดีกว่ามาก
<form method='post' action='/course/insert'>
@csrf
<label>Name</label>
<input name='name' type='text'>
<label>Descriptione</label>
<input name='description' type='text'>
<label>Active</label>
<input name='active' type='checkbox'>
<button type='submit'>Submit</button>
</form>
สร้างการต่อเชื่อมกับฐานข้อมูลของ Laravel
เปิดไฟล์ .env จะพบชื่อฐานข้อมูลที่ตั้งให้เป็น laravel ซึ่งเป็นค่าปริยายที่มีมาให้ แต่ถ้าต้องการเปลี่ยนแปลงชื่อฐานข้อมูลก็แก้ไข ณ ตอนนี้ได้เลย
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
Laravel ไม่ได้สร้างฐานข้อมูลให้อัตโนมัติ จะต้องสร้างฐานข้อมูลในชื่อ laravel เองกับฐานข้อมูล MySQL ใน MyPHPadmin ด้วยตนเอง
สร้างตารางตารางได้ด้วย Migrations
การสร้างตารางจะสร้างด้วยตนเองใน MyPHPadmin ก็ได้ หรือจะสร้างจากอัตโนมัติก็ได้ ซึ่งดูเหมือนว่า Laravel จะมีตารางให้มา 4 ตารางเป็นตัวอย่าง ให้เราเลียบแบบการสร้างเพิ่มอีกหนึ่งตาราง โดยสมมุติว่าให้สร้างตาราง course ดังให้สร้างไฟ่ล์ใหม่ จากการเลียนแบบไฟล์ที่ใช้สร้างตาราง users
เปิดไฟล์ database/migrations/2014_10_12_000000_create_users_table.php
สร้างไฟล์ใหม่เลียนแบบ ในไฟล์ชื่อ 2021_06_27_000000_create_course_table.php โดยชื่อไฟล์ต้องอยู่ในรูปแบบนี้ ให้กำหนดคอลัมน์ในตารางโดยกำหนด id(Primary Key), name, description, และ active ดังนี้
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCourseTable extends Migration
{
public function up()
{
Schema::create('courses', function (Blueprint $table) {
$table->id(); // auto increment and primary key
$table->string('name');
$table->string('description');
$table->boolean('active');
});
}
public function down()
{
Schema::dropIfExists('courses');
}
}
ใช้คำสั่ง migrate ใน CLI ทุกไฟล์ในโฟลเดอร์ migrations มี 5 ไฟล์รวมไฟล์สร้างตาราง course ที่เราเพิ่งสร้าง จะได้ตารางทั้งหมดใน ฐานข้อมูล MySql ในฐานข้อมูลที่ชื่อ laravel
>php artisan migrate
เมื่อใช้คำสั่งนี้ให้ตรวจสอบว่ามีตารางเกิดขึ้นมาจริงไหม (เปิด MyPHPadmin) ดังเมื่อเปิดดูแล้ว ให้ทดลองแทรก(insert)ข้อมูลในตาราง course ลงไปประมาณ 3 แถว เพื่อให้ในการทดสอบการสืบค้นต่อไป
สร้างแบบจำลอง
ใช้คำสั่งใน CLI สร้างแบบจำลองในชื่อ CourseModel
>php artisan make:model CourseModel
ต่อมาปรับแต่งคลาสนี้ ในทำงานกับฐานข้อมูลให้สืบทอดจากคลาส Model สร้างชื่อตารางให้ตรงกับฐานข้อมูลรวมทั้งข้อมูลที่จำเป็นอื่น ๆ
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class CourseModel extends Model
{
protected $table = 'courses';
protected $primaryKey = 'id';
public $incrementing = true;
protected $connection = 'mysql';
}
สืบค้นข้อมูลจากฐานข้อมูล
เมื่อสร้างแบบจำลองพร้อมแล้ว ต่อไปจะเป็นการทดสอบสืบค้นข้อมูล ในการสืบค้นใช้คำสั่งแทรกลงไปในไฟล์หน้าแรกกัน โดยใช้การสืบค้นผ่าน คลาส CourseModel และเรียกคำสั่ง all( ) ซึ่งหมายถึงอ่านข้อมูลทั้งหมดจากตาราง course เมื่อสืบค้นได้แล้ว นำไปแสดงผล ในรูปแบบตาราง
<div class="content">
<h2>Courses</h2>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Active</th>
</tr>
</thead>
<tbody>
@php
use App\Models\CourseModel;
use Illuminate\Support\Facades\DB;
$courses = DB::select('select * from courses')
//or
//$courses = CourseModel::all();
@endphp
@foreach($courses as $course)
<tr>
<th scope="row">{{$course->id}}</th>
<td>{{$course->name}}</td>
<td>{{$course->active}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
จากตัวอย่างมีการใช้งานเพื่ออ่านตาราง course ทำได้สองลักษณะคือ
- แบบ Fluent query builder ด้วยคำสั่ง DB::select('select * from course')
- แบบEloquent ORM ด้วยคำสั่ง CourseModel::all()
แบบแรกใช้เป็นภาษา SQL ซึ่งเราต้องรู้จักการแบบภาษา SQLส่วนแบบที่สองใช้เป็นแบบฟังก์ชัน เราต้องรู้จักฟังก์ชันที่มีให้ใช้ ซึ่งแน่นอนมีในแบบที่คาดเดาได้ง่าย และทั้งสองแบบให้ผลการสืบค้นออกมาในรูปแบบ JSON อย่างกรณีตัวอย่างนี้ได้ผลออกมาในรูปแบบ อาร์เรย์ของ JSON แต่ถ้าใช้การสืบค้นที่ได้ค่ามาค่าเดียวก็จะได้ผลออกมาในรูปหนึ่งออบเจ็กต์ JSON
การใช้งาน Fluent Query Builder (FQD)
จะเห็นแล้วว่า การใช้ FQD เน้นการเขียน ภาษา SQL และใช้งานคู่กับฟังก์ชัน ดังนั้นแล้วเราต้องรู้ว่ามีฟังก์ชันอะไรบ้าง ซึ่งชื่อฟังก์ชันก็สื่อความหมายในตัวอยู่แล้ว
- select( )
- insert( )
- update( )
- delete( )
- unprepared( )
- connection( )
- table()
ในลำดับ 1-4 เราคาดเดาได้และต้องศึกษาอีกนิด เพื่อการใส่ตัวแปรทำอย่างไง ส่วนในลำดับ 5-6 ก็ทำความเข้าใจเพิ่มเติมอีกนิดเป็นตัวเสริมของ PDO โดย unprepared( ) ใช้แบบรูปแบบเต็ม SQL และ connection( ) เพื่ออ่านคุณสมบัติของ PDO
การใช้งานเริ่มจากการนำเข้า(use) DB แล้วเปิดฟังก์ชันแบบใส่ตัวแปรเป็นฟังก์ชัน หรือที่เรียกว่า Closure
use Illuminate\Support\Facades\DB;
การใส่ตัวแปร ทำได้ด้วยการเพิ่มตัวแปรลงในฟังก์ชันในรูปอาร์เรย์ ดังตัวอย่างในลักษณะต่าง ๆ คือ:
$users = DB::select('select * from users where active = ?', [1]);
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
$affected=DB::update('update users set votes = 100 where name = ?',['Anita']);
การใช้ unprepared( ) เป็นการใส่ SQL เต็มรูปแบบ ทำงานตามภาษา SQL ที่ใส่
DB::unprepared('update users set votes = 100 where name = "Dries"');
DB::unprepared('create table a (col varchar(1) null)');
การใช้งาน connectio( ) เป็นการอ่านคุณบัติใน PDO
$users = DB::connection('sqlite')->select(...);
$pdo = DB::connection()->getPdo();
ทรานเซกชันของ FQD และ Eloquent
การควบคุมชุดดำเนินการกับฐานข้อมูลที่ทำงานหลายคำสั่งดำเนินการจนกว่าทุกผลการดำเนินการจะเสร็จสิ้น จึงเรียกว่าหนึ่งทรานเซกชัน (Transaction) แต่ในบางครั้งการทำงานยังไม่จบในลำดับใดลำดับหนึ่งด้วยเหตุผลใดก็ตาม เช่น ในลำดับแรกมีการแทรกข้อมูลใหม่ แล้วต่อด้วยการลบข้อมูล แต่เกิดความผิดพลาดในลำดับนี้ การย้อนกลับในลำดับก่อนหน้านี้จึงต้องทำให้ข้อมูลเหมือนเดิม
ลาราเวล ใช้ DB facade (รูปแบบทางเข้าอย่างหนึ่ง) เป็นตัวเริ่มต้นในการเรียกฟังก์ชัน transaction( ) ซึ่งใช้งานได้กับ FQD และ Eloquent
การใช้งานเริ่มจากการนำเข้า(use) DB วิธีการนี้ไม่จำเป็นต้องมีการบังคับให้ย้อนกลับได้ เพราะจะถือว่ามีการบังคับให้ย้อนกลับแล้ว โดยไม่ต้องใส่คำสั่งย้อนกลับ (DB::rollBack()) และไม่ต้องใช้คำสั่งยืนยันการดำเนินการของทรานเซกชัน (DB::commit())
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
ซึ่งหากว่าต้องการใช้แบบไม่อัตโนมัติตามตัวอย่างก่อนหน้านี้ก็ดำเนินแยกแต่ละขึ้นตอนได้ตามฟังก์ชันต่อไปนี้
DB::beginTransaction();
DB::rollBack();
DB::commit();
ทดสอบการใช้งาน FQD
ตอนนี้ถึงเวลาทดสอบการใช้งาน FQD จากที่เห็นแนวทางการใช้งาน เราจะเริ่มจากการสร้าง เส้นทาง แล้วต่อด้วยสร้างคอนโทรลเลอร์ และหน้าเว็บ
Route::get('/courses', [CourseController::class, 'index']);
Route::get('/courses/{id}', [CourseController::class, 'show']);
ต่อด้วยการสร้างหน้า View รองรับการอ่านข้อมูลในรูปแบบอาร์เรย์ของตาราง Course
<div class="content">
<h2>Courses</h2>
<table class="table">
<thead>
<tr>
<th scope="col">#</th> <th scope="col">Name</th>
<th scope="col">Description</th><th scope="col">Active</th>
</tr>
</thead>
<tbody>
@foreach($courses as $course)
<tr>
<th scope="row">
<a href="/courses/{{$course->id}}">{{$course->id}}</a></th>
<td>{{$course->name}}</td>
<td>{{$course->description}}</td>
<td>{{$course->active}}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
และสร้างคอนโทรลเลอร์แบบเต็มรูปแบบ ด้วยทางเลือก --resoruce
php artisan make:controller CourseController --resource
use App\Models\CourseModel;
use Illuminate\Support\Facades\DB;
public function index()
{
return view('course', [
'courses' => DB::select('select * from courses')
]);
}
public function show($id)
{
//return single json
//return CourseModel::find($id);
//return array of json
//return array(CourseModel::find($id));
//return DB::select('select * from courses where id=?',[$id]);
return view('course', [
'courses' => DB::select('select * from courses where id=?',[$id])
]);
}
จากตัวอย่างการสืบค้นทั้งสองของ index( ) และ show( ) ให้ผลการสืบค้นเป็นอาร์เรย์ของ JSON ทั้งคู่ ซึ่งทำให้ไม่ต้องเปลี่ยนแปลงอะไรในหน้า course.blade.php แต่ถ้าการสืบค้นออกมาไม่เป็นอาร์เรย์ เช่นการสืบค้นการเลือกของการเลือกเพียง id ตัวใดตัวหนึ่ง ก็ต้องแปลงให้ผลเป็นอาร์เรย์ เช่น
'courses' => array(CourseModel::find($id));
ให้ทดลองดูผลว่าข้อมูลที่ได้เป็น JSON หรือไม่ โดยการเปิดการทำงานของบรรทัดที่เปิดการทำงานไว้ (//)
ทดสอบอีกครั้ง ด้วยการแทรกข้อมูล โดยเลือกใช้ฟังก์ชัน store( ) ในคอนโทรลเลอร์นี้ ฟังก์ชันนี้รองรับการทำงานแบบ POST (เราอาจไม่ต้องทำตามรูปแบบนี้ก็ได้) โดยเมื่อแทรกข้อมูลเสร็จก็ให้กลับไปหน้าเดิม เพื่อแสดงผลการแทรก
public function store(Request $request)//POST accept
{
$id = $request->input('id');
$name = $request->input('name');
$description = $request->input('description');
$active = $request->input('active');
DB::insert(
"insert into courses(id, name, description, active) values(?,?,?,?)",
[$id, $name, $description, $active]
);
return view('course', [
'courses' => DB::select('select * from courses')
]);
}
ต่อมาต้องสร้างฟอร์มให้กรอกข้อมูล ก่อนจะส่งไปในรูปแบบ POST โดยวางฟอร์มไว้ในหน้าเดียวกับที่อ่านตาราง
<div style="border:solid 1px gray;border-radius:10px;">
<form method='POST' action="/courses" class="row g-3">
@csrf
<div class='row' style='padding:30px 10px 0 30px'>
<div class="col" >
<input type='number' name='id' placeholder='id'
class="form-control-plaintext">
</div>
<div class="col">
<input type='text' name='name'class="form-control-plaintext"
placeholder='Name'>
</div>
<div class="col">
<input type='text' name='description' class="form-control-plaintext"
placeholder='Description'>
</div>
<div class="col">
<input type='text' name='active'class="form-control-plaintext"
placeholder='Active'>
</div>
<div class="col">
<button type='submit' class="btn btn-primary mb-3">
Insert
</button>
</div>
</div>
</form>
</div>
และไม่ลืมสร้างเส้นทางสำหรับการ POST นี้ด้วย
Route::post('/courses', [CourseController::class, 'store']);
ได้เห็นตัวอย่างไปหลายตัวอย่างแล้ว สำหรับการ ลบ การปรับปรุงข้อมูลก็ทำในทำนองเดียวกัน
DB::table()
นอกจากใช้โมเดลค่าเริ่มต้นในการสืบค้นแล้วยังสามารถใช้ คลาส DB เป็นจุดเริ่มต้นได้ด้วย และตามด้วยฟังก์ชัน table() ในการสืบค้นต่อไป การใช้ DB จะต้องนำเข้า (use) ด้วย
use Illuminate\Support\Facades\DB;
$courses = DB::table('courses')->get();
$course_java = DB::table('courses')->where('name', 'Java')->get();
ในบางกรณีผลการสืบค้นต้องการเพียงค่าเดียว เพียงออบเจ็กต์เดียว ผลการสืบค้นนี้จึงไม่ได้อยู่ในรูปอาร์เรย์ การอ่านค่าจึงใช้การอ่านค่าจากออบเจ็กต์โดยตรง ไม่ใช่การวนซ้ำตามที่เคยทำมา
การสืบค้นที่ได้ค่าเดียวมีอยู่ด้วยกันหลายฟังก์ชัน ยกตัวอย่างการใช้ first() จะเป็นการหาค่าแรกที่ค้นพบ การใช้ฟังก์ชัน find($id) เป็นการหาค่าตาม คีย์หลัก ซึ่งจะต้องมีค่าตัวเลข
$course_java_first = DB::table('courses')->where('name','Java')->first();
$course_id_1 = DB::table('courses')->find(1);
และกรณีที่หาค่าเป็นเดี่ยว คือไม่เป็นอาร์เรย์ และไม่เป็นแถว พบในคำสั่ง SQL เช่น การนับ Count, Min, Max ค่าประเภทนี้เป็นค่าตัวเลข หรือเรียกใช้ชื่อ Aggregate
ตาราง 1. Aggregateฟังก์ชัน | ความหมาย | ตัวอย่าง |
---|---|---|
count | นับจำนวนแถว | DB::table(‘book’)->count(); |
max | ค่าสูงสุด | DB::table(‘book’)->max(‘price’); |
min | ค่าต่ำสุด | DB::table(‘book’)->max(‘min’); |
avg | ค่าเฉลี่ย | DB::table(‘book’)->avg(‘price’); |
sum | ค่าผลรวม | DB::table(‘book’)->where(‘category’,’computer’)->avg(‘price’); |
การใช้งาน Eloquent
จากตัวอย่างทั้งหมดถือเป็นจุดเริ่มต้นที่ใช้งานฐานข้อมูลในแบบของ Eloquent ซึ่งเป็นรูปแบบ Object-relational mapper (ORM) โดยการสืบทอดจาก Model ที่เพิ่มเติมข้อมูลให้ตรงกับตารางฐานข้อมูล ซึ่งต่อไปทำให้ สืบค้น แทรก ปรับปรุง และลบ จากฐานข้อมูลได้
ในการใช้งาน Eloquent จะต้องสร้างคลาสที่สืบทอดจาก Model และกำหนดค่าดังนี้
ตาราง 2. การกำหนดคุณสมบัติใน Modelคุณสมบัติใน Model | ตัวอย่าง |
---|---|
ชื่อฐานข้อมูลจะใช้ชื่อตามค่ากำหนดไว้ใน .env แต่ถ้าต้องการใช้ชื่อฐานข้อมูลที่ต่าง ออกไป | protected $connection = 'sqlite'; |
ชื่อตาราง ต้องกำหนดว่าชื่ออะไร | protected $table = 'table_name'; |
ตามปกติคีย์ของตารางจะเป็น id แต่ถ้าไม่ใช้ต้องกำหนดใหม่ | protected $primaryKey = 'myId'; |
ตามปกติคีย์ของตาราง จะเป็นค่าเพิ่มขึ้นอัตโนมัติ แต่ถ้าไม่เป็นเช่นนั้นให้เขียน | public $incrementing = false; |
แต่ถ้าคีย์ของตารางไม่ได้เป็นตัวเลขเช่น เป็นอักษร ก็ต้องกำหนด: | protected $keyType = 'string'; |
ตารางอาจมีคีย์หลายตัว ก็กำหนดแต่ละตัวเป็น primary key ได้ | protected $primaryKey = 'primary key 1'; protected $primaryKey = 'primary key 2'; |
คอลัมน์ที่มีข้อมูลให้อัตโนมัติ แต่ต้องสร้างในฐานข้อมูลด้วย มีไทป์ เป็น TimeStamp แต่ถ้าต้องการให้มีชื่อคอลัมน์เป็นอย่างอื่น ก็กำหนดใหม่ | created_at updated_at const CREATED_AT = 'create_date'; const UPDATED_AT = 'update_date'; |
สร้างการสืบค้นของ Eloquent
การสืบค้นแรกที่เราได้เคยทำไว้แล้วคือ all( ) ยังมีวิธีอื่นสร้างคำสั่งสืบค้นได้
find($id)
การสืบค้นที่คืนด้วยการหาจาก id ซึ่งเป็นคีย์หลักของตาราง เราสามารถใช้คำสั่ง find($id) ได้ จากตัวอย่าง Code 11 ในฟังก์ชัน show($id) เราสามารถเปลี่ยนมาใช้ การคืนค่าเป็นอาร์เรย์ได้
'courses'=> array(CourseModel::find($id))
//or
//'courses' => [CourseModel::find($id))]
แต่ถ้าต้องการให้คืนค่าเป็นค่าออบเจ็กต์ JSON ค่าเดียวใช้
//return single json
return CourseModel::find($id);
ผลการคืนค่าจะเป็น
{
"id": 2,
"name": "C++",
"description": "Programming",
"active": 0
}
where()
การสืบค้นด้วย where( ) ใช้กับ โมเดลซึ่งทำได้หลากลายมาก คล้ายคำสั่งของ SQL ผลการสืบค้นอยู่ในรูปอาร์เรย์ของออบเจ็กต์ ดังนั้นจึงต้องใช้การวนซ้ำในการอ่านค่า
ตัวอย่างต่อไปนี่้ ใช้การสืบค้นของโมเดล CourseModel ที่เคยได้ทดลองทำก่อนหน้านี้ การสืบค้นใช้เงื่อนไขต่าง ๆ อย่างแรกใช้ where โดยกำหนดชื่อฟิลด์ active ให้มีค่าเท่ากับ 1 ต่อมาจัดเรียงตาม ด้วย orderBy ให้ผลการเรียงตาม name และเงื่อนไขสุดท้ายใช้ take โดยให้นำเพียงแถวแรกที่เลือกได้เท่านั้น สุดท้ายผลการสืบค้นใช้ฟังก์ชัน get( )
$courses = CourseModel::where('active',1)
->orderBy('name')
->take(2)
->get();
refresh( )
จากกรณีที่อ่านข้อมูล ดังตัวอย่างที่ผ่านมาของ $courses แล้ว ถ้าต้องการทำงานซ้ำอีกครั้ง เราใช้เพียง refresh( ) จะทำให้สืบค้นข้อมูลจาก Model อีกครั้ง
$courses->refresh();
แต่จะไม่มีผลกับการแก้ไขข้อมูลของออบเจ็กต์ เช่น กำหนดค่าข้อมูลใหม่ของ $courses เช่น
$courses[0].active = 0;
$courses->refresh();
ผลการ refresh() จะทำให้ไปสืบค้นค่าเดิม ที่มี active=1 อีกครั้ง ซึ่งมาจากฐานข้อมูล
การแก้ไขตารางข้อมูล
การแทรกข้อมูลใหม่ ไม่จำเป็นต้องย้อนไปอ่านแบบจำลองใหม่ เราสามารถที่จะสร้าง Model เป็นออบเจ็กต์ตัวใหม่แล้วใช้เพียงฟังก์ชัน save()
$course = new CourseModel();
$course->name = 'ABC';
$course->save();
//หรือใช้ผ่านฟังก์ชัน create
$course = Flight::create([
'id'=>4, 'name'=>'ABC',
]);
การลบข้อมูล สามารถใช้ผ่านการค้นก่อนแล้วต่อด้วยฟังก์ชัน delete() หรือใช้ร่วมกับฟังก์ชัน where
$course = CourseModel::find(1);
$course->delete();
$delete = CourseModel::where('active', 0)->delete();
การปรับปรุงข้อมูล ใช้การค้นข้อมูลก่อนแล้วตามด้วยฟังก์ชัน save()
$course = CourseModel::find(1);
$course->name = 'CBA';
$course->save();
หรือใช้ผ่านฟังก์ชัน update( ) ก็ได้ แต่วิธีการนี้ใช้ได้กับการปรับปรุงข้อมูลหลาย ๆ แถวได้ ตามผลการสืบค้น
CourseModel::where('active', 1) ->update(['active'=> 0]);
Soft Delete
ในการทดสอบการใช้งานฐานข้อมูล ไม่ได้ต้องการลบฐานข้อมูลจริง เราสามารถทำได้ด้วยการใช้ Soft Delete วิธีการคือต้องเพิ่มคอลัมน์ deleted_at และ updated_at ในตารางฐานข้อมูลและมีไทป์เป็น DateTime (default=Null) โดยไม่จำเป็นต้องระบุสองฟิลด์นี้ใน Model และเพิ่มเติมคีย์เวิร์ด SoftDelete
use Illuminate\Database\Eloquent\SoftDeletes;
class CourseModel extends Model
{
use SoftDeletes; //ข้อมูลที่เพิ่มเข้าไป
//และข้อมูลก่อนหน้านี้
}
เมื่อเราใช้คำสั่งลบ จะไม่เป็นการลบไปจากฐานข้อมูลจริง แต่ถ้าเราสืบค้นก็จะไม่พบ ข้อมูลที่ใช้ Soft Delete ไปแล้ว แต่ต้องการสืบค้นให้พบค่าที่ใช้ Soft Delete ลบไปแล้วก็ต้องใช้คำสั่ง:
$course = CourseModel::withTrashed()->where('active', 0)->get();
ถ้าต้องการอ่านข้อมูลกลับมา ก็ใช้คำสั่ง restore() ซึ่งมีผลให้ตารางข้อมูล ฟิลด์ delete_at มีค่าเป็น Nullเช่น
CourseModel::where('active',0)->restore();
หรือจะต้องการลบไปจริง ๆ จากฐานข้อมูล ก็ใช้ forceDelete( ):
CourseModel::where('active',0)->forceDelete();
การปรับปรุงข้อมูล ใช้การค้นข้อมูลก่อนแล้วตามด้วยฟังก์ชัน save()
$course = CourseModel::find(1);
$course->name = 'CBA';
$course->save();
หรือใช้ผ่านฟังก์ชัน update( ) ก็ได้ แต่วิธีการนี้ใช้ได้กับการปรับปรุงข้อมูลหลาย ๆ แถวได้ ตามผลการสืบค้น
CourseModel::where('active', 1) ->update(['active'=> 0]);
Factory
เมื่อต้องการทดสอบ หรือใช้งานอย่างรวดเร็ว เราไม่จำเป็นต้องระบุข้อมูลอย่างครบด้านเหมือนที่ทำก่อนหน้านี้ก็ได้ รวมทั้งยังไม่มีฐานข้อมูลจริงมาก่อนก็ได้ ด้วยการใช้งานแบบ Factory จะเป็นการระบุข้อมูลตัวอย่างอย่างง่าย ๆ ก่อนอื่นต้องสร้างไฟล์ สมมุติให้ชื่อ Book เป็น Model และ BookFactory เป็นตัวแบบ Factory สร้างสองอย่างด้วยคำสั่ง
php artisan make:model Book --factory
หรือต้องการให้มีคำว่า Model ต่อท้าย
php artisan make:model ฺBookModel --factory
แต่ถ้าต้องการสร้างเฉพาะ Factory
php artisan make:factory BookModelFactory
ภายใน BookFactory สร้างหนังสือหลอก ๆ มาก่อน ตามรายการเพิ่มเติม ให้มีข้อมูล id, title, price, และ year
private static $nextId = 0;
public function definition(): array
{
self::$nextId++;
return [
'id'=>self::$nextId,
'title'=>substr(fake()->text(),0, 6),
'price'=>rand(80, 400),
'year'=>rand(2018, 2023),
];
}
สำหรับไฟล์ Book.php เป็นคลาสที่ต้องการใช้ BookFactory ให้ใส่การใช้ HasFactory แค่นี้ถือเป็นใช้ได้ โดยไม่ต้องนิยามอะไรเพิ่มเติมอีก เพราะจะใช้งานให้มีฟิลด์ตาม BookFactory
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
}
เราจะทดสอบการสร้างออบเจ็กต์ $books สัก 4ตัวผ่าน BookController ในฟังก์ชัน __invoke( ) แต่มีการสร้างค่าเริ่มต้นที่ __construct( ) มาก่อน
use Illuminate\Http\Request;
use App\Models\Book;
class BookController extends Controller
{
private $book;
public function __construct(){
$this->book = Book::factory()->count(4)->make();
}
public function __invoke(){
return $this->book;
}
}
ฟังก์ชัน __invoke( ) เป็นฟังก์ชันปริยายที่ใช้งานผ่าน Route โดยไม่จำเป็นต้องระบุชื่อฟังก์ชัน ดังเขียนเส้นทางการอ่านค่า book คือ
use App\Http\Controllers\BookController;
Route::get('/books', BookController::class);
สร้างข้อมูล factories ลงฐานข้อมูล
ในกรณีที่ใช้ factories สร้างลงฐานข้อมูลจริง เราจะใช้ข้อมูลหลอก ๆ หรือข้อมูลจริงก็ได้ เพียง เปลี่ยนฟังก์ชันจาก make( ) ไปเป็น create( ) แต่ในฐานข้อมูลจริงจะต้องมีฟิลด์ created_at และ updated_at ที่มีไทป์ เป็น date หรือ datetime
สมมุติว่าจะใช้ factories สร้างลงฐานข้อมูลแต่ไม่ใช้ข้อมูลหลอก เราเพียงเพิ่มรายการแต่ละฟิลด์ใหม่ เช่น
Book::factory()->create([
'id'=>8, 'title'=>'C++', 'price'=>200, 'year'=>2023
]);
ยังมีฟังก์ชันการทำงานอีกมากมาย อ่านเพิ่มเติมได้ที่
- DB builder: https://laravel.com/docs/9.x/queries
- Eloquent: https://laravel.com/docs/9.x/eloquent