【開発ノート】画像素材ストッカー開発
開発目的
web制作で使用する画像素材をチームで保管・共有するため
開発環境構築
Laravel sailで構築
参考記事
メモ
-
sail up -d
の後、localhostにアクセスするにはマイグレーションする必要あり
phpmyadminの導入
参考記事
メモ
- DBユーザーに権限付与必要
メモ
Laravel Sailで作成してプロジェクトをgithub経由でcloneしたらsailコマンドが使えなかった
原因
必要なパッケージをインストールしていないから
そのためのcomposerもインストールしていない
解決方法
下記のコマンドでcomposerをインストールして各種パッケージをインストールする
docker run --rm \
-u "$(id -u):$(id -g)" \
-v $(pwd):/var/www/html \
-w /var/www/html \
composer install
開発
各種テーブルとモデル作成、リレーションシップ設定
Googleドライブへの接続
-
google cloude consoleでGoogle Drive APIを使えるように設定
参考:
https://qiita.com/doran/items/15b2c59adb410ddeeb8a -
パッケージのインストール
# 必要なパッケージのインストール
composer require google/apiclient
composer require google/cloud-storage
- 認証ファイルの配置
mkdir -p config/google
mv service-account.json config/google/service-account.json
mv oauth-credentials.json config/google/oauth-credentials.json
- gitignoreの設定
/config/google/*.json
!/config/google/.gitkeep
-
サービスクラスの実装
-
カスタム例外クラスの作成
-
サービスプロバイダーの登録
php artisan make:provider GoogleServiceProvider
- 設定ファイルの更新(config/services.php)
return [
// 他の設定...
'google' => [
'auth_type' => env('GOOGLE_DRIVE_AUTH_TYPE', 'service-account'),
'folder_id' => env('GOOGLE_DRIVE_FOLDER_ID'),
'credentials_path' => env('GOOGLE_DRIVE_CREDENTIALS_PATH', base_path('config/google/service-account.json')),
],
];
- 環境変数の設定
GOOGLE_DRIVE_FOLDER_ID=your-folder-id
GOOGLE_DRIVE_AUTH_TYPE=service-account
GOOGLE_DRIVE_FOLDER_ID
はgoogle driveで作成したフォルダにアクセルしURLを確認する
例:
https://drive.google.com/drive/folders/1aB2cD3eF4gH5iJ6kL7mN8...
この1aB2cD3eF4gH5iJ6kL7mN8...
部分がGOOGLE_DRIVE_FOLDER_ID
-
コントローラーで処理を実装
-
viewとrouteの設定
Google Cloud Consoleでの設定手順
a. プロジェクトの作成:
Google Cloud Console にアクセス
新しいプロジェクトを作成
b. APIの有効化:
左メニューから「APIとサービス」→「ライブラリ」
検索で "Google Drive API" を探す
「有効にする」をクリック
c. サービスアカウントの作成:
左メニューから「APIとサービス」→「認証情報」
「認証情報を作成」→「サービスアカウント」を選択
必要事項を入力:
サービスアカウント名(例:my-app-drive-access)
説明(任意)
「作成」をクリック
d. キーの作成:
作成したサービスアカウントをクリック
「キー」タブを選択
「鍵を追加」→「新しい鍵を作成」
JSONを選択
キーがダウンロードされます
e. 権限の設定:
Google Driveで使用するフォルダを開く
フォルダを右クリック→「共有」
サービスアカウントのメールアドレスを追加
メールアドレスは [サービスアカウント名]@[プロジェクトID].iam.gserviceaccount.com
※Google Cloud Console で確認できる
Google Cloud Console にアクセス
左メニューから「IAM と管理」→「サービスアカウント」を選択
サービスアカウントの一覧が表示され、そこにメールアドレスが表示されている
編集者権限を付与
開発
google driveへのアップロードロジックをサービスクラスとして作成
initialize() メソッド
Google Clientを設定し、認証タイプに応じてサービスアカウントまたはOAuthを初期化
initializeServiceAccount() メソッド
サービスアカウントを使用してGoogle Clientを設定
認証ファイルが存在しない場合は例外をスロー。
initializeOAuth() メソッド
OAuthを使用してGoogle Clientを設定
uploadFile() メソッド
指定されたファイルをGoogle Driveにアップロード
アップロードが成功したらfile_id
view_link
name
を返却する
開発
アップロードに成功したらファイル情報をDBに登録
public function fileUpload($file)
{
try {
$result = $this->driveService->uploadFile($file);
Image::create([
'major_category_id' => 1,
'drive_file_id' => $result['file_id'],
'drive_view_link' => $result['web_view_link'],
'drive_download_link' => $result['web_content_link'],
'title' => $result['name'],
'user_name' => 'test',
'mime_type' => $result['mimeType'],
'file_size' => $result['size'],
'discription' => $result['description'],
'thumbnail_link' => $result['thumbnail_link']
]);
return [
'success' => true,
'file_id' => $result['file_id'],
'file_url' => $result['web_view_link'],
'file_name' => $result['name'],
'file_type' => $result['mimeType'],
'file_size' => $result['size'],
'created_time' => $result['created_time'],
'modified_time' => $result['modified_time'],
'description' => $result['description'],
'web_content_link' => $result['web_content_link'],
'thumbnail_link' => $result['thumbnail_link']
];
} catch (GoogleDriveException $e) {
return [
'success' => false,
'error' => 'アップロードに失敗しました: ' . $e->getMessage()
];
}
}
試験実装
DBからファイル情報を取得してviewで表示
// namespace App\Http\Controllers
class HomeController extends Controller
{
public function index()
{
$files = Image::with('majorCategory')->get()->map(function ($image) {
$image->file_size = number_format($image->file_size / 1024, 1);
$image->major_category = $image->majorCategory->name;
return $image;
})->sortByDesc('created_at');
return view('index', compact('files'));
}
}
// route/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
メモ
以下のカラムを取得するときにアクセサで自動処理をしておく
- mime_type → 拡張子の文字列だけ返却
- file_size → KBの小数点第一位までを返却
// namespace App\Models;
class Image extends Model
{
...
public function getMimeTypeAttribute($value)
{
return explode('/', $value)[1];
}
public function getFileSizeAttribute($value)
{
return number_format($value / 1024, 1);
}
}
開発
google認証の実装
- socialiteのインストール
composer require laravel/socialite
- 各ファイルの作成
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AllowedEmail extends Model
{
protected $fillable = ['email'];
public static function isAllowed($email)
{
return static::where('email', $email)->exists();
}
}
// config/services.php
return [
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],
];
// routes/web.php
use Laravel\Socialite\Facades\Socialite;
use App\Http\Controllers\Auth\GoogleController;
Route::get('auth/google', [GoogleController::class, 'redirectToGoogle'])->name('auth.google');
Route::get('auth/google/callback', [GoogleController::class, 'handleGoogleCallback']);
// App/Http/Controllers/Auth/GoogleController.php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\AllowedEmail;
use Laravel\Socialite\Facades\Socialite;
use Exception;
use Illuminate\Support\Facades\Auth;
class GoogleController extends Controller
{
public function redirectToGoogle()
{
return Socialite::driver('google')->redirect();
}
public function handleGoogleCallback()
{
try {
$googleUser = Socialite::driver('google')->user();
// メールアドレスが許可リストにあるか確認
if (!AllowedEmail::isAllowed($googleUser->email)) {
return redirect()->route('login')
->with('error', 'このメールアドレスではログインできません。');
}
// ユーザーが存在するか確認し、なければ作成
$user = User::where('google_id', $googleUser->id)->first();
if (!$user) {
$user = User::create([
'name' => $googleUser->name,
'email' => $googleUser->email,
'google_id' => $googleUser->id,
'password' => encrypt(rand(1,10000)), // ダミーパスワード
]);
}
Auth::login($user);
return redirect()->intended('dashboard');
} catch (Exception $e) {
return redirect()->route('login')
->with('error', '認証中にエラーが発生しました。');
}
}
}
// database/migrations/xxxx_xx_xx_create_allowed_emails_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAllowedEmailsTable extends Migration
{
public function up()
{
Schema::create('allowed_emails', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('allowed_emails');
}
}
// database/migrations/xxxx_xx_xx_add_google_id_to_users_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddGoogleIdToUsersTable extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('google_id')->nullable()->after('id');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('google_id');
});
}
}
- middlewareの作成
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;
class CheckAuth
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
if (!Auth::check()) {
// APIリクエストの場合は401レスポンス
if ($request->expectsJson()) {
return response()->json([
'message' => '認証が必要です。',
], 401);
}
// 現在のURLを保存してリダイレクト
return redirect()->route('login')
->with('error', 'ログインが必要です。')
->with('redirect', $request->fullUrl());
}
// セッションの有効期限を延長
if ($request->session()->has('auth')) {
$request->session()->regenerate();
}
return $next($request);
}
}
\bootstrap\app.php
にエイリアスを登録
<?php
use App\Http\Middleware\CheckAuth;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'auth' => CheckAuth::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();