Merhaba, bi projemde servisler kullanarak business logicleri controllerdan kaldırdım. Ama aklıma takılan bi yer var.
Kullanıcının yolladığı verileri DTO içinde validate ediyorum. Mesela e-posta adresi unique mi uygun formatta mı vs. Aynı şekilde TC kimlik no için de doğrulama yapıyorum. Ayrıca TC için format kontrolü de yapıyorum. Örnek kodlarım şöyle:
RegisterData:
<?php
namespace App\Data\Auth;
use App\Rules\ValidIdentityNumber;
use Spatie\LaravelData\Attributes\MapName;
use Spatie\LaravelData\Attributes\Validation\{Bail, Email, Exists, IntegerType, Max, Min, Required, StringType, Unique};
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
#[MapName(SnakeCaseMapper::class)]
class RegisterData extends Data
{
public function __construct(
public string $identityNumber,
#[Bail, Required, StringType, Max(255)]
public string $firstName,
#[Bail, Required, StringType, Max(255)]
public string $lastName,
#[Bail, Required, Email, Max(255)]
public string $email,
#[Bail, Required, IntegerType, Exists('countries', 'id')]
public string $phoneCountryId,
#[Bail, Required, StringType, Min(10), Max(255), Unique('users', 'phone')]
public string $phone,
#[Bail, Required, StringType, Max(255)]
public string $password,
public string $birthdate
) {}
public static function stopOnFirstFailure(): bool
{
return true;
}
public static function attributes(): array
{
return [
'identity_number' => __('T.C. Kimlik Numarası'),
'first_name' => __('Ad'),
'last_name' => __('Soyad'),
'birthdate' => __('Doğum Tarihi'),
'email' => __('E-posta adresi'),
'phone_country_id' => __('Ülke Kodu'),
'phone' => __('Telefon Numarası'),
'password' => __('Parola'),
];
}
public static function rules(): array
{
return [
'identityNumber' => [
'bail',
'required',
'numeric',
'digits:11',
new ValidIdentityNumber,
'unique:users,identity_number',
],
'birthdate' => [
'bail',
'required',
'date',
'date_format:Y-m-d',
'before:' . now()->subYears(18)->format('Y-m-d'),
],
];
}
public static function messages(): array
{
return [
'birthdate' => [
'before' => __('En az 18 yaşında olmalısınız.'),
],
];
}
}
ValidIdentityNumber:
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidIdentityNumber implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
// Rakam değilse veya uzunluğu 11 değilse direkt false
if (!is_numeric($value) || strlen($value) !== 11) {
$fail(__('Geçerli bir T.C. Kimlik Numarası girin.'));
}
// İlk rakam 0 olamaz
if ($value[0] === '0') {
$fail(__('Geçerli bir T.C. Kimlik Numarası girin.'));
}
$digits = array_map('intval', str_split($value));
$sumOdd = $digits[0] + $digits[2] + $digits[4] + $digits[6] + $digits[8];
$sumEven = $digits[1] + $digits[3] + $digits[5] + $digits[7];
$digit10 = (($sumOdd * 7) - $sumEven) % 10;
$digit11 = (array_sum(array_slice($digits, 0, 10))) % 10;
if ($digits[9] !== $digit10 || $digits[10] !== $digit11) {
$fail(__('Geçerli bir T.C. Kimlik Numarası girin.'));
}
}
}
AuthController:
?php
namespace App\Http\Controllers;
use App\Data\Auth\{ForgotPasswordData, LoginData, RegisterData, ResetPasswordData};
use App\Http\Resources\User\{AccesTokenResource, UserResource};
use App\Integrations\Nvi\TCKimlikNoDogrulaClient;
use App\Services\AuthService;
use App\Traits\HasResponse;
use Illuminate\Http\{JsonResponse, Request, Response};
class AuthController extends Controller
{
use HasResponse;
public function __construct(private AuthService $service) {}
public function register(RegisterData $data, TCKimlikNoDogrulaClient $client): JsonResponse
{
$user = $this->service->register($data, $client);
$this->service->login(new LoginData($user->email, $data->password));
return $this->successResponse(__('Kayıt başarılı!'), [
'user' => new UserResource($user)
], 201);
}
...
AuthService:
<?php
namespace App\Services;
use App\Data\Auth\{ForgotPasswordData, LoginData, RegisterData, ResetPasswordData, TokenData};
use App\Exceptions\Auth\{InvalidCredentialsException, InvalidIdentityNumberException, InvalidTokenException, PasswordResetThrottledException};
use App\Integrations\Nvi\TCKimlikNoDogrulaClient;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\{Auth, Hash, Password};
use Illuminate\Support\Str;
class AuthService
{
...
/**
* @param RegisterData $data
* @param TCKimlikNoDogrulaClient $client
*
* @return User
*/
public function register(RegisterData $data, TCKimlikNoDogrulaClient $client): User
{
$birthYear = Carbon::parse($data->birthdate)->format('Y');
if (!$client->validate(
$data->identityNumber,
$data->firstName,
$data->lastName,
$birthYear
)) {
throw new InvalidIdentityNumberException();
}
return User::create($data->toArray());
}
...
Şimdi sormak istediğim şu: TC Kimlik için NVI doğrulamasını servis katmanında mı yapmalıyım? Bazı doğrulamalar DTO'da bazıları servis içinde olunca karışık bi hal almıyor mu? Bu durumda best practice nedir sizce?