摘要:看下两个方法的源码同样是使用了对象来添加命令和。
说明:本文主要学习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。
开发环境: Laravel5.3 + PHP7
表的操作-tables在设计database时需要创建、删除和更新表,Schema Builder类提供了一些methods来面向对象的执行这些操作,而不需要写一行SQL。在写Laravel程序时,也经常使用类似命令php artisan make:migration create_accounts_table --create=accounts来做一个迁移类创建数据表,会在database/migrations文件夹下得到类似如下的代码类:
</>复制代码
use IlluminateSupportFacadesSchema;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
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");
}
}
(1)creating tables
在执行php artisan migrate命令时操作的是up()方法中语句。在创建的迁移类CreateAccountsTable中,Schema::create()就是创建表的语句,并且第一个参数就是表的名字,第二个参数是个闭包,是操作columns的语句,并且参数是个Blueprint对象。为什么有这么奇怪的写法呢?
看下Schema Facade中getFacadeAccessor的源码:
</>复制代码
/**
* Get a schema builder instance for the default connection.
*
* @return IlluminateDatabaseSchemaBuilder
*/
protected static function getFacadeAccessor()
{
// 这里"db"服务是在DatabaseServiceProvider中定义的,是DatabaseManager对象,且laravel默认connection是mysql
// 则返回的是MysqlBuilder,也就是IlluminateDatabaseSchemaBuilder的子类
return static::$app["db"]->connection()->getSchemaBuilder();
}
根据注释就知道Schema::create就是等同于MysqlBuilder::create(),看下源码:
</>复制代码
// IlluminateDatabaseSchemaBuilder
/**
* Create a new table on the schema.
*
* @param string $table
* @param Closure $callback
* @return IlluminateDatabaseSchemaBlueprint
*/
public function create($table, Closure $callback)
{
/** @var IlluminateDatabaseSchemaBlueprint $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);
}
// IlluminateDatabaseSchemaBlueprint
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表了:
</>复制代码
Route::get("create_accounts", function () {
IlluminateSupportFacadesSchema::create("accounts", function(IlluminateDatabaseSchemaBlueprint $table)
{
$table->increments("id");
$table->string("name");
$table->string("number");
$table->tinyInteger("status");
$table->enum("source", ["bank account", "credit card", "investment account"]);
$table->timestamps();
});
});
(2)dropping tables
Schema Builder提供了两个方法来删除表:drop(string $table)和dropIfExists(string $table),参数是表名,dropIfExists()表示只有在表存在才删除,所以dropIfExists()比drop()更优雅。看下两个方法的源码:
</>复制代码
/**
* Drop a table from the schema.
*
* @param string $table
* @return IlluminateDatabaseSchemaBlueprint
*/
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 IlluminateDatabaseSchemaBlueprint
*/
public function dropIfExists($table)
{
$blueprint = $this->createBlueprint($table);
$blueprint->dropIfExists();
$this->build($blueprint);
}
// IlluminateDatabaseSchemaBlueprint
public function drop()
{
return $this->addCommand("drop");
}
public function dropIfExists()
{
return $this->addCommand("dropIfExists");
}
同样是使用了Blueprint对象来添加命令drop和dropIfExists。在路由中删除accounts表:
</>复制代码
Route::get("delete_accounts", function () {
IlluminateSupportFacadesSchema::dropIfExists("accounts");
// IlluminateSupportFacadesSchema::drop("accounts");
});
(3)updating tables
更新表的操作包括更新表名和更新表字段。
使用Schema::rename($from, $to)方法来更新表名:
</>复制代码
Route::get("rename_bank_accounts", function () {
IlluminateSupportFacadesSchema::rename("accounts", "bank_accounts");
});
看下Schema的rename()源码,同样是使用Blueprint对象添加rename命令:
</>复制代码
public function rename($from, $to)
{
$blueprint = $this->createBlueprint($from);
$blueprint->rename($to);
$this->build($blueprint);
}
// IlluminateDatabaseSchemaBlueprint
public function rename($to)
{
return $this->addCommand("rename", compact("to"));
}
使用Schema::table()方法来更新表字段值,如更新accounts的number字段,,不过如果该表中有字段类型为enum就不支持修改:
</>复制代码
Route::get("update_accounts", function () {
IlluminateSupportFacadesSchema::table("accounts", function(IlluminateDatabaseSchemaBlueprint $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 (IlluminateSupportFacadesSchema::hasTable("accounts"))
// if (IlluminateSupportFacadesSchema::hasColumn("accounts", "name"))
if (IlluminateSupportFacadesSchema::hasColumns("accounts", ["name"]))
{
// ["id", "name", "number", "status", "source", "created_at", "updated_at"]
return IlluminateSupportFacadesSchema::getColumnListing("accounts");
}
});
字段的操作-column
(1)adding columns
设计database时需要添加columns,上文说过这段逻辑是在Schema::create(),Schema::table()的闭包里执行的,利用Blueprint对象来依次添加每一个column字段属性和Mysql中数据类型对应,如:
</>复制代码
IlluminateSupportFacadesSchema::create("accounts", function(IlluminateDatabaseSchemaBlueprint $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 |
... | ... |
``
(2)removing columnsSchema提供了dropColumn()方法来删除表中字段:
</>复制代码
Route::get("drop_column", function () {
IlluminateSupportFacadesSchema::table("accounts", function(IlluminateDatabaseSchemaBlueprint $table)
{
$table->dropColumn(["number", "status"]);
});
});
(3)renaming columns
Schema提供了renameColumn()来修改column名称,不过如果该表中有字段类型为enum就不支持修改:
</>复制代码
Route::get("rename_column", function () {
IlluminateSupportFacadesSchema::table("accounts", function(IlluminateDatabaseSchemaBlueprint $table)
{
$table->renameColumn("name", "title");
});
});
索引操作-index
(1)simple index
Schema提供了index()方法来给column加索引,且索引名称约定为table-name_column-name_index-type:
</>复制代码
Route::get("index_column", function () {
IlluminateSupportFacadesSchema::table("accounts", function(IlluminateDatabaseSchemaBlueprint $table)
{
$table->index("name");
});
});
同时,Schema提供了dropIndex("table-name_column-name_index")来删除simple index。
(2)unique indexSchema提供了unique()方法来给column加索引,且索引名称约定为table-name_column-name_index-type:
</>复制代码
Route::get("unique_column", function () {
IlluminateSupportFacadesSchema::table("accounts", function(IlluminateDatabaseSchemaBlueprint $table)
{
$table->unique(["title"]);
});
});
同时,Schema提供了dropUnique("table-name_column-name_unique")来删除unique index。
(3)foreign keySchema提供了foreign()->reference()->on() fluent api来设计foreign key,且索引名称约定为table-name_column-name_index-type:
</>复制代码
Route::get("foreign_key_column", function () {
IlluminateSupportFacadesSchema::create("bills", function(IlluminateDatabaseSchemaBlueprint $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的使用及其原理。到时见。
RightCapital招聘Laravel DevOps
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/21962.html
摘要:说明本文主要学习模块的源码。这里,就已经得到了链接器实例了,该中还装着一个,下文在其使用时再聊下其具体连接逻辑。 说明:本文主要学习Laravel Database模块的Query Builder源码。实际上,Laravel通过Schema Builder来设计数据库,通过Query Builder来CURD数据库。Query Builder并不复杂或神秘,只是在PDO扩展的基础上又开...
摘要:而且,与是一对多关系一个分类下有很多,一个只能归属于一个与是一对多关系一篇博客下有很多,一条只能归属于一篇与是多对多关系一篇有很多,一个下有很多。 说明:本文主要聊一聊Laravel测试数据填充器Seeder的小技巧,同时介绍下Laravel开发插件三件套,这三个插件挺好用哦。同时,作者会将开发过程中的一些截图和代码黏上去,提高阅读效率。 备注:在设计个人博客软件时,总会碰到有分类Ca...
摘要:抱歉,最近忙,本篇等有时间更新。引言本文基于框架做的一个生成和存储,主要目的是学习使用框架。书籍基于的,学习时使用框架开发。开发环境备注一直想把这本书的个作为系列分享出来,供初学者学习玩玩。 抱歉,最近忙,本篇等有时间更新。 引言 本文基于Laravel框架做的一个URL生成和存储demo,主要目的是学习使用Laravel框架。内容基于英文书籍《Packt.Laravel.Applic...
摘要:说明本文主要讲述使用作为缓存加快页面访问速度。何不用来做缓存,等到该达到一定浏览页面后再刷新下,效率也很高。可作缓存系统队列系统。 说明:本文主要讲述使用Redis作为缓存加快页面访问速度。同时,作者会将开发过程中的一些截图和代码黏上去,提高阅读效率。 备注:作者最近在学习github上别人的源码时,发现好多在计算一篇博客页面访问量view_count时都是这么做的:利用Laravel...
阅读 3584·2023-04-26 00:05
阅读 969·2021-11-11 16:55
阅读 3556·2021-09-26 09:46
阅读 3536·2019-08-30 15:56
阅读 926·2019-08-30 15:55
阅读 2953·2019-08-30 15:53
阅读 1968·2019-08-29 17:11
阅读 831·2019-08-29 16:52