短網址服務是個簡單的小型服務,它可以考驗一些基本的資料庫操作(新增及查詢)。
其實短網址服務可以是個水很深的專案,從短碼的生成方式、Redirector Server 的設計、流量統計等等都是可以深入探討的主題。
我們在這個篇章中不會探討這麼深入的主題,僅以純 Laravel 的短網址服務作為範例。
$ laravel new url-shorter
要求使用 SQLite,所以先設定好 SQLite 的連線。
<?php
// config/database.php
return [
'default' => env('DB_CONNECTION', 'sqlite'); // 這裡改成 SQLite
'connections' => [
// ...
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
// ...
],
// ...
];
我們可以看到在 connections.sqlite.database
中的值是 env('DB_DATABASE', database_path('database.sqlite'))
也就是說,在完全不設定的前提下,Laravel 會使用 database/database.sqlite
這個檔案作為 SQLite 儲存的位置。
首先,要先建立 SQLite 的儲存檔
$ touch database/database.sqlite
記得移除 .env
中的 DB_CONNECTION
, DB_HOST
, DB_PORT
, DB_DATABASE
, DB_USERNAME
, DB_PASSWORD
值,如此才會套用 config/database.php
中的預設值。
我們可以嘗試執行 migrations 測試是否能夠正常連線
$ php artisan migrate # 執行 Migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (3.72ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (2.78ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (2.89ms)
$ php artisan migrate:reset # 重置 Migrate
Rolling back: 2019_08_19_000000_create_failed_jobs_table
Rolled back: 2019_08_19_000000_create_failed_jobs_table (2.84ms)
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_100000_create_password_resets_table (1.22ms)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back: 2014_10_12_000000_create_users_table (1.26ms)
因為 Laravel 有預設的 Migrations,會自動幫忙先建立 users, password_resets 及 failed_jobs,這在目前的我們是多餘的。
$ rm database/migrations/*.php
利用 Laravel Make Model 的功能,我們可以建立 URL Model 及其對應的 migrations。
$ php artisan make:model -m Url
其中的 -m
參數是用於順便建立 urls
Table 的 Migration。
<?php
// database/migrations/xxxx_xx_xx_xxxxxx_create_urls_table.php
// ...
class CreateUrlsTable extends Migration
{
public function up()
{
Schema::create('urls', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('urls');
}
}
$table->id()
用於 Primary Key 的儲存,Laravel 預設是 Big Integer + Auto Increment$table->string('url')
建立一個名為 url
的 column,用於儲存網址$table->timestamps()
建立 created_at
及 updated_at
兩個 columns,Eloquent ORM 會自動維護這兩個 Columns建立完成之後,不要忘記 php artisan migrate
<?php
// app/Models/Url.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Url extends Model
{
protected $fillable = ['url'];
}
$fillable
是用於 Url::create()
及 $url->update()
時使用
$fillable
允許範圍內的 column 並不會被 create 及 update 修改利用 Artisan Tinker 的功能,我們可以輕鬆地與 Laravel 交互。
$ php artisan tinker
Psy Shell v0.10.4 (PHP 7.4.9 — cli) by Justin Hileman
>>>
以下示範一些 tinker shell 的使用方式
(tinker) > Url::create(['url' => 'www.google.com.tw']);
=> App\Url {#3985
url: "https://www.google.com.tw",
updated_at: "2020-01-01 00:00:00",
created_at: "2020-01-01 00:00:00",
id: 1,
}
}
(tinker) > Url::find(1)
=> App\Url {#3424
id: "1",
url: "https://www.google.com.tw",
created_at: "2020-01-01 00:00:00",
updated_at: "2020-01-01 00:00:00",
}
(tinker) > Url::find(1)->update(['url' => 'https://www.yahoo.com.tw'])
=> true
(tinker) > Url::find(1)
=> App\Url {#3424
id: "1",
url: "https://www.yahoo.com.tw",
created_at: "2020-01-01 00:00:00",
updated_at: "2020-01-01 00:00:00",
}
url
$ php artisan make:controller ShorterController
<?php
// app/Http/Controllers/ShorterController.php
// ...
class ShorterController extends Controller
{
public function __invoke(Request $request)
{
$shortedUrl = Url::create(['url' => $request->url]);
return $shortedUrl->id;
}
}
<?php
// routes/web.php
use App\Http\Controllers\ShorterController;
Route::post('/', ShorterController::class)->name('shorter');
$ php artisan make:controller RedirectController
<?php
// app/Http/Controllers/RedirectController.php
use Illuminate\Support\Facades\Redirect;
class RedirectController extends Controller
{
public function __invoke(int $id)
{
$url = Url::find($id)->url;
return redirect()->to($url);
}
}
<?php
// routes/web.php
Route::post('/', 'ShorterController')->name('shorter');
Route::get('/{id}', 'RedirectController')->name('redirector');