はじめに
Laravel + Vue.js のアウトプットとしてタスク管理アプリを制作しました。
今回は制作記録をまとめていきたいと思います。
サービス概要
登録したタスクをドラッグ&ドロップで自由に並び替えが出来るタスク管理サービスです。
タスクは「フォルダ」▶「カード」▶「アイテム」とカテゴリを細かく分類して登録出来るようにしています。すべての項目でブラウザから自由に並び替えができます。
サービス完成品
実際のアプリケーションはこちらです。デモ用としてゲストユーザーも用意しています。
s-taskapp.com
TaskApp
製作期間
2020/1/20 〜 2020/3/20
約 220 時間
制作背景
このサービスを作ろうと思った理由ですが、タスク管理の Web サービスは多種多様なものが存在していますが、新しいサービスを使用する際はその機能に慣れるまである程度の時間を有すると感じており、それが思っていた以上にストレスでした。
個人向けで、もっとシンプルなタスク管理サービスが存在していても良いのではと思ったのが制作のきっかけです。
サービスの差別化
上記の理由から、とにかくシンプルに作りました。
サービスの機能や制作過程
- 機能一覧
- 基本的な認証機能(ユーザー登録、ログイン、ログアウト)
- マイページ機能
- メールアドレス認証機能(マイページよりメールアドレス変更時)
- 退会機能(論理削除に変更)
- パスワードリマインダー
- Twitter 認証機能
- タスク登録機能全般
- ドラッグ&ドロップでタスクの移動
- タスクフォルダの登録、編集、削除
- タスクカードの登録、編集、削除
- タスクアイテムの登録、編集、削除
使用した技術・開発環境
Vagrant + CentOS8(LAMP 環境) PHP(7.4.12) MySQL(8.0.21) webpack(4.44.1) Laravel(5.8.38) Vue.js(2.6.12) Vuex(3.5.1) axios(0.19.2)
ER 図
DB 設計はざっと紙に書き出してから、ER 図に落とし込みました。
VScode の拡張機能で「Draw.io Integration」というものを使用して作成しました。作成したものを png ファイル や svg ファイルにもエクスポート出来るのでおすすめです!
制作過程でつまずいたところ
SPA の認証機能
技術的な挑戦という観点もありますが、スマホからの利用も考慮して SPA 構成にしようと思いました。
また、一度ページが読み込まれれば画面遷移なくページを切り替えれるのは、ユーザーもストレスが少なくて良いと感じたためです。
認証方法には Session を使用していますが、モバイル端末を扱う場合は Token の方がパフォーマンスが良いみたいです。メリット・デメリットに関しては以下の記事を参考にしまた。
SPA の認証関係はこちらの記事を参考にしました。
まずは機能を 1 つ 1 つ分解して実装して行こうと考えましたが、Vue + Vuex + axios + Laravel が絡んで来ると何をしているのか理解が追いつきませんでした(笑
取り敢えず手を動かしながら、発生しまくるエラーを泥臭く解消していく日々が続きました。
紙に認証を行うまでの流れを書いて理解する
- フロント側からユーザー情報を送信(api を叩く)
- 送られてきた情報を元に、Laravel 側でバリデーションチェックを行う
- 問題なければ通常通り認証して、JSON 形式でユーザー情報を返す(ここが MPA と違う)
- 返却された情報にはステータスコードも含まれているので、それを元にフロント側でエラーメッセージを表示させたりエラーページに遷移させたりする
- 問題なければ Vuex(store)にユーザー情報やステータスコードを保存しておく(これがフロント側で状態管理の役割を担う)
- vue-router を用いて画面遷移する
ざっくり、こんな感じでしょうか?
Vuex の使い方や Vue-Router の使い方などでも苦戦しましたが、公式ドキュメントやエラーを解決していく過程で把握することが出来ました。
vue-draggable と Laravel の連携
タスクの並べ替え機能は Vue.js のパッケージである vue-draggable を使用しました。
完成動画でも載せていますが、タスクを上下並べ替えを横移動も出来るようにしました。vue-draggable のドキュメントを読めば、フロンド側の縦横移動は比較的かんたんに実装出来るのではないかと思います。
今回は移動後の情報をサーバサイドに保存することを考えていたので、その情報をどのように渡してどう保存しようか躓きました。また、ググってみても関連記事が少なく試行錯誤が続きました。
こちらの記事でまとめるのはかなり長文になりそうなので、別の記事でまとめようと思います。
ざっくりとした解決方法は、
- それぞれのタスクの DOM に id をもたせる
- DB 側の各タスクに priority カラム を用意しておく
- vue-draggable で移動時のイベントを拾う
- イベント発火時に DOM に付与している id を Laravel 側に送信する(axios を使用した非同期通信)
- DB に保存されているタスクと、送信されてきたタスク id を元に priority カラム の更新を行う
- 更新後のデータを JSON 形式で返す
- Vuex のストアを更新
ざっくりこんな感じの流れになります。
Twitter 認証機能
Laravel を使用した TwitterAPI を使用した認証方法はいろいろな記事がありましたが、SPA 構成では中々ヒントになるような記事がありませんでした。
認証後の状態管理は通常のユーザーログインと同じなおですが、TwitterAPI にリクエストする方法が少し異なりました。
Laravel の Socialate を使用したやり方だと、Twitter 認証のページにリダイレクトさせる 場合は、通常は以下のようなコードになります。
<?php
public function redirectToProvider()
{
return Socialite::driver('twitter')->redirect();
}
ちなみにこのコードで返却されるものをデバッグしてログを確認してみたら以下のような内容が返ってきていました。
HTTP/1.0 302 Found
Cache-Control: no-cache, private
Date: Fri, 04 Dec 2020 12:25:47 GMT
Location: https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="refresh" content="0;url='https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx'" />
<title>Redirecting to https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxxs</title>
</head>
<body>
Redirecting to <a href="https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx">https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx</a>.
</body>
</html>
トークンを加えたクエリパラメータをリダイレクト先の URL として渡して、スタータスコード 302 でリダイレクトさせています。
SPA 構成だとリダイレクトはフロント側で行う必要があるので、リダイレクト先の URL のみ取得します。
<?php
public function redirectToTwitter(): JsonResponse // 返り値の型をJSON形式に指定する。PHP7から返り値に型宣言がかけるようになった
{
// リダイレクトURLをJSON形式で返す
$redirect_url = Socialite::driver("twitter")
->redirect()
->getTargetUrl();
\Log::debug("リダイレクトURLを取得しています。。。" . $redirect_url);
\Log::debug(" ");
return response()->json(["redirect_url" => $redirect_url]);
}
返却されてきた URL とステータスコードを元にフロント側で分岐させていきます。HTTP レスポンスステータスコードが意図しないものであれば VueRouter の機能を使って 500 エラーページへ遷移させます。このエラーページは別途作成しておく必要があります。
// Login.vue
// 抜粋
async twitterLogin() {
// catchの処理は axios interceptors を使用して別ファイルにまとめている
const response = await axios.get("/api/auth/twitter");
if (response.status === 200) {
window.location = response.data.redirect_url;
} else {
this.$router.push("/500");
}
},
Twitter 側で認証が正常に行われると CALLBACK URLS で指定した URL に遷移してくるので、あとはフロント側で Callback ページ を作 ってあげてトークンが付与されたクエリパラメータを Laravel 側に投げて認証するという流れになります。
この辺りのフロントとバックの連携も別記事にまとめていきたいですね。
おわりに
今回は過去のアウトプットの中でも、かなり未知の技術に挑戦した形になりましたが何とか完成させることが出来ました。
自分が触ったことのない技術でも、素早くキャッチアップして成果を上げていくのは大変ですが、思うようにエラーが解決出来たり、サービスをリリースできた時の喜びは大きいですね!