記事作成の背景
ORMのPrismaでデータベースから一意のレコードをfindUniqueで取得する際に、沼ってしまったので備忘録として記載します。
沼った原因は何だったのか?
結論は、スキーマ定義で対象となるカラムに対してUnique属性を付与していなかったことが原因でした。
今回は、emailとpasswordの2つで絞り込んで一意のレコードが取得できるようにします。
scema.prisma(修正前)
model Users{
id Int @id @default(autoincrement())
name String
email String
password String
created_at String
updated_at String
}
↑のスキーマ定義だとエラーが生じてしまいます。
コンソールには、「IDとは、一意となるカラムを少なくとも1つは指定してね」という表示が出ます。
なので、下記のように修正します。
scema.prisma(修正後)
model Users{
id Int @id @default(autoincrement())
name String
email String @unique
password String @unique
created_at String
updated_at String
}
これで一意のレコードを取得するためのスキーマ定義完成です。
今回のサンプルはこちら↓↓
emailとpasswordでデータベースからレコードが取得出来たら、toastでメッセージを表示する機能です。
LoginForm.js
'use client';
import {signIn} from 'next-auth/react';
import { useRouter } from 'next/router';
import toast from 'react-hot-toast';
import { useState } from 'react';
import { getLoginUser } from '../lib/actions/user.action';
import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
import nextSession from "next-session";
export default function LoginForm({callbackUrl}){
//const router = useRouter();
const [email,setEmail] = useState('');
const [password,setPassword] = useState('');
return (
<form onSubmit={handleSubmit} className='flex-1 space-y-6'>
<div className='space-y-2'>
<label className='label' htmlFor='email'>
email
</label>
<input
id='email'
className='input input-bordered w-full'
value={email}
onChange={e=>setEmail(e.target.value)}
type='email'
placeholder='Enter your email address'
required
/>
</div>
<div className='space-y-2'>
<label className='label' htmlFor='password'>
Password
</label>
<input
id='password'
className='input input-bordered w-full'
value={password}
onChange={e=>setPassword(e.target.value)}
type='password'
placeholder='Enter password'
required
minLength={6}
/>
</div>
<button className='btn btn-primary btn-block mt-4' id='loginBtn'>Log In</button>
<button className='btn btn-secondary btn-block mt-4' id='redirectToHome' disabled>
<a href='http://localhost:3000/'>
ホーム画面へ
</a>
</button>
</form>
)
}
const handleSubmit = async event=>{
event.preventDefault();
//指定したのページへ線維させるか判定するためのフラグ
let redirectFlg = false;
let email = document.getElementById('email').value;
let password = document.getElementById('password').value;
const res = await signIn('credentials',{
email,
password,
redirect:false
});
//非同期通信
var selectResult = await getLoginUser(email,password);
sessionStorage.key='名前';
sessionStorage.value = selectResult;
sessionStorage.setItem('name',selectResult);
const sessionData = sessionStorage.getItem('name');
console.log('セッションに保存されている名前は、'+sessionStorage.getItem('name'));
console.log('セッションに保存されている名前は、'+sessionData);
if(res.status === 200){
toast.success('Login Successful');
//redirectFlg = true;
redirectFlg = true;
}else{
if(res.error === 'CredentialSignin'){
res.error = 'Wrong Password';
}
toast.error(`Login Failed:${res.error}`);
}
if(redirectFlg){
//Loginボタンのオブジェクト
let loginBtn = document.getElementById('loginBtn');
let redirectToHome = document.getElementById('redirectToHome');
loginBtn.disabled = true;
redirectToHome.disabled = false;
}else{
//Loginボタンのオブジェクト
let loginBtn = document.getElementById('loginBtn');
let redirectToHome = document.getElementById('redirectToHome');
loginBtn.disabled = false;
redirectToHome.disabled = true;
}
}
user.action.js
'use server';
import prisma from '../prisma';
import bcrypt from 'bcrypt';
export async function createUser(user){
//パスワードをハッシュ化する
const passwordHash = await bcrypt.hash(user.password,10);
//作成日時を取得する
let createdAtDate = getCreatedAtDate();
//更新日時を取得する
let updatedAtDate = getUpdatedAtDate();
//登録処理を実行する
const newUser = await prisma.users.create({data:{
name:user.name,
email:user.email,
password:passwordHash,
created_at:createdAtDate,
updated_at:updatedAtDate,
}});
return newUser;
/*
const passwordHash = await bcrypt.hash(user.password,10);
const newuser = await User.create({
...user,
password:passwordHash
});
return JSON.parse(JSON.stringify(newuser));
*/
}
export async function getUserByEmail(email){
const user = await prisma.users.findUnique({
where:{
email
}
});
if(!user){
throw new Error('User Not Found');
}
return JSON.parse(JSON.stringify(user));
}
/**
* 作成日時を取得する<BR/>
* 作成日時を取得する処理
* @returns {String} 作成日時 getCreatedAtDate
*/
function getCreatedAtDate(){
//作成日時を取得する
const createdAt = new Date();
createdAt.setTime(createdAt.getTime()-createdAt.getTimezoneOffset()*60*1000);
const getCreatedAtDate = createdAt.toISOString().replace('T',' ').substring(0,19);
//日付型を文字列に型変換して返却
return getCreatedAtDate.toString();
}
/**
* 更新日時を取得する<BR/>
* 更新日時を取得する処理
* @returns {String} 更新日時 getUpdatedAtDate
*/
function getUpdatedAtDate(){
//更新日時を取得する
const updatedAt = new Date();
updatedAt.setTime(updatedAt.getTime()-updatedAt.getTimezoneOffset()*60*1000);
const getUpdatedAtDate = updatedAt.toISOString().replace('T',' ').substring(0,19);
//日付型を文字列の型変換して返却する
return getUpdatedAtDate.toString();
}
export async function getLoginUser(email,password){
var userInfo='';
//パスワードをハッシュ化
const passwordHash = await bcrypt.hash(password,10);
console.log(passwordHash);
userInfo = await prisma.users.findUnique({
where:({
email:email,
password:'$2b$10$/qDzMuB0dQxMSabo3leDE.HH63JS2nbXwTb/POKs9digm4pP4DQD2',//password
})
});
console.log('ユーザーの検索結果は:'+userInfo.name);
//userInfo = JSON.parse(userInfo);
return userInfo.name; //userInfo
}
以上です。