larablog 專案使用的資料表
上圖是專案使用的資料表項目以及關係示意圖,以下是各資料表簡介:
- categories:分類資料表,儲存文章分類,透過「parent_id」項目指定上層分類。 關係設定:與 posts 資料表為一對多。
- posts:文章資料表,儲存網站文章資料,透過「state(發佈狀態)」及「featured(是否為精選)」做為文章顯示開關。 關係設定:「category_id」為從屬於 categories 資料表關聯外鍵;「user_id」為從屬於 users 資料表關聯外鍵;與 tags 資料表透過 post_tag 資料表中介實現多對多關聯。
- tags:標籤資料表,儲存文章會標記的標籤項目。 關係設定:與 posts 資料表透過 post_tag 資料表中介實現多對多關聯。
- post_tag:作為文章(posts)與標籤(tags)的中介資料表,命名方式依照 Laravel 的命名慣例為:單數,英文字母排列。
- comments:回應資料表,儲存網站訪客對文章的回應。 關係設定:「post_id」為從屬於 posts 資料表關聯外鍵。
- users:會員資料表,儲存網站會員資料。(以本專案的情況僅儲存管理者) 關係設定:與 posts 資料表為一對多,與 user_social_profiles 的關係為一對一。
- user_social_profiles:會員社群網站資料表,儲存文章詳細頁面中作者簡介、社群網站頁面資料。 關係設定:「user_id」為從屬於 users 資料表關聯外鍵。
建立 Model 的同時也建立 Migration
在建立 Model 的時候加上「-m」參數,會一併建立資料表的 Migration 檔案,以 Category 來說會是:
php artisan make:model Category -m
在建立 Model 時同時產生的 Migration 檔案,在資料表名稱部分會以複數命名。學過英文的人就知道字尾為「y」的名詞,其複數會是「去 y 加上 ies」,所以在新增「Category」這個 Model 時加上「-m」參數,那麼連帶產生的 Migration 檔案所要建立的資料表名稱會是「categories」而不是「categorys」,這會影響稍後的外鍵設定。
中介資料表
作為「posts」與「tags」中介的「post_tag」資料表,在命名規則部分與上述資料表不一樣:按哥布林老師的建議,作為多對多關係的中介資料表,資料庫名稱應為「單數,按英文字母順序排列」,建立 Migration 的指令如下:
php artisan make:migration create_post_tag_table
在 Migration 檔案中需定義對應「posts」與「tags」資料表的外鍵,在命名上的建議是「post_id」與「tag_id」。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostTagTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->id();
$table->foreignId('post_id')->constrained();
$table->foreignId('tag_id')->constrained();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('post_tag', function (Blueprint $table){
$table->dropForeign(['post_id']);
$table->dropForeign(['tag_id']);
});
Schema::dropIfExists('post_tag');
}
}
與會員有關的 Model 及相關的 Migration 檔案,在建立專案時就已經建立,之後再到 User Model 撰寫資料庫關聯內容。
資料表間有設定關聯時需注意建立順序
從資料表關係圖可以看出「posts」的「category_id」欄位是關聯「categories」資料表「id」欄位的外鍵,在 Migration 檔案的建立順序上應為:categories 先,posts 後。如果沒有按照前述順序在執行 Migration 會發生錯誤,此時可透過更改 Migration 檔名的時間流水號解決。
以新方法設定資料表關聯,和注意事項
Laravel 透過 Migration 檔案進行資料表管理,以下是 posts 資料表的 Migration 檔案內容:
<?php
// database/migrations/2021_09_08_151439_create_posts_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->bigInteger('category_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->string('title');
$table->string('slug')->unique();
$table->string('cover_image');
$table->string('introtext');
$table->text('content');
$table->bigInteger('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('sort')->default(0);
$table->enum('status',['pending', 'published', 'unpublished']);
$table->enum('featured',['yes','no']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('post', function (Blueprint $table){
$table->dropForeign(['category_id']);
$table->dropForeign(['user_id']);
});
Schema::dropIfExists('posts');
}
}
在 Migration 檔案中我定義了建立資料表的「up」方法,以及滾回(rollback)時刪除資料表的「down」方法。
在「up」方法中可以看到「category_id」欄位型態是「unsigned 的 bigInteger」,然後指定此欄位作為外鍵:與「categories」的「id」欄位做關聯,以同樣的格式定義「user_id」欄位。
如果建立資料表時有按照 Laravel 的建議格式,那麼可以透過「constrained()」簡化敘述,也就是說你可以將
$table->bigInteger('category_id')->unsigned();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
更改為
$table->foreignId('category_id')->constrained(); //外鍵設定新寫法
這樣 Laravel 會認為「category_id」欄位是做為與「categories」資料表的「id」欄位關聯的外鍵欄位,是不是比較簡單呢?
不過用新式寫法建立外鍵關聯要注意:Laravel 透過欄位名稱去推測外鍵關聯的資料庫及其欄位,如果你的資料表或(及)關聯欄位命名方式不是按照建議規則,那麼還是按照舊式規則。
在 Migration 檔案定義好要建立的資料表欄位及外鍵設定後透過以下指令執行:
php artisan migrate
在 Model 撰寫資料表關聯
在 Laravel 8,Model 檔案會放在 /app/Models 資料夾,完成資料表建立作業後接著編輯 Model 檔案,加上編輯欄位與關聯設定。
/app/Models/Category.php(文章分類)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $table = 'categories';
protected $fillable = [
'parent_id',
'name',
'description',
'slug',
'status',
'featured',
];
public function parentCategory(){
return $this->belongsTo('App\Models\Category');
}
public function posts(){
return $this->hasMany('App\Models\Post');
}
}
在 Category 類別中我定義了存取的資料表名稱以及填入資料的欄位項目,接著定義「parentCategory」方法定義上一層分類;「posts」方法則定義與「posts」資料表的一對多(hasMany)關係。
/app/Models/Post.php(文章)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use TCG\Voyager\Traits\Translatable;
class Post extends Model
{
use HasFactory, Translatable;
protected $table = 'posts';
protected $fillable = [
'category_id',
'title',
'slug',
'cover_image',
'introtext',
'content',
'user_id',
'sort',
'status',
'featured',
];
public function user(){
return $this->belongsTo('App\Models\User');
}
public function category(){
return $this->belongsTo('App\Models\Category');
}
public function tags(){
return $this->belongsToMany('App\Models\Tag')->withTimestamps();
}
public function comments(){
return $this->hasMany('App\Models\Comment');
}
}
我在「user」方法定義從屬(belongsTo)於「users」的一對多反向關係:一個使用者會有多篇文章,「category」方法的情況也一樣。
「tags」方法用來定義與「tags」資料表的多對多(belongsToMany)關係,加上「withTimestamps()」以在中介資料表新增資料時也填上建立時間。「comments」方法則定義與「comments」資料表的一對多(hasMany)關係:一篇文章會有多篇訪客回應。
/app/Models/Tag.php(標籤)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Cviebrock\EloquentSluggable\Sluggable;
class Tag extends Model
{
use HasFactory;
protected $table = 'tags';
protected $fillable = [
'name',
'slug',
'status',
'featured',
];
public function posts(){
return $this->belongsToMany('App\Models\Post');
}
}
與「posts」 資料表的多對多(belongsToMany)關係寫在「posts」方法:一篇文章會有多個標籤,而一個標籤可能會被多個文章標記。
/app/Models/Comment.php(訪客回應)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $table = 'comments';
protected $fillable = [
'post_id',
'name',
'email',
'website',
'comment',
'admin_reply',
];
public function posts(){
return $this->belongsTo('App\Models\Post')->withTimestamps();
}
}
「posts」方法定義與「posts」資料表的反向多對一關係。
/app/Models/User.php(會員)
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends \TCG\Voyager\Models\User
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function posts(){
return $this->hasMany('App\Models\Post');
}
public function user_social_profile(){
return $this->hasOne('App\Models\UserSocialProfile');
}
}
「posts」方法定義與「posts」資料表一對多關係;「user_social_profile」方法定義與「user_social_profiles」資料表的一對一(hasOne)關係:一個會員有一組社群網站資料
/app/Models/UserSocialProfile.php(會員社群網站資料)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class UserSocialProfile extends Model
{
use HasFactory;
protected $table = 'user_social_profiles';
protected $fillable = [
'user_id',
'avatar',
'profile',
'facebook',
'instagram',
'line',
'twitter',
'github',
];
public function user(){
return $this->belongsTo('App\Models\User');
}
}
「user」方法定義與「users」資料表的反向一對一關係。
結語
透過設定資料表間的關聯可以避免資料的重複性、減少輸入錯誤,連帶的還有批次變更的效果。在 Model 檔案中建立關係方法後可透過 Eloquent 語法存取關聯資料表間的資料欄位,讓專案的規模可以做得更大。
瞭解並活用資料表關聯是我在這個專案中想實現的目標,在撰寫的當下其實還不夠熟練,要向哥布林老師多多請教,也請觀看此文章的朋友多多關注哥布林老師的 Laravel 百萬年薪訓練營 以及 Laravel Care 計畫 喔。
居住在臺灣的 Joomler,期望以程式設計、開放原碼推廣活動收入養活一家老小。
35 歲後改姓李,id 作為曾為郭姓的證明。
FFXI:Abokuo@Sylph鯖、よろしくです。
darkmarket deep web sites how to get on dark web