Skip to content

Commit c4ae2d5

Browse files
committed
rv role
1 parent 837d46c commit c4ae2d5

File tree

10 files changed

+277
-20
lines changed

10 files changed

+277
-20
lines changed

README.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Laravelはvendor/に数千〜数万のファイルがあり、それを頻繁に
2222
・リポジトリ: `git@github.com:idw-coder/laravel-rds.git`
2323
・ブランチ戦略: main ブランチ運用
2424

25+
### ユーザー認証
26+
Sanctum とGoogoleOAuth
2527
### CI/CD
2628
ローカルではLaravel、MySQLはDocker Sail環境、Vueはnpm run dev で開発サーバーを使用
2729
フロントエンド(Vue)、バックエンド(Laravel)はlightsailに
@@ -504,6 +506,45 @@ graph TB
504506
style UpdateGoogleId fill:#fff3cd
505507
```
506508
509+
### roles テーブルの追加
510+
511+
```bash
512+
./vendor/bin/sail artisan make:migration create_roles_table
513+
```
514+
515+
#### ユーザーとロールの中間テーブル(role_user)
516+
517+
「ユーザー × ロール」の関係を中間テーブルで管理する
518+
https://readouble.com/laravel/10.x/ja/eloquent-relationships.html#many-to-many
519+
520+
```bash
521+
./vendor/bin/sail artisan make:migration create_role_user_table
522+
```
523+
524+
Seeder を作成する
525+
```bash
526+
./vendor/bin/sail artisan make:seeder RoleSeeder
527+
```
528+
529+
それに合わせてdatabase\seeders\DatabaseSeeder.phpも修正
530+
531+
DatabaseSeeder.phpのrun()メソッドが実行される
532+
```bash
533+
./vendor/bin/sail artisan migrate:fresh --seed
534+
```
535+
- `migrate:fresh`: 全テーブルを削除して再作成(空の状態)
536+
- `--seed`: DatabaseSeederを実行して初期データを投入
537+
538+
User.php roles() リレーション追加
539+
540+
app\Models\Role.phpを作成
541+
542+
543+
## TODO
544+
545+
- **ユーザー登録機能の追加**: 現在はGoogle認証でのみ自動登録されるため、メール・パスワードでの新規登録機能(`register`メソッド)を`AuthController`に追加する。これにより、Googleアカウントを持たないユーザーも登録可能になる。
546+
- **認証方法の統一**: メール・パスワード登録とGoogle認証の両方をサポートし、ユーザーが選択できるようにする。既存ユーザーが後からGoogle認証を紐付けたり、Google認証ユーザーがパスワードを設定できるようにする。
547+
507548
### APIのエラーハンドリングとレスポンス形式
508549
509550
ルート一覧を確認
@@ -557,4 +598,5 @@ WARN[0000] The "MYSQL_EXTRA_OPTIONS" variable is not set. Defaulting to a blank
557598
| GET\|HEAD | storage/{path} | storage.local | - | - |
558599
| GET\|HEAD | up | - | - | - |
559600
560-
</div>
601+
</div>
602+

app/Http/Controllers/Api/AuthController.php

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,21 @@ public function login(Request $request)
2323
// ユーザーを検索
2424
$user = User::where('email', $request->email)->first();
2525

26-
// Googleログインの場合
27-
if ($user && !$user->password) {
26+
// Googleログイン専用ユーザーの場合
27+
if ($user && is_null($user->password)) {
2828
return response()->json([
29-
'message' => 'このアカウントは Google でログインしてください'
29+
'message' => 'このアカウントは Google アカウントでログインしてください。',
3030
], 400);
3131
}
3232

33-
/**
34-
* メール・パスワードログインの場合
35-
*
36-
* 認証に失敗した場合は401 Unauthorizedを返却
37-
*/
33+
// メール・パスワード認証
3834
if (!Auth::attempt($request->only('email', 'password'))) {
3935
return response()->json([
4036
'message' => 'ログイン情報が正しくありません。',
4137
], 401);
4238
}
4339

44-
// 認証済みユーザー取得
40+
// 認証済みユーザー
4541
/** @var User $user */
4642
$user = Auth::user();
4743

@@ -53,7 +49,14 @@ public function login(Request $request)
5349

5450
return response()->json([
5551
'token' => $token,
56-
'user' => $user,
52+
53+
// ロールを含めて返す(重要)
54+
'user' => [
55+
'id' => $user->id,
56+
'name' => $user->name,
57+
'email' => $user->email,
58+
'roles' => $user->roles->pluck('name'),
59+
],
5760
]);
5861
} catch (ValidationException $e) {
5962
// バリデーションエラーの場合
@@ -81,7 +84,7 @@ public function login(Request $request)
8184
$response['file'] = $e->getFile();
8285
$response['line'] = $e->getLine();
8386
}
84-
87+
8588
return response()->json($response, 500);
8689
}
8790
}

app/Http/Controllers/Api/GoogleAuthController.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Http\Controllers\Controller;
66
use App\Models\User;
7+
use App\Models\Role;
78
use Illuminate\Http\Request;
89
use Laravel\Socialite\Facades\Socialite;
910

@@ -35,6 +36,8 @@ public function handleGoogleCallback(Request $request)
3536
->orWhere('email', $googleUser->email)
3637
->first();
3738

39+
$isNewUser = false;
40+
3841
if ($user) {
3942
// 既存ユーザーの場合、google_id を更新(未設定の場合)
4043
if (!$user->google_id) {
@@ -49,13 +52,25 @@ public function handleGoogleCallback(Request $request)
4952
'google_id' => $googleUser->id,
5053
'password' => null, // Googleログインユーザーはパスワード不要
5154
]);
55+
56+
$isNewUser = true;
57+
}
58+
59+
/**
60+
* 新規ユーザーの場合 → free ロールを付与
61+
*/
62+
if ($isNewUser) {
63+
$freeRoleId = Role::where('name', 'free')->value('id'); // freeロールのID取得
64+
if ($freeRoleId) {
65+
$user->roles()->attach($freeRoleId);
66+
}
5267
}
5368

54-
// トークンを生成
69+
// SanctumのAPIトークン発行
5570
$token = $user->createToken('auth-token')->plainTextToken;
5671

5772
return response()->json([
58-
'user' => $user,
73+
'user' => $user->load('roles'), // 付与ロールも返す
5974
'token' => $token
6075
]);
6176
} catch (\Exception $e) {

app/Http/Controllers/Api/UserController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class UserController extends Controller
1414
*/
1515
public function show(Request $request)
1616
{
17-
return response()->json($request->user());
17+
return response()->json($request->user()->load('roles'));
1818
}
1919

2020
/**

app/Models/Role.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class Role extends Model
9+
{
10+
use HasFactory;
11+
12+
/**
13+
* The attributes that are mass assignable.
14+
*
15+
* @var list<string>
16+
*/
17+
protected $fillable = [
18+
'name',
19+
'label',
20+
];
21+
22+
/**
23+
* このロールを持つユーザー(多対多)
24+
*
25+
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
26+
*/
27+
public function users()
28+
{
29+
return $this->belongsToMany(User::class, 'role_user');
30+
}
31+
}
32+

app/Models/User.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Database\Eloquent\Factories\HasFactory;
88
use Illuminate\Foundation\Auth\User as Authenticatable;
99
use Illuminate\Notifications\Notifiable;
10+
use App\Models\Role;
1011

1112
class User extends Authenticatable
1213
{
@@ -67,4 +68,21 @@ public function posts()
6768
{
6869
return $this->hasMany(Post::class);
6970
}
71+
72+
/**
73+
* このユーザーが持つロール(多対多)
74+
*
75+
* belongsToMany:
76+
* - 第1引数: 関連先モデル(Role)
77+
* - 第2引数: 中間テーブル名(role_user)
78+
*
79+
* これにより $user->roles() でロール一覧を取得でき、
80+
* attach(), sync(), detach() などで紐付け操作が可能になる。
81+
*
82+
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
83+
*/
84+
public function roles()
85+
{
86+
return $this->belongsToMany(Role::class, 'role_user');
87+
}
7088
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* roles テーブル:
13+
* - name: システム内部で使うロール識別子(例: free, paid, admin)
14+
* - label: 管理画面やUIで表示するための名前(例: 無料ユーザー / 有料ユーザー)
15+
*/
16+
public function up(): void
17+
{
18+
Schema::create('roles', function (Blueprint $table) {
19+
$table->id(); // 主キー
20+
$table->string('name')->unique(); // ロール識別子(例: 'free', 'paid', 'admin')
21+
$table->string('label')->nullable(); // 表示名(任意、null 許可)
22+
$table->timestamps(); // created_at / updated_at
23+
});
24+
}
25+
26+
/**
27+
* Reverse the migrations.
28+
*/
29+
public function down(): void
30+
{
31+
Schema::dropIfExists('roles');
32+
}
33+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* role_user テーブル(中間テーブル)
13+
* - ユーザーとロールを紐付けるための多対多の中間テーブル
14+
* - 外部キー制約を付与
15+
* - user_id + role_id の組をユニークにして重複を防止
16+
*/
17+
public function up(): void
18+
{
19+
Schema::create('role_user', function (Blueprint $table) {
20+
$table->id();
21+
22+
// user_id(users.id と紐づく)
23+
$table->unsignedBigInteger('user_id');
24+
25+
// role_id(roles.id と紐づく)
26+
$table->unsignedBigInteger('role_id');
27+
28+
// どのロールがいつ付与されたかを追いたい場合に timestamps が有用
29+
$table->timestamps();
30+
31+
// 外部キー制約:ユーザーが削除されたら関連ロールも削除
32+
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
33+
34+
// 外部キー制約:ロールが削除されたら紐付けも削除
35+
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
36+
37+
// user_id と role_id の組み合わせでユニーク(重複登録を防止)
38+
$table->unique(['user_id', 'role_id']);
39+
});
40+
}
41+
42+
/**
43+
* Reverse the migrations.
44+
*/
45+
public function down(): void
46+
{
47+
Schema::dropIfExists('role_user');
48+
}
49+
};

database/seeders/DatabaseSeeder.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,47 @@
33
namespace Database\Seeders;
44

55
use App\Models\User;
6-
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
76
use Illuminate\Database\Seeder;
7+
use Illuminate\Support\Facades\Hash;
88

99
class DatabaseSeeder extends Seeder
1010
{
11-
use WithoutModelEvents;
12-
1311
/**
1412
* Seed the application's database.
1513
*/
1614
public function run(): void
1715
{
18-
// User::factory(10)->create();
16+
// 先にロールを作成
17+
$this->call(RoleSeeder::class);
18+
19+
// freeロールを取得
20+
$freeRoleId = \App\Models\Role::where('name', 'free')->value('id');
1921

20-
User::factory()->create([
22+
// テストユーザー作成
23+
$user = User::factory()->create([
2124
'name' => 'Test User',
2225
'email' => 'test@example.com',
26+
'password' => Hash::make('password'),
2327
]);
28+
29+
// freeロール付与
30+
if ($freeRoleId) {
31+
$user->roles()->attach($freeRoleId);
32+
}
33+
34+
// adminロールを取得
35+
$adminRoleId = \App\Models\Role::where('name', 'admin')->value('id');
36+
37+
// 管理者ユーザー作成
38+
$adminUser = User::factory()->create([
39+
'name' => 'Admin User',
40+
'email' => 'admin@example.com',
41+
'password' => Hash::make('password'),
42+
]);
43+
44+
// adminロール付与
45+
if ($adminRoleId) {
46+
$adminUser->roles()->attach($adminRoleId);
47+
}
2448
}
2549
}

0 commit comments

Comments
 (0)