ValidatedDTO
ValidatedDTO cho phép định nghĩa và validate dữ liệu đầu vào một cách rõ ràng và type-safe bằng PHP Attributes.
Tạo DTO cơ bản
<?php
namespace App\DTOs;
use Vietiso\Core\ValidatedDTO\ValidatedDTO;
use Vietiso\Core\ValidatedDTO\Rules\Required;
use Vietiso\Core\ValidatedDTO\Rules\Email;
use Vietiso\Core\ValidatedDTO\Rules\Min;
use Vietiso\Core\ValidatedDTO\Rules\Max;
class CreateUserDTO extends ValidatedDTO
{
#[Required]
#[Min(2)]
#[Max(100)]
public string $name;
#[Required]
#[Email]
public string $email;
#[Required]
#[Min(8)]
public string $password;
}
Sử dụng trong Controller
use App\DTOs\CreateUserDTO;
use Vietiso\Core\Route\Attributes\Post;
#[Post('/users')]
public function store(CreateUserDTO $dto)
{
// $dto đã được validate tự động
$user = User::create([
'name' => $dto->name,
'email' => $dto->email,
'password' => $dto->password,
]);
return $user;
}
Các Validation Rules
Required & Nullable
#[Required]
public string $name;
#[Nullable]
public ?string $bio;
// Required với điều kiện
#[RequiredIf('type', 'business')]
public ?string $company_name;
Type Rules
#[TypeString]
public string $name;
#[TypeInteger]
public int $age;
#[TypeFloat]
public float $price;
#[TypeBoolean]
public bool $is_active;
#[TypeList] // Mảng tuần tự [1, 2, 3]
public array $tags;
#[TypeAssociativeArray] // Mảng kết hợp ['key' => 'value']
public array $metadata;
#[TypeEnum(UserStatus::class)]
public UserStatus $status;
String Rules
#[Alpha] // Chỉ chữ cái
public string $code;
#[AlphaNum] // Chữ cái và số
public string $username;
#[AlphaDash] // Chữ cái, số, dấu gạch ngang, gạch dưới
public string $slug;
#[AlphaSpaces] // Chữ cái và khoảng trắng
public string $full_name;
#[Lowercase]
public string $code;
#[Uppercase]
public string $country_code;
#[StartWith('VN')]
public string $phone;
#[Regex('/^[A-Z]{2}\d{6}$/')]
public string $passport;
#[NotRegex('/[<>]/')]
public string $content;
Size Rules
#[Min(2)]
#[Max(100)]
public string $name; // String length
#[Min(0)]
#[Max(100)]
public int $quantity; // Numeric value
#[Min(1)]
#[Max(10)]
public array $items; // Array count
#[Size(10)] // Exactly 10
public string $phone;
// So sánh với field khác
#[Gt('min_price')] // Greater than
public float $max_price;
#[Gte('min_value')] // Greater than or equal
public int $value;
Format Rules
#[Email]
public string $email;
#[Url]
public string $website;
#[Ip]
public string $ip_address;
#[Ipv4]
public string $ipv4;
#[Ipv6]
public string $ipv6;
#[MacAddress]
public string $mac;
#[Phone] // Sử dụng libphonenumber
public string $phone;
#[Date]
public string $birthday;
#[Json]
public string $json_data;
#[CssColor]
public string $theme_color;
#[Password(min: 8, letters: true, numbers: true, symbols: true)]
public string $password;
In / Not In
#[In(['admin', 'user', 'guest'])]
public string $role;
#[NotIn(['banned', 'deleted'])]
public string $status;
File Rules
#[File]
public UploadedFile $document;
#[Image] // jpeg, png, gif, webp, svg
public UploadedFile $avatar;
#[UploadedFile(maxSize: 5120)] // Max 5MB
public UploadedFile $file;
#[MimeType(['image/jpeg', 'image/png'])]
public UploadedFile $photo;
#[Extension(['pdf', 'doc', 'docx'])]
public UploadedFile $document;
Database Rules
// Kiểm tra tồn tại trong database
#[Exists('users', 'id')]
public int $user_id;
// Kiểm tra unique
#[Unique('users', 'email')]
public string $email;
// Unique với ignore (cho update)
#[Unique('users', 'email', ignore: 'id')]
public string $email;
Other Rules
#[Accepted] // true, 'yes', 'on', 1, '1'
public mixed $terms;
#[Distinct] // Không có giá trị trùng lặp trong array
public array $emails;
#[Before('end_date')]
public string $start_date;
#[BeforeOrEqual('2024-12-31')]
public string $deadline;
#[Equal('password')] // Phải bằng field password
public string $password_confirmation;
Bail - Dừng validation khi có lỗi
#[Bail]
#[Required]
#[Email]
#[Unique('users', 'email')]
public string $email;
// Nếu Required fail -> không chạy Email và Unique
Custom Validation
use Vietiso\Core\ValidatedDTO\Rules\Custom;
#[Custom(callback: 'validateSlug')]
public string $slug;
public function validateSlug(string $value): bool|string
{
if (str_contains($value, ' ')) {
return 'Slug cannot contain spaces';
}
return true;
}
Làm việc với DTO
Chuyển đổi dữ liệu
// Lấy tất cả data
$data = $dto->all();
$data = $dto->toArray();
// Lấy một số fields
$data = $dto->only(['name', 'email']);
// Loại trừ một số fields
$data = $dto->except(['password']);
// Chuyển sang JSON
$json = $dto->toJson();
Tạo DTO từ nhiều nguồn
// Từ array
$dto = CreateUserDTO::fromArray([
'name' => 'John',
'email' => 'john@example.com',
'password' => 'secret123',
]);
// Từ JSON
$dto = CreateUserDTO::fromJson('{"name": "John", ...}');
Xử lý lỗi Validation
Khi validation fail, ValidationException sẽ được throw tự động. Response mặc định:
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email field is required."],
"password": ["The password must be at least 8 characters."]
}
}
Custom Error Handling
Override phương thức failedValidation trong DTO:
<?php
namespace App\DTOs;
use Vietiso\Core\ValidatedDTO\ValidatedDTO;
use Vietiso\Core\ValidatedDTO\ValidationException;
use Vietiso\Core\Http\Response;
use Vietiso\Core\Http\Request;
class CreateUserDTO extends ValidatedDTO
{
// ...properties
public static function failedValidation(Request $request, ValidationException $th): Response
{
return Response::json([
'success' => false,
'message' => 'Validation failed',
'errors' => $th->getErrors()
])->setStatusCode(422);
}
}
Nested DTO
<?php
namespace App\DTOs;
use Vietiso\Core\ValidatedDTO\ValidatedDTO;
use Vietiso\Core\ValidatedDTO\Rules\Required;
class AddressDTO extends ValidatedDTO
{
#[Required]
public string $street;
#[Required]
public string $city;
public ?string $zip;
}
class CreateOrderDTO extends ValidatedDTO
{
#[Required]
public int $user_id;
#[Required]
public AddressDTO $shipping_address;
#[Required]
public AddressDTO $billing_address;
}