說明:本文主要學習Schema Builder和Migration System的使用及相關原理。傳統上在設計database時須要寫大量的SQL語句,但Laravel提供了Schema Builder這個神器使得在設計database時使用面向對象方法來作,不須要寫一行SQL,而且還提供了另外一個神器Migration System,能夠對database作版本控制,包括回滾上一次的遷移操做。本小系列主要分爲上中下三篇。本篇主要學習使用Schema Builder來creating,dropping,updating tables;adding,removing,renaming columns;simple index,unique index,foreign keys
,同時也會學習相關源碼來進一步瞭解Schema Builder。php
開發環境: Laravel5.3 + PHP7
mysql
在設計database時須要建立、刪除和更新表,Schema Builder類提供了一些methods來面向對象的執行這些操做,而不須要寫一行SQL。在寫Laravel程序時,也常常使用相似命令php artisan make:migration create_accounts_table --create=accounts
來作一個遷移類建立數據表,會在database/migrations
文件夾下獲得相似以下的代碼類:laravel
use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAccountsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('accounts', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('accounts'); } }
在執行php artisan migrate
命令時操做的是up()
方法中語句。在建立的遷移類CreateAccountsTable
中,Schema::create()
就是建立表的語句,而且第一個參數就是表的名字,第二個參數是個閉包,是操做columns的語句,而且參數是個Blueprint對象。爲何有這麼奇怪的寫法呢?sql
看下Schema Facade中getFacadeAccessor
的源碼:json
/** * Get a schema builder instance for the default connection. * * @return \Illuminate\Database\Schema\Builder */ protected static function getFacadeAccessor() { // 這裏'db'服務是在DatabaseServiceProvider中定義的,是DatabaseManager對象,且laravel默認connection是mysql // 則返回的是MysqlBuilder,也就是\Illuminate\Database\Schema\Builder的子類 return static::$app['db']->connection()->getSchemaBuilder(); }
根據註釋就知道Schema::create
就是等同於MysqlBuilder::create()
,看下源碼:api
// \Illuminate\Database\Schema\Builder /** * Create a new table on the schema. * * @param string $table * @param \Closure $callback * @return \Illuminate\Database\Schema\Blueprint */ public function create($table, Closure $callback) { /** @var \Illuminate\Database\Schema\Blueprint $blueprint */ $blueprint = $this->createBlueprint($table); // 添加'create'命令 $blueprint->create(); // 執行閉包裏的操做,也就是操做columns $callback($blueprint); $this->build($blueprint); } protected function createBlueprint($table, Closure $callback = null) { if (isset($this->resolver)) { return call_user_func($this->resolver, $table, $callback); } return new Blueprint($table, $callback); } // Illuminate\Database\Schema\Blueprint public function create() { return $this->addCommand('create'); } protected function build(Blueprint $blueprint) { $blueprint->build($this->connection, $this->grammar); }
create()
中的$callback($blueprint);
語句執行了閉包操做,而且閉包參數仍是個Blueprint對象。最關鍵的方法時Blueprint對象的build()
方法,下文再聊具體細節。閉包
固然,Schema Builder是能夠在任意模塊中使用的,如在路由中使用,執行https://localhost:8888/create_accounts
就能夠建立一個accounts
表了:app
Route::get('create_accounts', function () { \Illuminate\Support\Facades\Schema::create('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('number'); $table->tinyInteger('status'); $table->enum('source', ['bank account', 'credit card', 'investment account']); $table->timestamps(); }); });
Schema Builder提供了兩個方法來刪除表:drop(string $table)
和dropIfExists(string $table)
,參數是表名,dropIfExists()
表示只有在表存在才刪除,因此dropIfExists()
比drop()
更優雅。看下兩個方法的源碼:ide
/** * Drop a table from the schema. * * @param string $table * @return \Illuminate\Database\Schema\Blueprint */ public function drop($table) { $blueprint = $this->createBlueprint($table); $blueprint->drop(); $this->build($blueprint); } /** * Drop a table from the schema if it exists. * * @param string $table * @return \Illuminate\Database\Schema\Blueprint */ public function dropIfExists($table) { $blueprint = $this->createBlueprint($table); $blueprint->dropIfExists(); $this->build($blueprint); } // Illuminate\Database\Schema\Blueprint public function drop() { return $this->addCommand('drop'); } public function dropIfExists() { return $this->addCommand('dropIfExists'); }
一樣是使用了Blueprint對象來添加命令drop
和dropIfExists
。在路由中刪除accounts
表:函數
Route::get('delete_accounts', function () { \Illuminate\Support\Facades\Schema::dropIfExists('accounts'); // \Illuminate\Support\Facades\Schema::drop('accounts'); });
更新表的操做包括更新表名和更新表字段。
使用Schema::rename($from, $to)
方法來更新表名:
Route::get('rename_bank_accounts', function () { \Illuminate\Support\Facades\Schema::rename('accounts', 'bank_accounts'); });
看下Schema的rename()
源碼,一樣是使用Blueprint對象添加rename
命令:
public function rename($from, $to) { $blueprint = $this->createBlueprint($from); $blueprint->rename($to); $this->build($blueprint); } // Illuminate\Database\Schema\Blueprint public function rename($to) { return $this->addCommand('rename', compact('to')); }
使用Schema::table()
方法來更新表字段值,如更新accounts
的number
字段,,不過若是該表中有字段類型爲enum
就不支持修改:
Route::get('update_accounts', function () { \Illuminate\Support\Facades\Schema::table('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->string('number', 50)->change(); }); });
同時,Schema還提供了幾個有用的方法,如hasTable($table),hasColumn($table, $column),hasColumns($table, array $column),getColumnListing($table)
:
Route::get('get_column_listing', function () { // if (\Illuminate\Support\Facades\Schema::hasTable('accounts')) // if (\Illuminate\Support\Facades\Schema::hasColumn('accounts', 'name')) if (\Illuminate\Support\Facades\Schema::hasColumns('accounts', ['name'])) { // ['id', 'name', 'number', 'status', 'source', 'created_at', 'updated_at'] return \Illuminate\Support\Facades\Schema::getColumnListing('accounts'); } });
設計database時須要添加columns,上文說過這段邏輯是在Schema::create(),Schema::table()
的閉包裏執行的,利用Blueprint對象來依次添加每個column字段屬性和Mysql中數據類型對應,如:
\Illuminate\Support\Facades\Schema::create('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->string('name'); // => $this->addColumn('string', $column, compact('length')); });
經常使用的Schema字段類型函數表以下:
Schema Column Type | MySQL Column Type |
---|---|
bigIncrements('id') |
id UNSIGNED BIGINT |
bigInteger('number') |
number BIGINT |
binary('data') |
data BLOB |
boolean('is_viewed') |
is_viewed BOOLEAN |
char('title', 50) |
title CHAR(50) |
date('created_at') |
created_at DATE |
dateTime('updated_at') |
updated_at DATETIME |
decimal('amount', 2, 2) |
amount DECIMAL(2,2) |
double('length', 10, 10) |
length DOUBLE(10, 10) |
enum('source', ['fund', 'equity']) |
source ENUM('fund', 'equity') |
float('width', 5, 5) |
width FLOAT(5, 5) |
json('options') |
options JSON |
string('content') |
content VARCHAR(255) |
text('description') |
description TEXT |
... |
... |
``
Schema提供了dropColumn()
方法來刪除表中字段:
Route::get('drop_column', function () { \Illuminate\Support\Facades\Schema::table('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->dropColumn(['number', 'status']); }); });
Schema提供了renameColumn()
來修改column名稱,不過若是該表中有字段類型爲enum
就不支持修改:
Route::get('rename_column', function () { \Illuminate\Support\Facades\Schema::table('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->renameColumn('name', 'title'); }); });
Schema提供了index()
方法來給column加索引,且索引名稱約定爲table-name_column-name_index-type
:
Route::get('index_column', function () { \Illuminate\Support\Facades\Schema::table('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->index('name'); }); });
同時,Schema提供了dropIndex('table-name_column-name_index')
來刪除simple index。
Schema提供了unique()
方法來給column加索引,且索引名稱約定爲table-name_column-name_index-type
:
Route::get('unique_column', function () { \Illuminate\Support\Facades\Schema::table('accounts', function(\Illuminate\Database\Schema\Blueprint $table) { $table->unique(['title']); }); });
同時,Schema提供了dropUnique('table-name_column-name_unique')
來刪除unique index。
Schema提供了foreign()->reference()->on()
fluent api來設計foreign key,且索引名稱約定爲table-name_column-name_index-type
:
Route::get('foreign_key_column', function () { \Illuminate\Support\Facades\Schema::create('bills', function(\Illuminate\Database\Schema\Blueprint $table) { $table->increments('id'); $table->unsignedInteger('account_id'); $table->float('amount', 5, 5); $table->foreign('account_id') ->references('id') ->on('accounts') ->onUpdate('CASCADE') ->onDelete('CASCADE'); }); });
同時,Schema提供了dropForeign('table-name_column-name_foreign')
來刪除foreign index。
總結:本篇主要學習下Laravel使用了Schema和Blueprint兩個類來設計database,中篇將以Schema::create()爲例仔細研究下源碼是如何轉換爲SQL並執行SQL語句的,下篇聊下Migration System的使用及其原理。到時見。