add controllers, servoices

This commit is contained in:
root
2026-03-09 02:52:13 -04:00
parent c8de5f7edc
commit d76c871cb7
501 changed files with 34439 additions and 21843 deletions
@@ -0,0 +1,36 @@
<?php
namespace App\Services\Auth;
class RegistrationCaptchaService
{
public function generate(?int $length = null): string
{
$length = $length ?? random_int(4, 8);
$characters = 'ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz0123456789';
$captchaText = '';
for ($i = 0; $i < $length; $i++) {
$captchaText .= $characters[random_int(0, strlen($characters) - 1)];
}
session()->put('captcha_answer', $captchaText);
return $captchaText;
}
public function verify(string $input): bool
{
$expected = (string) session()->get('captcha_answer', '');
if ($expected === '') {
return false;
}
return hash_equals($expected, $input);
}
public function clear(): void
{
session()->forget('captcha_answer');
}
}
@@ -0,0 +1,84 @@
<?php
namespace App\Services\Auth;
use App\Services\PhoneFormatterService;
class RegistrationFormatterService
{
public function __construct(private PhoneFormatterService $phoneFormatter)
{
}
public function format(array $payload): array
{
$g = static function (string $key) use ($payload): string {
return trim((string) ($payload[$key] ?? ''));
};
$data = [
'firstname' => $this->formatName($g('firstname')),
'lastname' => $this->formatName($g('lastname')),
'email' => $this->formatEmail($g('email')),
'gender' => $g('gender'),
'city' => $this->formatName($g('city')),
'cellphone' => $this->phoneFormatter->formatPhoneNumber($g('cellphone')),
'address_street' => $this->formatAddress($g('address_street')),
'apt' => strtoupper($g('apt')),
'state' => strtoupper($g('state')),
'zip' => $g('zip'),
'accept_school_policy' => (int) ($payload['accept_school_policy'] ?? 0),
];
$noSecondInfo = !empty($payload['no_second_parent_info']);
$secondFirstname = $this->formatName($g('second_firstname'));
$secondLastname = $this->formatName($g('second_lastname'));
$secondGender = $g('second_gender');
$secondEmail = $this->formatEmail($g('second_email'));
$secondCellphone = $this->phoneFormatter->formatPhoneNumber($g('second_cellphone'));
$secondProvided = !$noSecondInfo && (
$secondFirstname !== '' ||
$secondLastname !== '' ||
$secondGender !== '' ||
$secondEmail !== '' ||
preg_replace('/\D/', '', (string) $secondCellphone) !== ''
);
if ($secondProvided) {
$data['second_firstname'] = $secondFirstname;
$data['second_lastname'] = $secondLastname;
$data['second_gender'] = $secondGender;
$data['second_email'] = $secondEmail;
$data['second_cellphone'] = $secondCellphone;
}
return $data;
}
private function formatName(string $name): string
{
$name = trim($name);
if ($name === '') {
return '';
}
return ucwords(strtolower($name), " -");
}
private function formatEmail(string $email): string
{
return strtolower(trim($email));
}
private function formatAddress(string $address): string
{
$address = trim($address);
if ($address === '') {
return '';
}
return ucwords(strtolower($address));
}
}
+159
View File
@@ -0,0 +1,159 @@
<?php
namespace App\Services\Auth;
use App\Models\Configuration;
use App\Models\ParentModel;
use App\Models\Role;
use App\Models\User;
use App\Models\UserRole;
use App\Services\EmailService;
use App\Services\SchoolIdService;
use Illuminate\Support\Facades\DB;
class RegistrationService
{
public function __construct(
private EmailService $emailService,
private SchoolIdService $schoolIdService,
private RegistrationFormatterService $formatter,
private RegistrationCaptchaService $captchaService
) {
}
public function register(array $payload): array
{
$captcha = (string) ($payload['captcha'] ?? '');
if (!$this->captchaService->verify($captcha)) {
return [
'ok' => false,
'code' => 'captcha_invalid',
'message' => 'Incorrect CAPTCHA answer. Please try again.',
];
}
$isParent = !empty($payload['is_parent']);
$noSecondInfo = !empty($payload['no_second_parent_info']);
$formatted = $this->formatter->format($payload);
$email = (string) ($formatted['email'] ?? '');
$existing = User::query()->where('email', $email)->first();
if ($existing) {
$pending = !empty($existing->token) && (int) ($existing->is_verified ?? 0) === 0;
if ($pending) {
return [
'ok' => false,
'code' => 'pending_activation',
'message' => 'This email address is already registered and pending activation.',
];
}
return [
'ok' => false,
'code' => 'email_exists',
'message' => 'The email address you entered is already in use.',
];
}
$roleName = $isParent ? 'parent' : 'guest';
$role = Role::query()->where('name', $roleName)->first();
if (!$role) {
return [
'ok' => false,
'code' => 'role_missing',
'message' => 'Role not found.',
];
}
$token = bin2hex(random_bytes(48));
$schoolYear = Configuration::getConfig('school_year');
$semester = Configuration::getConfig('semester');
$userData = [
'firstname' => (string) ($formatted['firstname'] ?? ''),
'lastname' => (string) ($formatted['lastname'] ?? ''),
'gender' => (string) ($formatted['gender'] ?? ''),
'email' => $email,
'cellphone' => (string) ($formatted['cellphone'] ?? ($payload['cellphone'] ?? '')),
'address_street' => (string) ($formatted['address_street'] ?? ''),
'apt' => $formatted['apt'] ?? null,
'city' => (string) ($formatted['city'] ?? ''),
'state' => (string) ($formatted['state'] ?? ''),
'zip' => (string) ($formatted['zip'] ?? ''),
'token' => $token,
'is_verified' => 0,
'accept_school_policy' => (int) ($formatted['accept_school_policy'] ?? 0),
'status' => 'Inactive',
'school_id' => $this->schoolIdService->generateUserSchoolId(),
'semester' => (string) ($semester ?? ''),
'school_year' => $schoolYear,
'password' => pbkdf2_hash(bin2hex(random_bytes(8))),
];
$user = null;
DB::transaction(function () use (
$userData,
$role,
$isParent,
$noSecondInfo,
$formatted,
$schoolYear,
$semester,
&$user
) {
$user = User::query()->create($userData);
UserRole::query()->create([
'user_id' => (int) $user->id,
'role_id' => (int) $role->id,
'created_at' => now(),
]);
if ($isParent && !$noSecondInfo && !empty($formatted['second_firstname'])) {
ParentModel::query()->create([
'secondparent_firstname' => $formatted['second_firstname'] ?? null,
'secondparent_lastname' => $formatted['second_lastname'] ?? null,
'secondparent_gender' => $formatted['second_gender'] ?? null,
'secondparent_email' => $formatted['second_email'] ?? null,
'secondparent_phone' => $formatted['second_cellphone'] ?? null,
'firstparent_id' => (int) $user->id,
'semester' => $semester,
'school_year' => $schoolYear,
]);
}
});
$activationLink = url('/user/confirm/' . $token);
$recipientName = trim((string) ($formatted['firstname'] ?? '') . ' ' . (string) ($formatted['lastname'] ?? ''));
$emailData = [
'recipientName' => $recipientName,
'activationLink' => $activationLink,
'orgName' => 'Al Rahma Sunday School',
'contactInfo' => 'alrahma.isgl@gmail.com',
'logoUrl' => 'https://alrahmaisgl.org/assets/images/alrahma_logo.png',
'subject' => 'Email Confirmation',
];
if (view()->exists($isParent ? 'emails.welcome_parent' : 'emails.welcome_staff')) {
$html = view($isParent ? 'emails.welcome_parent' : 'emails.welcome_staff', $emailData);
} else {
$html = '<p>Hello ' . e($recipientName !== '' ? $recipientName : 'there') . ',</p>'
. '<p>Please confirm your email by clicking the link below:</p>'
. '<p><a href="' . e($activationLink) . '">Activate your account</a></p>'
. '<p>Thank you.</p>';
}
$sent = $this->emailService->send($email, 'Email Confirmation', $html, 'general');
$this->captchaService->clear();
return [
'ok' => true,
'user' => $user,
'role' => $roleName,
'activation_sent' => $sent,
];
}
}