گام 6: ساخت یک کنترلر

5.0 version
Maintained

ساخت یک کنترلر

پروژه‌ی guestbook در حال حاضر بر روی سرور عمل‌آوری در حال اجرا است، اما ما کمی تقلب کردیم. این پروژه هنوز هیچ صفحه‌ی وبی ندارد و صفحه‌ی اصلی، خطای کسل‌کننده‌ی 404 را نشان می‌دهد. بیایید درستش کنیم.

زمانی که یک درخواست HTTP می‌آید، همانند صفحه‌ی اصلی (http://localhost:8000/) ، سیمفونی تلاش می‌کند تا راهی (route) را که با مسیر درخواستی (request path) (دراینجا /) تطابق دارد، پیدا کند. راه (route)، یک اتصال بین مسیر درخواستی و PHP callable است، یک تابع که پاسخ HTTP را برای آن درخواست می‌سازد.

این توابعِ قابل فراخوانی، «کنترلرها» نامیده می‌شوند.در سیمفونی، اکثر کنترلرها به صورت کلاس‌های PHP پیاده‌سازی می‌شوند. شما می‌توانید چنین کلاسی را به صورت دستی بسازید اما چون ما علاقه داریم که به سرعت پیش برویم، بیایید ببینیم که سمفونی چگونه می‌تواند به ما کمک کند.

تنبلی با باندلِ Maker

برای تولید بدون زحمت کنترلرها، می‌توانیم از بسته‌ی symfony/maker-bundle استفاده کنیم.

1
$ symfony composer req maker --dev

از آنجایی که باندل maker فقط در محیط توسعه مفید است، برای جلوگیری از فعال‌شدن آن در محیط عمل‌آوری، اضافه‌کردن پرچم --dev را فراموش نکنید.

باندل maker در تولید کلاس‌های مختلف زیادی به شما کمک می‌کند. ما در این کتاب، تمام مدت از آن استفاده خواهیم کرد. هر "generator" در یک فرمان (command) تعریف می‌شود و تمام فرامین، بخشی از فضای نام (namespace) فرمانِ``make`` هستند.

فرمان توکار list در کنسول سیمفونی، تمام فرامین موجود در یک فضای نام را نمایش می‌دهد. از این فرمان برای کشف تمام generator‌های ارائه‌شده توسط باندل maker استفاده کنید:

1
$ symfony console list make

انتخاب یک قالب پیکربندی

قبل از ایجاد اولین کنترلر پروژه، لازم است در مورد قالب‌های پیکربندی‌ای (configuration formats) که می‌خواهیم استفاده کنیم، تصمیم بگیریم. سیمفونی به صورت آماده از YAML، XML، PHP و حاشیه‌نویسی (annotations) پشتیبانی می‌کند.

برای پیکربندی‌های مربوط به بسته‌ها، YAML بهترین گزینه است. این قالب در پوشه‌ی config/ استفاده شده است. اغلب هنگامی که یک بسته‌ی جدید نصب می‌کنید، recipe مربوط به آن بسته یک فایل جدید با پسوند .yaml به آن پوشه اضافه می‌کند.

برای پیکربندی مربوط به کدهای PHP، حاشیه‌نویسی‌ها (annotations) بهتر هستند زیرا در کنار کد تعریف می‌شوند. بگذارید با یک مثال توضیح دهم. وقتی یک درخواست می‌آید، تعدای پیکربندی لازم است تا به سیمفونی بگوید که مسیر درخواستی باید توسط یک کنترلر مشخص (یک کلاس PHP) رسیدگی شود. هنگامی که از قالب‌های YAML، XML یا PHP استفاده می‌کنید، دو فایل درگیر می‌شوند (فایل پیکربندی و فایل کنترلر PHP). زمانی که از حاشیه‌نویسی استفاده می‌کنید، پیکربندی مستقیماً درون کلاس کنترلر انجام می‌شود.

برای مدیریت حاشیه‌نویسی‌ها، نیاز داریم که یک وابستگی دیگر بیافزاییم:

1
$ symfony composer req annotations

شما ممکن است متعجب شوید که چگونه می‌توانید نام بسته‌ای که برای یک ویژگی لازم دارید را حدس بزنید؟ در اکثر اوقات لازم نیست که بدانید. در موارد زیادی، بسته‌ای که باید نصب شود، در پیغام‌های خطای سیمفونی وجود دارد. برای نمونه اجرای symfony make:controller بدون بسته‌ی annotations منجر به پیغام استثنایی شامل یک راهنمایی برای نصب بسته‌ی صحیح می‌گردد.

تولید یک کنترلر

اولین کنترلر خود را از طریق فرمان make:controller ایجاد کنید:

1
$ symfony console make:controller ConferenceController

این فرمان یک کلاس ConferenceController در داخل پوشه‌ی src/Controller/ ایجاد می‌کند. کلاس تولید‌شده شامل مقداری کد الگو است که آماده‌اند تا با اصلاحات جزئی کارکرد مورد نظر را فراهم کنند:

src/Controller/ConferenceController.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ConferenceController extends AbstractController
{
    /**
     * @Route("/conference", name="conference")
     */
    public function index()
    {
        return $this->render('conference/index.html.twig', [
            'controller_name' => 'ConferenceController',
        ]);
    }
}

حاشیه‌نویسیِ @Route("/conference", name="conference") چیزی است که باعث می‌شود متد index() یک کنترلر باشد (پیکربندی در کنار کدی است که پیکره‌بندی می‌شود).

زمانی که /conference را در مرورگر وارد کنید، کنترلر اجرا شده و پاسخ (response) بازگردانده می‌شود.

راه را تغییر دهید تا با صفحه‌ی اصلی مطابقت پیدا کند:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -8,7 +8,7 @@ use Symfony\Component\Routing\Annotation\Route;
 class ConferenceController extends AbstractController
 {
     /**
-     * @Route("/conference", name="conference")
+     * @Route("/", name="homepage")
      */
     public function index()
     {

زمانی که می‌خواهیم به صفحه‌ی اصلی ارجاع دهیم، نامِ راه (route name) می‌تواند مفید باشد. به جای هاردکد کردن مسیرِ /، قصد داریم از نامِ راه استفاده کنیم.

بیایید به جای صفحه‌ی renderشده‌ی پیشفرض، یک HTML ساده بازگردانیم:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,6 +3,7 @@
 namespace App\Controller;

 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Annotation\Route;

 class ConferenceController extends AbstractController
@@ -12,8 +13,13 @@ class ConferenceController extends AbstractController
      */
     public function index()
     {
-        return $this->render('conference/index.html.twig', [
-            'controller_name' => 'ConferenceController',
-        ]);
+        return new Response(<<<EOF
+<html>
+    <body>
+        <img src="/images/under-construction.gif" />
+    </body>
+</html>
+EOF
+        );
     }
 }

مرورگر را تازه‌سازی کنید:

وظیفه‌ی اصلی یک کنترلر، بازگرداندن یک پاسخ (HTTP Response) برای درخواست است.

اضافه کردن یک تخم‌مرغ عید پاک

برای اینکه نشان دهیم چگونه یک پاسخ می‌تواند از اطلاعات درخواست بهره بگیرد، بیایید یک Easter egg کوچک اضافه کنیم. بیایید هر زمان که صفحه‌ی اصلی شامل یک رشته‌ی پرس‌وجو (query string) به صورت ?hello=Fabien بود، برای خوش‌آمدگویی به آن فرد، یک متن اضافه کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -3,6 +3,7 @@
 namespace App\Controller;

 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Annotation\Route;

@@ -11,11 +12,17 @@ class ConferenceController extends AbstractController
     /**
      * @Route("/", name="homepage")
      */
-    public function index()
+    public function index(Request $request)
     {
+        $greet = '';
+        if ($name = $request->query->get('hello')) {
+            $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+        }
+
         return new Response(<<<EOF
 <html>
     <body>
+        $greet
         <img src="/images/under-construction.gif" />
     </body>
 </html>

سیمفونی داده‌های درخواست را به صورت شیءِ Request ارائه می‌کند. هر زمان که سیمفونی یک آرگمانِ کنترلر (controller argument) با این type-hint ببیند، به صورت خودکار متوجه می‌شود که باید شیء درخواست را به آن بدهد. ما می‌توانیم از آن استفاده کنیم تا مقدار name را از رشته‌ی پرس‌وجو بگیریم و یک عنوان <h1> اضافه کنیم.

سعی کنید ابتدا / و سپس /?hello=Fabien را در مرورگر وارد کنید تا تفاوت را ببینید.

توجه

به فراخوانی htmlspecialchars() برای جلوگیری از مشکلات XSS دقت کنید. این چیزی است که وقتی از یک موتور قالب‌گیری مناسب استفاده کنیم (در بخش‌های آتی)، به صورت خودکار برای ما انجام می‌شود.

ما همچنین می‌توانستیم نام را بخشی از URL قرار دهیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -9,13 +9,19 @@ use Symfony\Component\Routing\Annotation\Route;
 class ConferenceController extends AbstractController
 {
     /**
-     * @Route("/", name="homepage")
+     * @Route("/hello/{name}", name="homepage")
      */
-    public function index()
+    public function index(string $name = '')
     {
+        $greet = '';
+        if ($name) {
+            $greet = sprintf('<h1>Hello %s!</h1>', htmlspecialchars($name));
+        }
+
         return new Response(<<<EOF
 <html>
     <body>
+        $greet
         <img src="/images/under-construction.gif" />
     </body>
 </html>

بخش {name} از راه، یک پارامتر راه (route parameter) پویا (dynamic) است که مشابه wildcard عمل می‌کند. حالا می‌توانید ابتدا /hello و سپس /hello/Fabien را در مرورگر وارد کنید تا همان نتایج قبلی را دریافت کنید. می‌توانید مقدار پارامتر {name} را با اضافه کردن یک آرگمان با نام یکسان name بگیرید. بنابراین، $name.


  • « Previous گام 5: عیب‌یابی مشکلات
  • Next » گام 7: راه‌اندازی یک پایگاه‌داده

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.