تصميم الـ DTOs في لارافيل: إزاي تخلي كودك نظيف ومحمي من الفوضى؟
أكيد مريت بالموقف ده: بتبدأ مشروع لارافيل (Laravel) صغير، الـ Controller بيكون بسيط، وبتبعت الـ Request مباشرة للـ Model أو للـ Database. مع الوقت، المشروع بيكبر، الـ Controller بيتحول لـ "محرقة كود" (Fat Controller)، وكل حتة في الكود معتمدة على الـ Request Object مباشرة. ده مش بس بيخلي الكود صعب يتقري، ده كمان بيخلي أي تغيير بسيط في الـ Database يهد المشروع كله.
النهاردة هنتكلم عن بطل خارق في عالم البرمجة النظيفة (Clean Code) وهو الـ DTOs أو كائنات نقل البيانات (Data Transfer Objects). فكرتها ببساطة إننا بننقل الداتا من مكان للتاني في "قالب" محدد، بدل ما نبعتها وهي سايبة كده.
Table of contents [Show]
يعني إيه DTO وليه لازم نستخدمه؟
الـ DTO هو كلاس (Class) بسيط جداً، مهمته الوحيدة إنه "يشيل" البيانات من الـ Controller ويوصلها للـ Service Layer أو الـ Repository. تخيل إنك بتبعت جواب في ظرف مغلف، مش بتبعت الورق متناثر. ده بيضمن إن الـ Service عارفة بالظبط إيه البيانات اللي هتوصلها، وبيحمينا من إننا نبعت بيانات زيادة أو ناقصة.
من أهم فوائد الـ DTO في لارافيل:
- الوضوح (Type Safety): بتبقى عارف إيه أنواع البيانات اللي داخلة (String, Integer, Boolean) وده بيقلل الـ Bugs.
- سهولة الاختبار (Unit Testing): تقدر تعمل اختبار للكود بتاعك من غير ما تضطر تعمل محاكاة (Mocking) للـ Request Object المعقد.
- الاستقلالية (Decoupling): الـ Service الخاصة بيك مش مرتبطة بالـ HTTP Request، يعني تقدر تستخدم نفس الـ Service من الـ CLI أو من Cron Job من غير مشاكل.
إزاي نصمم الـ DTO في لارافيل؟
من أول PHP 8.2 وإحنا عندنا مميزات خرافية زي الـ Readonly Classes والـ Constructor Property Promotion، واللي بتخلي كتابة الـ DTO أسهل بكتير. تعالوا نشوف مثال عملي لـ DTO بيستقبل بيانات مستخدم جديد:
readonly class CreateUserDTO
{
public function __construct(
public string $name,
public string $email,
public string $password,
public ?string $phoneNumber = null,
) {}
public static function fromRequest(Request $request): self
{
return new self(
name: $request->validated('name'),
email: $request->validated('email'),
password: $request->validated('password'),
phoneNumber: $request->validated('phone_number'),
);
}
}
زي ما أنت شايف، استخدمنا Static Factory Method باسم fromRequest عشان نحول الـ Request للـ DTO بسهولة جوه الـ Controller.
استخدام الـ DTO داخل الـ Controller
دلوقتي الكود بتاعك في الـ Controller بقى نضيف جداً ومفهوم:
public function store(RegisterRequest $request, UserService $service)
{
$dto = CreateUserDTO::fromRequest($request);
$user = $service->register($dto);
return response()->json($user, 201);
}
كده الـ UserService مش مهتمة خالص إن الداتا جاية من HTTP Request، هي مستنية CreateUserDTO، وده بيخلي الـ Architecture بتاعك احترافي وقابل للتوسع (Scalable).
نصيحة من أخ لمبرمج زميل
يا بطل، مش لازم تستخدم الـ DTOs في كل صغيرة وكبيرة في المشاريع اللي حجمها صغير جداً، عشان ما تقعش في فخ الـ "Over-engineering". لكن، أول ما تحس إن الـ Controllers بدأت تتقل، أو إنك بتعدل في الـ Database وبتضطر تعدل في الـ Services والـ Controllers مع بعض، هنا لازم تبدأ فوراً في استخدام الـ DTOs.
طور مهاراتك في الـ PHP الحديث، واهتم دائماً بفصل المهام (Separation of Concerns). الكود اللي بتكتبه النهاردة هو اللي هيريحك (أو يتعبك) بكره لما المشروع يكبر ويطلب منك تحديثات جديدة.