使用 Laravel 5.6 實作「短網址產生器」應用程式

環境

  • Windows 7
  • Apache 2.4.33
  • MySQL 5.7.21
  • PHP 7.2.4

建立專案

1
laravel new surl

新增資料庫

1
CREATE DATABASE `surl`

設置 .env 檔

1
2
3
4
5
6
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=surl
DB_USERNAME=root
DB_PASSWORD=secret

新增遷移

新增 links 資料表。

1
php artisan make:migration create_links_table

配置欄位。

1
2
3
4
5
6
7
8
Schema::create('links', function (Blueprint $table) {
$table->increments('id');
$table->string('link')->nullable(); //舊連結
$table->string('code'); //新連結代碼
$table->integer('status')->default(0); //狀態
$table->string('user_ip')->nullable(); //使用者 IP
$table->timestamps();
});

新增 link_infos 資料表。

1
php artisan make:migration create_link_infos_table

配置欄位。

1
2
3
4
5
Schema::create('link_infos', function (Blueprint $table) {
$table->increments('id');
$table->integer('clicks')->default(0); //點擊次數
$table->timestamps();
});

執行遷移。

1
php artisan migrate

遇到錯誤訊息。

1
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long;

設置 app\Providers\AuthServiceProvider.php 如下:

1
2
3
4
5
6
use Schema;
// ...
public function boot()
{
Schema::defaultStringLength(191);
}

新增模型

新增 Link 模型。

1
php artisan make:model Link

配置可寫入欄位。

1
2
3
protected $fillable = [
'link', 'code', 'status', 'user_ip'
];

新增 LinkInfo 模型。

1
php artisan make:model LinkInfo

配置可寫入欄位。

1
2
3
protected $fillable = [
'clicks',
];

新增路由

1
2
3
Route::get('/', ['as' => 'index', 'uses' => 'LinkController@index']); // 首頁
Route::post('/', ['as' => 'generate', 'uses' => 'LinkController@generate']); // 配給新連結
Route::get('{code}', ['as' => 'redirect', 'uses' => 'LinkController@redirect']); // 導向新連結

新增控制器

調用模型。

1
2
use App\Link;
use App\LinkInfo;

首頁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public function index(Request $request)
{
LinkController::chargeNewCode(10);

$links = Link::
select('links.code', 'links.link', 'links.updated_at', 'link_infos.clicks')
->join('link_infos', 'links.id', '=', 'link_infos.id')
->where('links.user_ip', $request->ip())
->orderBy('links.id', 'desc')
->paginate(10);

$new_link = Link::
where('id', session('status'))
->value('code');

if ($request->ajax()) {
return view('link.part', [
'agent' => $agent,
'links' => $links,
]);
}

return view('index', [
'agent' => $agent,
'links' => $links,
'new_link' => $new_link,
]);
}

配給新連結。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function generate(Request $request)
{
$prepared_link = Link::
where('status', 0)
->orderBy('id')
->first();

$this->validate($request, [
'link' => 'required|max:255|url|active_url',
]);

$prepared_link->update([
'link' => $request->input('link'),
'status' => '1',
'user_ip' => $request->ip(),
]);

return back()->with('status', $prepared_link->id);
}

導向新連結。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public function redirect($code)
{
$redirect_link = Link::
where([
['code', $code],
['status', '1'],
])
->first();

if ($redirect_link) {
LinkInfo::where('id', $redirect_link->id)->increment('clicks');

return redirect($redirect_link->link);
} else {
abort(404);
}
}

補充新連結。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static function chargeNewCode($number)
{
if (Link::where('status', 0)->count() < $number) {
for ($i = 1; $i <= $number; $i++) {
$links = new Link;
$links->code = LinkController::getRandomCode(5);
$links->save();

$link_infos = new LinkInfo;
$link_infos->save();
}

LinkController::destroyDuplicateCode();
}
}

產生亂數代碼。

1
2
3
4
5
public static function getRandomCode($length) {
$bytes = openssl_random_pseudo_bytes($length * 2);

return substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $length);
}

刪除重複代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static function destroyDuplicateCode() {
$duplicate_codes = Link::
selectRaw('`code`, count(`code`)')
->groupBy('code')
->havingRaw('count(`code`) > 1')
->pluck('code')
->all();

if (count($duplicate_codes) > 0) {
foreach ($duplicate_codes as $duplicate_code) {
$links = Link::
where('code', $duplicate_code)
->pluck('id')
->all();

foreach ($links as $link) {
Link::
where('id', $link)
->delete();

LinkInfo::
where('id', $link)
->delete();
}
}
}
}

程式碼