Đăng bởi:
Phan Sỹ TânĐăng ngày:
Jan 30, 2021Đăng ở:
Tin Tức Công NghệHôm nay mình sẽ giới thiệu Polymorphic Relationship. Ở phần I này, mình sẽ làm một ví dụ để tiếp cận Polymorphic Relatiionship.
Như các bạn đã biết, Laravel framework hỗ trợ tính năng Eloquent Relationship cho các model nhằm dễ dàng thao tác với cơ sở dữ liệu. Mỗi loại relationship thể hiện mối quan hệ tương ứng với các bảng:
Với phiên bản 5.8 trở lên, Laravel đã hỗ trợ Polymorphic Relationships (quan hệ đa hình). Để tiếp cận, hãy xem ví dụ dưới đây.
Một cửa hàng bán những mặt hàng: Điện thoại, phụ kiện và linh kiện. Viết chức năng báo cáo bán hàng thông qua thông tin hoá đơn.
Để đơn giản hoá, thiết kế 4 bảng như sau:
public function up(){
Schema::create('phones', function (Blueprint $table) {
$table->id();
$table->string('name', 200)->nullable();
$table->string('note', 500)->nullable();
$table->timestamps();
});
Schema::create('fits', function (Blueprint $table) {
$table->id();
$table->string('name', 200)->nullable();
$table->string('note', 500)->nullable();
$table->timestamps();
});
Schema::create('accessories', function (Blueprint $table) {
$table->id();
$table->string('name', 200)->nullable();
$table->string('note', 500)->nullable();
$table->timestamps();
});
Schema::create('invoice_details', function (Blueprint $table) {
$table->id();
$table->integer('invoice_id')->unsigned()->nullable();
$table->string('product_type', 50)->nullable();
$table->integer('product_id')->unsigned()->nullable();
$table->integer('price')->default(0);
$table->integer('quantity')->default(0);
$table->timestamps();
});
}
public function run(){
DB::table('phones')->truncate();
DB::table('fits')->truncate();
DB::table('accessories')->truncate();
DB::table('invoice_details')->truncate();
DB::table('phones')->insert([
['id' => 1, 'name' => 'iPhone 11'],
['id' => 2, 'name' => 'Samsung Galaxy 8'],
['id' => 3, 'name' => 'Xiaomi MI 10'],
]);
DB::table('fits')->insert([
['id' => 1, 'name' => 'Tai nghe Airport 2'],
['id' => 2, 'name' => 'Bao da iPhone 11 KST'],
]);
DB::table('accessories')->insert([
['id' => 1, 'name' => 'Anten wifi iPad 2'],
['id' => 2, 'name' => 'Camera sau iPhone 11'],
['id' => 3, 'name' => ' Xương Samsung Galaxy A7 2018 Xanh'],
]);
DB::table('invoice_details')->insert([
['invoice_id' => 2, 'product_type' => 'phone', 'product_id' => 1, 'quantity' => 1, 'price' => 10000000],
['invoice_id' => 4, 'product_type' => 'fit', 'product_id' => 2, 'quantity' => 2, 'price' => 200000],
['invoice_id' => 5, 'product_type' => 'accessory', 'product_id' => 2, 'quantity' => 2, 'price' => 50000],
['invoice_id' => 7, 'product_type' => 'accessory', 'product_id' => 3, 'quantity' => 1, 'price' => 50000],
]);
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model
{
protected $guarded = ['id'];
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Fit extends Model
{
protected $guarded = ['id'];
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Accessory extends Model
{
protected $guarded = ['id'];
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InvoiceDetail extends Model
{
protected $guarded = ['id'];
public function fit(){
return $this->belongsTo(Fit::class, 'product_id');
}
public function phone(){
return $this->belongsTo(Phone::class, 'product_id');
}
public function accessory(){
return $this->belongsTo(Accessory::class, 'product_id');
}
}
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ReportController;
Route::get('reports', [ReportController::class, 'index']);
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\InvoiceDetail;
class ReportController extends Controller
{
public function index(){
$report = InvoiceDetail::with('phone', 'fit', 'accessory')->get();
return view('report.index', compact('report'));
}
}
@extends('layouts.master')
@section('title', 'Báo cáo bán hàng')
@section('content')
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover">
<tbody>
<tr>
<th class="text-center">#</th>
<th class="text-center">Hoá đơn</th>
<th class="text-center">ID sản phẩm</th>
<th class="text-center">Sản phẩm</th>
<th class="text-center">Đơn giá</th>
<th class="text-center">Số lượng </th>
<th class="text-center">Thành tiền</th>
</tr>
@php $index = 1; @endphp
@foreach ($report as $item)
<tr>
<td class="text-center">{{ $index++ }}</td>
<td class="text-center"><a href="#">#HĐ{{ $item->invoice_id }}</a></td>
<td class="text-center"><strong>{{$item->product_id}}</strong></td>
<td>
@switch($item->product_type)
@case('phone')
<span class="label label-info">Điện thoại</span>
{{ $item->phone->name }}
@break
@case('fit')
<span class="label label-success">Phụ kiện</span>
{{ $item->fit->name }}
@break
@case('accessory')
<span class="label label-warning">Linh kiện</span>
{{ $item->accessory->name }}
@break
@default
@endswitch
</td>
<td class="text-right">{{ format_price($item->price) }}</td>
<td class="text-center">{{ $item->quantity }}</td>
<td class="text-right">{{ format_price($item->price * $item->quantity) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
Với ví dụ ở mục 2, đã sử dụng mối quan hệ One To Many Relationship. Tại ReportController:
$report = InvoiceDetail::with('phone', 'fit', 'accessory')->get();
Các truy vấn SQL tương ứng như sau:
Các giá trị trong mệnh đề where… in là các “product_id” được tìm thấy trong câu lệnh:
select * from `invoice_details`
Các bạn chú ý vào điện thoại. kết quả hiển thị ra một điện thoại với ID là 1. Nhưng câu truy vấn lại là:
select * from `phones` where `phones`.`id` in (1, 2, 3)
Cho nên bản ghi của bảng phones có id 2 và 3 được truy vấn ra nhưng không được sử dụng. Nếu số lượng bản ghi trong bảng “invoice_details” càng lớn thì truy vấn càng lớn, lãng phí thời gian và bộ nhớ càng lớn.
Do đó ta nên sử dụng Polymorphic Relationship thay vì One To Many Relationship như hiện tại. Các bước thay đổi:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class InvoiceDetail extends Model
{
protected $guarded = ['id'];
public function product()
{
return $this->morphTo();
}
}
public function boot()
{
\Illuminate\Database\Eloquent\Relations\Relation::morphMap([
'phone' => \App\Models\Phone::class,
'fit' => \App\Models\Fit::class,
'accessory' => \App\Models\Accessory::class,
]);
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\InvoiceDetail;
class ReportController extends Controller
{
public function index(){
$report = InvoiceDetail::with('product')->get();
return view('report.index', compact('report'));
}
}
@extends('layouts.master')
@section('title', 'Báo cáo bán hàng')
@section('content')
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover">
<tbody>
<tr>
<th class="text-center">#</th>
<th class="text-center">Hoá đơn</th>
<th class="text-center">ID sản phẩm</th>
<th class="text-center">Sản phẩm</th>
<th class="text-center">Đơn giá</th>
<th class="text-center">Số lượng </th>
<th class="text-center">Thành tiền</th>
</tr>
@php $index = 1; @endphp
@foreach ($report as $item)
<tr>
<td class="text-center">{{ $index++ }}</td>
<td class="text-center"><a href="#">#HĐ{{ $item->invoice_id }}</a></td>
<td class="text-center"><strong>{{$item->product_id}}</strong></td>
<td>
@switch($item->product_type)
@case('phone')
<span class="label label-info">Điện thoại</span>
@break
@case('fit')
<span class="label label-success">Phụ kiện</span>
@break
@case('accessory')
<span class="label label-warning">Linh kiện</span>
@break
@default
@endswitch
{{ $item->product->name }}
</td>
<td class="text-right">{{ format_price($item->price) }}</td>
<td class="text-center">{{ $item->quantity }}</td>
<td class="text-right">{{ format_price($item->price * $item->quantity) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
Kết quả thu được:
Chúc các bạn thành công!
Demo: https://github.com/phongtrank55/demo-post/tree/poly-relation
Tham khảo: https://laravel.com/docs/5.8/eloquent-relationships
Bình luận
Để lại bình luận
Email và số điện thoại sẽ không được công khai. Những trường bắt buộc được đánh dấu *