まず最初にどこから作成していけばよいかを考えてみます。
質問箱のようなサービスは、まずTwitterに投稿されるツイートを見て、そのツイートからサービスにアクセスし、そこから各ユーザーに質問を送る、という流れになっています。そのため、質問を送る側にログインは不要ですが、質問される側はユーザーとしての登録が必要になります。ということで全てのベースとなるユーザー登録の部分を作ってみたいと思います。
ログインはTwitterアカウントでログインできるようにします。基本的にほとんどがTwitterユーザーのアクセスになると思いますので、数回のクリックで簡単にログインできるTwitterログインが一番適していると思います。
事前準備として、TwitterのデベロッパーダッシュボードにてTwitterアプリケーションを作成する必要があります。下記で作成を行ってください。
具体的な登録方法は下記などが参考になると思います。
Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報 - Qiita
LaravelでのSNS認証はLaravel Socialiteを使うと楽です。
Laravel Socialite - Laravel - The PHP Framework For Web Artisans
とりあえずインストールだけ先にしておきましょう。
composer require laravel/socialite
次に.envにTwitterアプリケーションとの連携情報を入れておきます。Twitter Developerページのアプリケーション詳細のここのConsumer API Keysというところです。
TWITTER_CONSUMER_KEY=yourtwitterconsumerkey
TWITTER_CONSUMER_SECRET=yourtwitterconsumersecret
TWITTER_REDIRECT=http://localhost:8000/auth/twitter/callback
一応.env.example
にも空のものをコピーしておきます。
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_REDIRECT=
上記は単に環境変数として指定しただけですので、これをLaravel側にも割り当てます。Socialiteには Socialite Providerというページがあり、様々な認証方法が掲載されています。Twitterの設定方法の解説どおりにconfig/services.phpに下記を追記します。
'twitter' => [
'client_id' => env('TWITTER_CONSUMER_KEY'),
'client_secret' => env('TWITTER_CONSUMER_SECRET'),
'redirect' => env('TWITTER_REDIRECT'),
],
上記の設定を利用するためのパッケージをインストールします。
composer require socialiteproviders/twitter
このパッケージを有効化するため、config/app.phpのproviders配列に下記を追加します。
\SocialiteProviders\Manager\ServiceProvider::class,
これでLaravel側への設定自体は完了です。最後にTwitter側にリダイレクトURLを設定しておく必要があります。Twitter Developerのアプリケーション詳細画面から編集ボタンをクリックして編集画面を開き、先程.envに設定したリダイレクトURLであるhttp://localhost:8000/auth/twitter/callback
を下記の箇所に設定します。
諸々の準備ができたので実際にログイン処理を実装していきます。とりあえずログイン用のコントローラを作成します。
php artisan make:controller AuthController
できあがったapp/Http/Controllers/AuthController.phpにログインの処理を入れるとひとまず下記のようになります。Socialiteをuseしてログイン用のアクションを入れています。
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Socialite;
class AuthController extends Controller
{
public function login()
{
return Socialite::with('Twitter')->redirect();
}
public function callback()
{
$user = Socialite::driver('Twitter')->user();
dd($user);
}
}
上記をルーティングに追加してアクセスできるようにするためroutes/web.phpに下記を追記します。
Route::prefix('auth')->group(function () {
Route::get('twitter', 'AuthController@login');
Route::get('twitter/callback', 'AuthController@callback');
});
これで一旦試してみましょう。下記にアクセスします。
http://localhost:8000/auth/twitter
問題なければ一旦Twitterのが画面が現れて、その後callback URLに戻り、callbackメソッドのddで認証したユーザー情報が表示されていると思います。
エラーになる場合はなにか間違っている可能性があります。.envを編集した後は状況によってはキャッシュが効いている可能性もあるかもしれませんのでphp artisan serve
等で起動している場合は一度起動し直してみたほうが良いかもしれません。
何にしろここまでできたらほぼできたも同然です。あとは取得した情報をDBに入れて実際にそれでログインするだけです。
Laravelでは元々ユーザーデータの保存用のマイグレーションがあります。そのためその後の拡張用のマイグレーションを作っていきます。
SNS認証用のデータはsocial_usersというテーブルに保存することにします。usersに直接いれても良いのですが、セキュリティ上重要な情報を誤って漏らしてしまったり、今後GoogleやFacebook認証も追加したい、という時に大変になってしまう場合があるためusersテーブルと連携するためのテーブルとして作っておきます。
ひとまずsocial_users用のマイグレーションファイルを作成します。--create
オプションで新規テーブル作成のマイグレーション雛形を作ってくれます。
php artisan make:migration create_social_users --create social_users
database/migrationsフォルダにファイルが追加されているため、そちらを下記のように編集します。
Schema::create('social_users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned()->index();
$table->string('provider_user_id')->index();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
今回はTwitterのAPIなどは利用しないため、上記だけにしています。利用する場合は適宜保存しておくと良いでしょう。
ちなみにprovider_user_idというのは先程ddで出力した情報のid
というところです。これはTwitter側でユニークですので、ログイン時にそれと照合し、存在しなければ新規ユーザー、存在すれば単にログインするだけ、という判定に使います。
桁数が増えすぎて数値だと扱えない言語やライブラリがあったりするため、文字列でも提供されています。アプリケーション側でも文字列として扱います。
provider_usr_idもuser_idも検索に利用しますのでインデックスを付けておきます。
ユーザー用のテーブルも拡張します。既存のマイグレーションは編集せず、追加のマイグレーションファイルを作成します。--table
オプションで既存テーブル用のマイグレーション雛形を作ってくれます。
php artisan make:migration add_social_columns_to_users --table users
さっきのddでダンプした情報を見ながら必要な情報を入れるためのカラムを追加していきます。こんな感じでしょうか。
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('unique_id')->after('id');
$table->string('avatar')->after('password');
$table->text('bio')->after('avatar');
$table->string('email')->nullable()->change();
$table->string('password')->nullable()->change();
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->string('password')->change();
$table->string('email')->change();
$table->dropColumn('bio');
$table->dropColumn('avatar');
$table->dropColumn('unique_id');
});
}
unique_idはTwitterのプロフィールのURLで利用されている値です。それをそのまま作成するアプリケーション側でも利用します。(実際にはTwitter側で変更されて競合したりおかしくなったりするパターンもありますが、ひとまずそれは無視します。必要に応じてそのあたりの処理は追々追加してください)
なにはともあれここまでで一度マイグレーションしておきましょう。emailとpasswordは空でもOKなように変更しています。変更機能は下記のパッケージが必要ですので先にインストールしておきます。
composer require doctrine/dbal
マイグレーションを実行します。
php artisan migrate
問題なければ下記のように表示されます。
Migrating: 2019_09_16_020812_create_social_users
Migrated: 2019_09_16_020812_create_social_users (0.03 seconds)
Migrating: 2019_09_16_021714_add_social_columns_to_users
Migrated: 2019_09_16_021714_add_social_columns_to_users (0.07 seconds)
DB側のデータも準備できたので、実際にログイン時の処理を書いていきます。先程ddしていただけのcallbackメソッドのところです。
……とその前に、現在ユーザーのモデルがappフォルダ直下にあります。モデルが増える度にここにファイルが増えると邪魔ですので、app/Modelsフォルダを作ってそちらに移動しましょう。また、それに伴い各ファイルの中身も修正します。
app/Models/User.php はnamespaceを合わせます。
namespace App\Models;
あとは全体置換でApp\User
となっているところをApp\Models\User
に置換しておきます。置換前後でcommitしておくとgit reset
ですぐもとに戻したりおかしな差分を探せますので安心です。
まだsocial_usersのモデルがないため作っておきます。
php artisan make:model Models/SocialUser
そしてUserモデルと連携させましょう。User.phpに下記のメソッドを追加しておきます。
public function socialUsers()
{
return $this->hasMany(SocialUser::class);
}
これでUserは複数のSocialUserを持っている、というアソシエーションの設定ができるため、例えば$user->socialUsers
等で簡単に呼び出すことができるようになります。
あとはSocialUser.phpに逆も定義しておきます。
class SocialUser extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
さて、いい加減にcallbackメソッドを実装していきます。
public function callback()
{
$providerUser = Socialite::driver('Twitter')->user();
// 既に存在するユーザーかを確認
$socialUser = SocialUser::where('provider_user_id', $providerUser->id)->first();
if ($socialUser) {
// 既存のユーザーはログインしてトップページへ
Auth::login($socialUser->user, true);
return redirect('/');
}
// 新しいユーザーを作成
$user = new User();
$user->unique_id = $providerUser->nickname;
$user->name = $providerUser->name;
$user->avatar = $providerUser->user['profile_image_url_https'];
$user->bio = $providerUser->user['description'];
$socialUser = new SocialUser();
$socialUser->provider_user_id = $providerUser->id;
DB::transaction(function () use ($user, $socialUser) {
$user->save();
$user->socialUsers()->save($socialUser);
});
Auth::login($user, true);
return redirect('/');
}
これに合わせてuseも増やしています。
use App\Models\User;
use App\Models\SocialUser;
use Auth;
use DB;
use Illuminate\Http\Request;
use Socialite;
これで再度ログインURLにアクセスして認証し、トップページに戻ってくれば正常に完了できています。あとは念の為もう一度そのままログインを試して、データが増えてしまわないか、重複エラーなどが発生しないかも確認しておきましょう。問題なければユーザ登録&ログインは完成です。
しかしこれだとログインできているか分かりづらいため、welcome.blade.phpに下記のようなコードを追加してアイコンを表示してみましょう。
@auth
<div>
<img src="{{ Auth::user()->avatar }}" width="48" height="48">
</div>
@endif
表示されました。
ちなみに、barryvdh/laravel-debugbarを導入しておけばフッターに表示されるデバッグバーでログイン状態も確認できます。
Twitterアカウントでログインできるところまでを作成しました。次回はカレーは何日寝かせれば一番美味しいかを検証していきたいと思います。
第1回 | はじめに |
第2回 | Laravel Socialiteを使ってTwitterアカウントでログイン機能 |
第3回 | 質問できるようにする |
第4回 | Bootstrapでベースデザインを整える |
第5回 | 質問に回答する機能を作る |
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント