
معمارية MVC في PHP
تُعد MVC واحدة من أهم الأنماط المعمارية في تطوير الويب. فهي تساعد المطورين على تنظيم الكود ضمن طبقات واضحة بدل خلط استعلامات قاعدة البيانات وHTML والتحقق من المدخلات ومنطق العمل داخل الملف نفسه.
في PHP، تُستخدم MVC كثيراً في أطر العمل الحديثة مثل Laravel وSymfony وCodeIgniter، وكذلك في العديد من التطبيقات المخصصة. فهم MVC يساعد المطور على كتابة مشاريع PHP أنظف وأسهل في الصيانة وأكثر قابلية للتوسع.
يشرح هذا المقال معمارية MVC في PHP بالتفصيل، بما في ذلك معنى MVC، ودور النماذج، والعروض، والمتحكمات، والتوجيه، وبنية المجلدات، وطبقة قاعدة البيانات، ومعالجة الطلبات، والتحقق، وكيف تعمل MVC داخل تطبيقات PHP حقيقية.
ما هي MVC؟
MVC هي اختصار لـ Model وView وController. وهي نمط معماري برمجي يقسم التطبيق إلى ثلاثة أجزاء رئيسية.
Model: يتعامل مع البيانات وعمليات قاعدة البيانات وقواعد العمل.
View: يتعامل مع واجهة المستخدم ويعرض مخرجات HTML.
Controller: يستقبل الطلبات، وينسق المنطق، ويعيد الاستجابات.
الهدف الأساسي من MVC هو فصل المسؤوليات. كل جزء له وظيفة محددة، وهذا يجعل المشروع أسهل في الفهم والاختبار والصيانة والتطوير.
بدون MVC، قد يحتوي ملف PHP واحد على HTML واستعلامات SQL والتحقق من البيانات وإعادة التوجيه وفحص الجلسات ومنطق العمل كلها معاً. قد ينجح هذا في السكربتات الصغيرة، لكنه يصبح صعب الإدارة عندما يكبر المشروع.
لماذا MVC مهمة في PHP؟
PHP لغة مرنة وتسمح للمطورين بكتابة الكود بطرق كثيرة. غالباً يبدأ المبتدئون بكتابة PHP مباشرة داخل ملفات HTML. هذا جيد لتعلم الصياغة الأساسية، لكن التطبيقات الأكبر تحتاج إلى تنظيم أفضل.
تُعد MVC مهمة لأنها تساعد على حل مشكلات شائعة في مشاريع PHP المتنامية:
تفصل منطق قاعدة البيانات عن مخرجات HTML.
تحافظ على معالجة الطلبات داخل المتحكمات.
تجعل العروض أنظف وأسهل في التعديل.
تقلل تكرار الكود.
تحسن قابلية الصيانة.
تجعل المشروع أسهل في التوسع.
تجهز المطورين للعمل مع أطر مثل Laravel وSymfony.
عندما يتبع المشروع نمط MVC، يستطيع المطورون العمل على أجزاء مختلفة بسهولة أكبر. على سبيل المثال، يمكن لمطور أن يعدل تصميم العرض بينما يعمل مطور آخر على منطق قاعدة البيانات.
Model في PHP MVC
الـ Model مسؤول عن البيانات وقواعد العمل. في تطبيق PHP، تتواصل النماذج عادة مع قاعدة البيانات باستخدام PDO أو query builders أو أدوات ORM.
على سبيل المثال، في تطبيق مدونة، يمكن أن يكون نموذج Post مسؤولاً عن جلب المقالات، والعثور على مقالة واحدة، وإنشاء مقالة جديدة، وتحديث مقالة، وحذف مقالة.
<?php
class Post
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function all(): array
{
$statement = $this->pdo->query("
SELECT id, title, content, created_at
FROM posts
ORDER BY id DESC
");
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
public function find(int $id): ?array
{
$statement = $this->pdo->prepare("
SELECT id, title, content, created_at
FROM posts
WHERE id = :id
LIMIT 1
");
$statement->execute([
"id" => $id
]);
$post = $statement->fetch(PDO::FETCH_ASSOC);
return $post ?: null;
}
}
?>هذا النموذج يركز فقط على عمليات البيانات. لا يطبع HTML ولا يتحكم مباشرة في تخطيط الصفحة.
في التطبيقات الأكبر، قد تتضمن النماذج أيضاً العلاقات، وقواعد التحقق، ودوال العمل، ومنطق المجال. في أطر مثل Laravel، ترتبط النماذج غالباً بجداول قاعدة البيانات من خلال ORM اسمه Eloquent.
View في PHP MVC
الـ View مسؤول عن عرض البيانات للمستخدم. عادة يحتوي على HTML مع تعبيرات PHP صغيرة لطباعة المتغيرات والحلقات والشروط.
على سبيل المثال، قد يبدو ملف عرض يعرض قائمة مقالات بهذا الشكل:
<h1>Blog Posts</h1>
<?php foreach ($posts as $post): ?>
<article>
<h2><?php echo htmlspecialchars($post["title"], ENT_QUOTES, "UTF-8"); ?></h2>
<p><?php echo htmlspecialchars($post["content"], ENT_QUOTES, "UTF-8"); ?></p>
</article>
<?php endforeach; ?>يجب أن يركز العرض على التقديم البصري. لا يفترض أن يحتوي على استعلامات قاعدة بيانات أو منطق عمل معقد.
العروض مفيدة لأن المصممين ومطوري الواجهة الأمامية يستطيعون العمل على بنية HTML دون الحاجة إلى فهم كل تفاصيل طبقة قاعدة البيانات.
في Laravel، تُكتب العروض عادة باستخدام قوالب Blade. أما في PHP MVC الصافية، فيمكن استخدام ملفات PHP عادية كقوالب.
Controller في PHP MVC
الـ Controller يستقبل الطلب، ويستدعي دوال النموذج المطلوبة، ويجهز البيانات، ثم يحمل العرض المناسب.
على سبيل المثال، يمكن أن يتعامل PostController مع عرض قائمة المقالات وعرض مقالة واحدة.
<?php
class PostController
{
private Post $postModel;
public function __construct(Post $postModel)
{
$this->postModel = $postModel;
}
public function index(): void
{
$posts = $this->postModel->all();
require __DIR__ . "/../views/posts/index.php";
}
public function show(int $id): void
{
$post = $this->postModel->find($id);
if (!$post) {
http_response_code(404);
echo "Post not found.";
return;
}
require __DIR__ . "/../views/posts/show.php";
}
}
?>في هذا المثال، لا يكتب المتحكم SQL مباشرة. هو يطلب البيانات من النموذج ثم يرسلها إلى العرض.
المتحكمات مفيدة لتنظيم سلوك التطبيق. يمكنها التعامل مع إرسال النماذج، وإعادة التوجيه، والتحقق، وفحص تسجيل الدخول، واستجابات API.
التوجيه في PHP MVC
التوجيه يربط عناوين URL بدوال المتحكمات. بدلاً من فتح كل ملف PHP مباشرة من المتصفح، يستطيع التطبيق تمرير الطلبات عبر نقطة دخول مركزية.
على سبيل المثال:
/posts يمكن أن يستدعي PostController@index.
/posts/show?id=5 يمكن أن يستدعي PostController@show.
/posts/create يمكن أن يستدعي PostController@create.
/posts/store يمكن أن يستدعي PostController@store.
يمكن كتابة router بسيط جداً باستخدام request URI.
<?php
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if ($uri === "/posts") {
$controller->index();
} elseif ($uri === "/posts/show") {
$id = (int) ($_GET["id"] ?? 0);
$controller->show($id);
} else {
http_response_code(404);
echo "Page not found.";
}
?>هذا مثال توجيه أساسي. الموجهات الحقيقية أكثر تقدماً، ويمكنها التعامل مع معاملات المسارات، وطرق HTTP، والـ middleware، والمسارات المسماة، ومجموعات المسارات.
توفر أطر مثل Laravel أنظمة توجيه قوية، لكن فهم router بسيط يساعدك على فهم ما يحدث خلف الكواليس.
نمط Front Controller
تستخدم كثير من تطبيقات MVC نمط front controller. هذا يعني أن كل الطلبات تدخل إلى التطبيق من خلال ملف رئيسي واحد، غالباً public/index.php.
يكون الـ front controller مسؤولاً عن تحميل الإعدادات، وتشغيل التطبيق، وتسجيل المسارات، وتمرير الطلب إلى المتحكم الصحيح.
يمكن أن يبدو front controller بسيط بهذا الشكل:
<?php
require __DIR__ . "/../config/database.php";
require __DIR__ . "/../app/Models/Post.php";
require __DIR__ . "/../app/Controllers/PostController.php";
$postModel = new Post($pdo);
$controller = new PostController($postModel);
$uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
if ($uri === "/posts") {
$controller->index();
} else {
http_response_code(404);
echo "Page not found.";
}
?>يوفر front controller للتطبيق نقطة دخول واحدة مضبوطة بدلاً من كشف ملفات PHP الداخلية الكثيرة للمتصفح.
هذه البنية شائعة في أطر العمل الحديثة. Laravel وSymfony والعديد من الأطر الأخرى تستخدم نقطة دخول عامة تبدأ التطبيق.
بنية مجلدات PHP MVC
يمكن تنظيم مشروع PHP MVC بسيط في مجلدات للنماذج، والعروض، والمتحكمات، والإعدادات، والملفات العامة.
project/
├── app/
│ ├── Controllers/
│ │ └── PostController.php
│ ├── Models/
│ │ └── Post.php
│ └── Views/
│ └── posts/
│ ├── index.php
│ └── show.php
├── config/
│ └── database.php
├── public/
│ └── index.php
└── routes/
└── web.phpيحتوي مجلد app على كود التطبيق الأساسي. يحتوي مجلد Controllers على الأصناف التي تعالج الطلبات. يحتوي مجلد Models على الأصناف المرتبطة بالبيانات. ويحتوي مجلد Views على القوالب.
يخزن مجلد config ملفات الإعدادات مثل إعدادات الاتصال بقاعدة البيانات. أما مجلد public فهو المجلد المتاح من الويب ويحتوي على نقطة الدخول الرئيسية.
هذه البنية تحافظ على تنظيم المشروع وتمنع وصول المستخدمين مباشرة إلى ملفات التطبيق الداخلية.
طبقة قاعدة البيانات في MVC
عادة تتم إدارة طبقة قاعدة البيانات من خلال النماذج أو أصناف repository. وظيفتها التواصل مع قاعدة البيانات وإرجاع البيانات إلى المتحكم.
قد يبدو ملف إعداد قاعدة البيانات بهذا الشكل:
<?php
$pdo = new PDO(
"mysql:host=localhost;dbname=php_mvc;charset=utf8mb4",
"root",
""
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
?>يمكن للنموذج استخدام اتصال PDO هذا لتشغيل العبارات المحضرة.
<?php
class User
{
public function __construct(private PDO $pdo)
{
}
public function findByEmail(string $email): ?array
{
$statement = $this->pdo->prepare("
SELECT id, name, email
FROM users
WHERE email = :email
LIMIT 1
");
$statement->execute([
"email" => $email
]);
$user = $statement->fetch();
return $user ?: null;
}
}
?>إبقاء منطق قاعدة البيانات داخل النماذج أو repositories يجعل المتحكمات أنظف وأسهل في القراءة.
معالجة الطلبات في MVC
معالجة الطلبات تعني استقبال المدخلات من المتصفح أو عميل API. قد تأتي هذه المدخلات من النماذج، أو query strings، أو طلبات JSON، أو الملفات المرفوعة، أو الكوكيز، أو الجلسات.
في MVC، يستقبل المتحكم عادة بيانات الطلب ويقرر ماذا يفعل بها.
<?php
class ContactController
{
public function store(): void
{
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
$message = trim($_POST["message"] ?? "");
if ($name === "" || !filter_var($email, FILTER_VALIDATE_EMAIL) || $message === "") {
echo "Invalid contact form.";
return;
}
echo "Message received.";
}
}
?>في مشروع أكبر، يمكن نقل التحقق من الطلب إلى صنف validator منفصل أو صنف request. هذا يجعل المتحكمات أصغر وأكثر تركيزاً.
توفر أطر مثل Laravel أصناف request وقواعد تحقق لتنظيم هذه العملية.
التحقق في MVC
التحقق هو عملية التأكد من أن بيانات الإدخال مقبولة. في تطبيقات MVC، يمكن أن يحدث التحقق داخل المتحكم أو النموذج أو الخدمة أو صنف تحقق منفصل.
في المشاريع البسيطة، قد يكون التحقق داخل المتحكم كافياً. أما في المشاريع الأكبر، فإن فصل التحقق يجعل الكود أنظف.
<?php
class PostValidator
{
public function validate(array $data): array
{
$errors = [];
$title = trim($data["title"] ?? "");
$content = trim($data["content"] ?? "");
if ($title === "") {
$errors["title"] = "Title is required.";
}
if (strlen($title) > 200) {
$errors["title"] = "Title must not exceed 200 characters.";
}
if ($content === "") {
$errors["content"] = "Content is required.";
}
return $errors;
}
}
?>بعد ذلك يستطيع المتحكم استخدام الـ validator:
<?php
$validator = new PostValidator();
$errors = $validator->validate($_POST);
if (count($errors) > 0) {
require __DIR__ . "/../views/posts/create.php";
return;
}
?>هذا يجعل منطق التحقق قابلاً لإعادة الاستخدام وأسهل في الاختبار.
عرض القوالب
عرض القالب يعني تحميل template وتمرير البيانات إليه. في PHP الصافية، يمكن إنشاء view renderer بسيط باستخدام extract() وrequire.
<?php
function view(string $path, array $data = []): void
{
extract($data);
require __DIR__ . "/../app/Views/" . $path . ".php";
}
?>يمكن للمتحكم استدعاء مساعد العرض بهذا الشكل:
<?php
public function index(): void
{
$posts = $this->postModel->all();
view("posts/index", [
"posts" => $posts
]);
}
?>بعد ذلك يستطيع ملف العرض الوصول إلى متغير $posts.
<?php foreach ($posts as $post): ?>
<h2><?php echo htmlspecialchars($post["title"], ENT_QUOTES, "UTF-8"); ?></h2>
<?php endforeach; ?>الـ view renderer تحسين صغير لكنه مفيد لأنه يوفر طريقة موحدة لتحميل القوالب.
Layouts في PHP MVC
معظم المواقع تحتوي على أجزاء مشتركة مثل الرأس، والتنقل، والتذييل. غالباً تستخدم تطبيقات MVC layouts لتجنب تكرار نفس HTML في كل عرض.
يمكن أن يحتوي ملف layout بسيط على بنية HTML الرئيسية:
<!DOCTYPE html>
<html>
<head>
<title><?php echo htmlspecialchars($title ?? "PHP MVC", ENT_QUOTES, "UTF-8"); ?></title>
</head>
<body>
<header>
<nav>
<a href="/posts">Posts</a>
</nav>
</header>
<main>
<?php echo $content; ?>
</main>
<footer>
<p>PHP MVC Application</p>
</footer>
</body>
</html>تستخدم أنظمة القوالب الأكثر تقدماً output buffering لإدخال محتوى الصفحة داخل layout.
<?php
ob_start();
require __DIR__ . "/posts/index.php";
$content = ob_get_clean();
require __DIR__ . "/layout.php";
?>تساعد layouts على إبقاء الواجهة متناسقة بين الصفحات وتقليل تكرار HTML.
MVC وCRUD
تطبيقات CRUD طريقة جيدة لفهم MVC. يمكن أن تحتوي وحدة المقالات على model وcontroller وviews لعرض المقالات وإنشائها وتعديلها وحذفها.
يمكن أن يحتوي PostController بسيط على دوال مثل:
index() لعرض قائمة المقالات.
show() لعرض مقالة واحدة.
create() لعرض نموذج.
store() لحفظ مقالة جديدة.
edit() لعرض نموذج التعديل.
update() لتحديث مقالة.
destroy() لحذف مقالة.
يتعامل النموذج مع عمليات قاعدة البيانات، وينسق المتحكم الطلب، ويعرض الـ View النماذج والبيانات.
هذه البنية قريبة جداً من طريقة عمل controllers وresources في Laravel.
MVC وREST API
يمكن استخدام MVC أيضاً مع REST APIs. في هذه الحالة، يعيد المتحكم JSON بدلاً من تحميل عرض HTML.
<?php
class ApiPostController
{
public function __construct(private Post $postModel)
{
}
public function index(): void
{
header("Content-Type: application/json");
echo json_encode([
"success" => true,
"data" => $this->postModel->all()
]);
}
}
?>في متحكمات API، يبقى النموذج مسؤولاً عن البيانات، لكن الاستجابة تكون عادة JSON. وهذا يجعل MVC مفيدة للمواقع التقليدية التي تُعرض من الخادم، وكذلك للتطبيقات المعتمدة على API.
قد تحتوي بنية REST API MVC على متحكمات منفصلة مثل PostApiController وUserApiController وAuthApiController.
فوائد معمارية MVC في PHP
توفر MVC عدة فوائد عملية لتطبيقات PHP.
كود أنظف: كل جزء له مسؤولية واضحة.
تنظيم أفضل: تُجمع الملفات حسب الغرض منها.
إعادة الاستخدام: يمكن إعادة استخدام النماذج والخدمات في متحكمات مختلفة.
قابلية الصيانة: تصبح التغييرات أسهل لأن المنطق مفصول.
قابلية التوسع: يمكن للبنية أن تكبر مع المشروع.
تعاون الفريق: يمكن فصل عمل الواجهة الخلفية والواجهة الأمامية بوضوح أكبر.
الاستعداد لأطر العمل: تجهز MVC المطورين للعمل مع Laravel وSymfony والأطر المشابهة.
في السكربتات الصغيرة، قد تبدو MVC كعمل إضافي. لكن في التطبيقات التي ستكبر، تساعد MVC على منع فوضى الكود والمنطق المتكرر.
أخطاء شائعة في PHP MVC
أحياناً يسيء المبتدئون فهم MVC ويضعون الكود في الطبقة الخطأ. هذا يقلل فوائد النمط.
من الأخطاء الشائعة:
كتابة استعلامات SQL مباشرة داخل العروض.
وضع الكثير من منطق العمل داخل المتحكمات.
استخدام النماذج كحاويات بيانات فارغة فقط.
خلط مخرجات HTML داخل النماذج.
تجاهل التحقق أو وضعه بشكل غير متناسق.
كشف ملفات التطبيق الداخلية للعامة.
إنشاء متحكمات ضخمة بمسؤوليات كثيرة.
تطبيق MVC الجيد يحافظ على تركيز العروض على العرض، والمتحكمات على سير الطلب، والنماذج على البيانات وقواعد العمل.
ملاحظات أمان في PHP MVC
تحسن MVC تنظيم التطبيق، لكنها لا تجعل التطبيق آمناً تلقائياً. يجب تطبيق الأمان في كل طبقة.
من الممارسات الأمنية المهمة:
استخدم العبارات المحضرة داخل النماذج وطبقات قاعدة البيانات.
تحقق من بيانات الطلب قبل حفظها.
اهرب المخرجات داخل العروض باستخدام htmlspecialchars().
احمِ النماذج باستخدام CSRF tokens.
افحص تسجيل الدخول والصلاحيات داخل المتحكمات أو middleware.
لا تكشف مجلدات داخلية مثل app أو config أو routes للعامة.
اخفِ الأخطاء التقنية في بيئة الإنتاج.
خزن الأسرار خارج الملفات العامة.
البنية الصديقة للأمان عادة تجعل مجلد public وحده متاحاً من خادم الويب. أما بقية المجلدات فيجب أن تبقى خارج الوصول العام المباشر.
MVC في Laravel
Laravel واحد من أشهر أطر PHP، ويعتمد كثيراً على مفاهيم MVC. في Laravel، تستقبل routes الطلبات، وتعالج controllers المنطق، وتتفاعل models مع قاعدة البيانات، وتعرض Blade views واجهة المستخدم.
تشمل أمثلة MVC في Laravel:
Routes: تُعرّف في routes/web.php أو routes/api.php.
Controllers: تُخزن في app/Http/Controllers.
Models: تُخزن في app/Models.
Views: تُخزن في resources/views.
Validation: تتم من خلال requests أو controllers أو validators.
Middleware: تُستخدم للمصادقة وCSRF والصلاحيات.
تعلم MVC في PHP الصافية يجعل Laravel أسهل في الفهم، لأن الإطار يستخدم الأفكار العامة نفسها بطريقة أكثر تقدماً وتنظيماً.
سير طلب MVC بسيط
يبدو سير طلب MVC المعتاد بهذا الشكل:
يرسل المتصفح طلباً إلى التطبيق.
يدخل الطلب عبر public/index.php.
يفحص router عنوان URL وطريقة HTTP.
يستدعي router دالة المتحكم الصحيحة.
يتحقق المتحكم من المدخلات ويستدعي النموذج.
يقرأ النموذج البيانات أو يكتبها باستخدام قاعدة البيانات.
يعيد النموذج البيانات إلى المتحكم.
يحمل المتحكم عرضاً أو يعيد JSON.
تُرسل الاستجابة إلى المتصفح أو عميل API.
هذا التدفق يحافظ على تنظيم التطبيق ويجعل فهم مكان كل جزء من الكود أسهل.
الخلاصة
معمارية MVC في PHP مفهوم مهم لبناء تطبيقات ويب منظمة وقابلة للصيانة. تفصل MVC التطبيق إلى models وviews وcontrollers، مما يجعل الكود أنظف وأسهل في الإدارة.
تتعامل models مع البيانات ومنطق العمل، وتتعامل views مع العرض، وتتولى controllers سير الطلب. يربط التوجيه عناوين URL بدوال المتحكمات، بينما يوفر front controller نقطة دخول واحدة للتطبيق.
بعد فهم معمارية MVC في PHP، تكون الخطوة التالية هي التدريب من خلال بناء تطبيق MVC CRUD صغير، ثم الانتقال إلى موضوعات أكثر تقدماً مثل الخدمات، وrepositories، وmiddleware، والمصادقة، وREST APIs، وComposer autoloading، وتطوير Laravel.

