การฉีดการพึ่งพาอัตโนมัติ
ใน webman การฉีดการพึ่งพาอัตโนมัติเป็นฟีเจอร์ที่เลือกได้ ฟีเจอร์นี้ถูกปิดตามค่าเริ่มต้น หากคุณต้องการการฉีดการพึ่งพาอัตโนมัติ แนะนำให้ใช้ php-di ดังนี้คือวิธีการใช้ webman ร่วมกับ php-di
การติดตั้ง
composer require psr/container ^1.1.1 php-di/php-di ^6.3 doctrine/annotations ^1.14
แก้ไขการตั้งค่า config/container.php
เนื้อหาสุดท้ายจะเป็นดังนี้:
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();
config/container.php
จะส่งคืนอินสแตนซ์ของคอนเทนเนอร์ที่สอดคล้องกับมาตรฐานPSR-11
หากคุณไม่ต้องการใช้php-di
คุณสามารถสร้างและส่งคืนอินสแตนซ์ของคอนเทนเนอร์อื่นที่สอดคล้องกับมาตรฐานPSR-11
ที่นี่ได้
การฉีดด้วยตัวสร้าง
สร้างไฟล์ app/service/Mailer.php
(หากไม่มีไดเร็กทอรีโปรดสร้างด้วยตนเอง) เนื้อหาจะเป็นดังนี้:
<?php
namespace app\service;
class Mailer
{
public function mail($email, $content)
{
// รหัสการส่งอีเมลถูกตัด
}
}
เนื้อหาของไฟล์ app/controller/UserController.php
จะเป็นดังนี้:
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
class UserController
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello and welcome!');
return response('ok');
}
}
ในกรณีปกติ ต้องใช้รหัสต่อไปนี้เพื่อสร้างอินสแตนซ์ของ app\controller\UserController
:
$mailer = new Mailer;
$user = new UserController($mailer);
เมื่อใช้ php-di
นักพัฒนาจะไม่ต้องสร้าง Mailer
โดย手动,webman จะช่วยสร้างให้คุณโดยอัตโนมัติ หากในกระบวนการสร้าง Mailer
มีการพึ่งพาชั้นอื่น webman จะสร้างและฉีดเข้าไปโดยอัตโนมัติ นักพัฒนาจะไม่ต้องทำการตั้งค่าใดๆ
หมายเหตุ
อินสแตนซ์ที่สร้างโดยเฟรมเวิร์กหรือphp-di
เท่านั้นที่สามารถทำการฉีดการพึ่งพาโดยอัตโนมัติได้ อินสแตนซ์ที่สร้างด้วยnew
จะไม่สามารถทำการฉีดการพึ่งพาโดยอัตโนมัติได้ หากต้องการทำการฉีด จำเป็นต้องใช้support\Container
แทนคำสั่งnew
ตัวอย่างเช่น:
use app\service\UserService;
use app\service\LogService;
use support\Container;
// อินสแตนซ์ที่สร้างด้วยคีย์เวิร์ด new ไม่สามารถทำการฉีดได้
$user_service = new UserService;
// อินสแตนซ์ที่สร้างด้วยคีย์เวิร์ด new ไม่สามารถทำการฉีดได้
$log_service = new LogService($path, $name);
// อินสแตนซ์ที่สร้างโดย Container สามารถทำการฉีดได้
$user_service = Container::get(UserService::class);
// อินสแตนซ์ที่สร้างโดย Container สามารถทำการฉีดได้
$log_service = Container::make(LogService::class, [$path, $name]);
การฉีดด้วยการใช้คำอธิบาย
นอกจากการฉีดการพึ่งพาอัตโนมัติด้วยตัวสร้างแล้ว เรายังสามารถใช้การฉีดด้วยคำอธิบายได้ ต่อไปนี้คือการเปลี่ยนแปลงจากตัวอย่างก่อนหน้านั้น โดยจะทำการเปลี่ยน app\controller\UserController
ให้เป็นดังนี้:
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
use DI\Annotation\Inject;
class UserController
{
/**
* @Inject
* @var Mailer
*/
private $mailer;
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello and welcome!');
return response('ok');
}
}
ตัวอย่างนี้ใช้การฉีดด้วยคำอธิบาย @Inject
และได้มีการประกาศประเภทของออบเจ็กต์ด้วยคำอธิบาย @var
ตัวอย่างนี้มีผลเหมือนกับการฉีดด้วยตัวสร้าง แต่โค้ดจะกระชับขึ้น
หมายเหตุ
webman ไม่รองรับการฉีดพารามิเตอร์ของคอนโทรลเลอร์ก่อนเวอร์ชัน 1.4.6 ดังนั้นรหัสต่อไปนี้จะไม่รองรับเมื่อ webman <= 1.4.6
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
class UserController
{
// ไม่รองรับการฉีดพารามิเตอร์ของคอนโทรลเลอร์ก่อนเวอร์ชัน 1.4.6
public function register(Request $request, Mailer $mailer)
{
$mailer->mail('hello@webman.com', 'Hello and welcome!');
return response('ok');
}
}
การฉีดด้วยการสร้างตัวเอง
บางครั้งพารามิเตอร์ที่ส่งเข้ามาในตัวสร้างอาจไม่ใช่อินสแตนซ์ของคลาส แต่เป็นสตริง ตัวเลข อาร์เรย์ หรือข้อมูลอื่นๆ เช่น ตัวสร้าง Mailer อาจจำเป็นต้องส่งหมายเลข IP และพอร์ตของเซิร์ฟเวอร์ SMTP:
<?php
namespace app\service;
class Mailer
{
private $smtpHost;
private $smtpPort;
public function __construct($smtp_host, $smtp_port)
{
$this->smtpHost = $smtp_host;
$this->smtpPort = $smtp_port;
}
public function mail($email, $content)
{
// รหัสการส่งอีเมลถูกตัด
}
}
กรณีนี้ไม่สามารถใช้การฉีดการสร้างตัวตามที่กล่าวถึงก่อนหน้าได้ เนื่องจาก php-di
ไม่สามารถระบุได้ว่าค่า $smtp_host
และ $smtp_port
คืออะไร ในกรณีนี้สามารถลองทำการฉีดด้วยวิธีการสร้างตัวตามที่กำหนดเองได้
เพิ่มโค้ดต่อไปนี้ลงใน config/dependence.php
(หากไฟล์ไม่มีโปรดสร้างด้วยตนเอง):
return [
// ... ละเว้นการตั้งค่าอื่น ๆ ที่นี่
app\service\Mailer::class => new app\service\Mailer('192.168.1.11', 25);
];
ดังนั้นเมื่อมีการฉีดการพึ่งพาเพื่อให้ได้อินสแตนซ์ของ app\service\Mailer
จะอัตโนมัติสร้างอินสแตนซ์ที่กำหนดไว้ในคอนฟิกนี้
เราสังเกตว่า ใน config/dependence.php
ได้มีการใช้ new
เพื่อสร้างอินสแตนซ์ของคลาส Mailer
ซึ่งไม่มีปัญหาในตัวอย่างนี้ แต่ลองจินตนาการว่าหากคลาส Mailer
มีการพึ่งพาคลาสอื่น ๆ หรือใช้การฉีดด้วยคำอธิบายในคลาส Mailer
การใช้ new
ในการสร้างจะไม่สามารถทำการฉีดการพึ่งพาได้ วิธีนี้จะถูกแก้ไขโดยการใช้การฉีดผ่านอินเตอร์เฟสที่กำหนดเอง โดยใช้วิธี Container::get(ชื่อคลาส)
หรือ Container::make(ชื่อคลาส, [พารามิเตอร์ตัวสร้าง])
เพื่อสร้างคลาส
การฉีดด้วยอินเตอร์เฟสที่กำหนดเอง
ในโครงการจริง เรามักจะต้องการเขียนโปรแกรมที่มุ่งเน้นอินเตอร์เฟสมากกว่าคลาสที่เฉพาะเจาะจง ตัวอย่างเช่น ใน app\controller\UserController
ควรมีการนำเข้า app\service\MailerInterface
แทนที่จะเป็น app\service\Mailer
กำหนดอินเตอร์เฟส MailerInterface
:
<?php
namespace app\service;
interface MailerInterface
{
public function mail($email, $content);
}
กำหนดการใช้งานครู MailerInterface
:
<?php
namespace app\service;
class Mailer implements MailerInterface
{
private $smtpHost;
private $smtpPort;
public function __construct($smtp_host, $smtp_port)
{
$this->smtpHost = $smtp_host;
$this->smtpPort = $smtp_port;
}
public function mail($email, $content)
{
// รหัสการส่งอีเมลถูกตัด
}
}
นำเข้า MailerInterface
แทนที่จะเป็นการใช้งานเฉพาะ:
<?php
namespace app\controller;
use support\Request;
use app\service\MailerInterface;
use DI\Annotation\Inject;
class UserController
{
/**
* @Inject
* @var MailerInterface
*/
private $mailer;
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello and welcome!');
return response('ok');
}
}
ใน config/dependence.php
นิยามการใช้งาน MailerInterface
ดังนี้:
use Psr\Container\ContainerInterface;
return [
app\service\MailerInterface::class => function(ContainerInterface $container) {
return $container->make(app\service\Mailer::class, ['smtp_host' => '192.168.1.11', 'smtp_port' => 25]);
}
];
ดังนั้นเมื่อธุรกิจต้องการใช้ MailerInterface
จะสามารถใช้การใช้งาน Mailer
ได้โดยอัตโนมัติ
ข้อดีของการเขียนโปรแกรมที่มุ่งเน้นอินเตอร์เฟสคือเมื่อเราต้องการเปลี่ยนส่วนประกอบบางอย่าง เราไม่ต้องเปลี่ยนรหัสธุรกิจ เพียงแค่เปลี่ยนการใช้งานใน
config/dependence.php
เท่านั้น ซึ่งเป็นประโยชน์มากในการทำการทดสอบแบบยูนิต
การฉีดค่าที่กำหนดเองอื่นๆ
config/dependence.php
นอกจากที่จะสามารถกำหนดการพึ่งพาของคลาส ยังสามารถกำหนดค่าอื่นๆ เช่น สตริง ตัวเลข อาร์เรย์ เป็นต้น
ตัวอย่างเช่น ใน config/dependence.php
นิยามดังนี้:
return [
'smtp_host' => '192.168.1.11',
'smtp_port' => 25
];
ในเวลานี้เราสามารถใช้ @Inject
เพื่อฉีด smtp_host
และ smtp_port
ไปยังคุณสมบัติของคลาสได้:
<?php
namespace app\service;
use DI\Annotation\Inject;
class Mailer
{
/**
* @Inject("smtp_host")
*/
private $smtpHost;
/**
* @Inject("smtp_port")
*/
private $smtpPort;
public function mail($email, $content)
{
// รหัสการส่งอีเมลถูกตัด
echo "{$this->smtpHost}:{$this->smtpPort}\n"; // จะส่งออก 192.168.1.11:25
}
}
หมายเหตุ:ใน
@Inject("key")
จะต้องใช้เครื่องหมายคำพูดคู่
เนื้อหาเพิ่มเติม
โปรดอ้างอิงที่ php-di 手册