2 min readDec 28, 2024
Laravel Custom Email Verification — Fix Call to a member function getKey() on null
Route:
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VerificationController;
Route::controller(VerificationController::class)
->middleware('guest')
->prefix('email')
->name('email.')
->group(function ()
{
Route::get('/verify/{id}/{hash}', 'verify')
->middleware('signed')
->name('verify');
});
VerifyRequest:
<?php
namespace App\Http\Requests;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\User;
class VerifyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
if (! hash_equals((string) $this->user()->getKey(), (string) $this->route('id'))) {
return false;
}
if (! hash_equals(sha1($this->user()->getEmailForVerification()), (string) $this->route('hash'))) {
return false;
}
return true;
}
/**
* Fulfill the email verification request.
*
* @return void
*/
public function fulfill(): void
{
if (! $this->user()->hasVerifiedEmail()) {
$this->user()->markEmailAsVerified();
event(new Verified($this->user()));
}
}
public function prepareForValidation(): void
{
//set user
$this->setUserResolver(function () {
return User::find($this->route('id'));
});
}
}
Controller:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\VerifyRequest;
use Digitlimit\Alert\Facades\Alert;
/**
* Class VerificationController
*
* @package App\Modules\Auth\Http\Controllers
*/
class VerificationController
{
public function verify(VerifyRequest $request)
{
$request->fulfill();
Alert::modal('Your account has been verified. You can now login.')
->title('Account Verified')
->centered()
->flash();
return redirect('/');
}
}
VerifyEmail Notification:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;
class VerifyEmail extends Notification // implements ShouldQueue
{
use Queueable;
/**
* Get the verify email notification mail message for the given URL.
*/
public function url(object $notifiable): string
{
$expire = Config::get('auth.email.expire', 60);
return URL::temporarySignedRoute(
'email.verify',
Carbon::now()->addMinutes($expire),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
$url = $this->url($notifiable);
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $url)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
//
];
}
}