Laravel là một trong những khung ứng dụng web open-source hiện đại được sử dụng nhiều nhất, được sử dụng nhiều nhất. Nó cung cấp các tính năng độc đáo như Eloquent ORM, Query builder, Homestead là những tính năng hiện đại, chỉ có trong Laravel.
Tôi thích Laravel vì thiết kế kiến trúc độc đáo của nó. Đằng sau Laravel sử dụng các design pattern như Singleton, Factory, Builder, Facade, Strategy, Provider, Proxy, v.v. Vì vậy, khi kiến thức của tôi ngày càng tăng, tôi sẽ tìm ra vẻ đẹp của nó. Laravel giúp cuộc sống của nhà phát triển trở nên dễ dàng hơn và loại bỏ sự nhàm chán.
Học code với Laravel không chỉ là về học tập để sử dụng các lớp khác nhau mà còn học hỏi triết lý của Laravel, sang trọng của nó và cú pháp đẹp của nó. Một phần quan trọng trong triết lý của Laravel là IoC container hoặc Service Container. Hiểu và sử dụng IoC container là một phần quan trọng trong việc thành thạo công việc của chúng ta, vì nó là phần cốt lõi của một ứng dụng Laravel.
IoC container là một công cụ mạnh mẽ để quản lý các lớp phụ thuộc. Nó có khả năng tự động phân giải các lớp mà không cần cấu hình. Ở đây tôi sẽ cố gắng thảo luận tại sao chúng ta cần nó và nó hoạt động như thế nào.
Nếu lúc đầu chúng ta biết Nguyên tắc Dependency Inversion Principle, nó sẽ giúp chúng ta hiểu tại sao chúng ta cần IoC Container. Vì vậy, ở phần đầu tôi sẽ thảo luận về Nguyên tắc Dependency Inversion Principle.
nguyên tắc Dependency Inversion Principl:
- High-level modules should not depend on low-level modules. Both should depend on abstractions. (Các mô-đun cấp cao không nên phụ thuộc vào các mô-đun cấp thấp. Cả hai đều nên phụ thuộc vào sự trừu tượng.)
- Abstractions should not depend on details. Details should depend on abstractions. (Sự trừu tượng không nên phụ thuộc vào chi tiết. Các chi tiết nên phụ thuộc vào sự trừu tượng.)
Hay đơn giản hơn: Phụ thuộc vào những phần tóm tắt chứ không dựa vào những cái cụ thể
class MySQLConnection
{ /** * db connection */ public function connect() { var_dump(‘MYSQL Connection’); }
}
class PasswordReminder
{ /** * @var MySQLConnection */ private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; }
}
Có một sự hiểu lầm phổ biến rằng đảo ngược phụ thuộc chỉ đơn giản là một cách khác để nói dependency injection. Tuy nhiên, cả hai không giống nhau, trong đoạn mã trên Mặc dù dependency injection MySQLConnection trong lớp PasswordReminder nhưng nó phụ thuộc vào MySQLConnection.
Mô-đun cấp cao PasswordReminder không được phụ thuộc vào MySQLConnection của mô-đun cấp thấp.
Nếu chúng ta muốn thay đổi kết nối từ MySQLConnection sang MongoDBConnection, chúng ta phải thay đổi hàm tạo mã cứng trong lớp PasswordReminder.
Lớp PasswordReminder nên phụ thuộc vào phần Tóm tắt chứ không phụ thuộc vào phần cụ thể. Nhưng làm thế nào chúng ta có thể làm điều đó?
Vui lòng xem ví dụ sau:
interface ConnectionInterface
{ public function connect();
}
class DbConnection implements ConnectionInterface
{ /** * db connection */ public function connect() { var_dump(‘MYSQL Connection’); }
}
class PasswordReminder
{ /** * @var DBConnection */ private $dbConnection; public function __construct(ConnectionInterface $dbConnection) { $this->dbConnection = $dbConnection; }
}
Trong đoạn mã trên, chúng ta muốn thay đổi kết nối từ MySQLConnection thành MongoDBConnection, chúng ta không cần phải thay đổi hàm tạo chèn trong lớp PasswordReminder, vì ở đây lớp PasswordReminder phụ thuộc vào Tóm tắt chứ không phụ thuộc vào cụ thể.
Nếu khái niệm của bạn không rõ ràng về giao diện thì bạn có thể đọc tài liệu này . Tài liệu này sẽ giúp bạn hiểu rõ ràng Nguyên tắc đảo ngược phụ thuộc, vùng chứa IoC, v.v.
Bây giờ tôi sẽ thảo luận về những gì xảy ra trong IoC container. chúng ta có thể nói một cách đơn giản rằng IoC container là một Container chứa Inversion of Control (các phụ thuộc của một lớp).
OrderRepositoryInterface :
namespace App\Repositories;
interface OrderRepositoryInterface { public function getAll();
}
DbOrderRepository class:
namespace App\Repositories;
class DbOrderRepository implements OrderRepositoryInterface
{ function getAll() { return 'Getting all from mysql'; }
}
OrdersController class:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Repositories\OrderRepositoryInterface;
class OrdersController extends Controller
{ protected $order; function __construct(OrderRepositoryInterface $order) { $this->order = $order; } public function index() { dd($this->order->getAll()); return View::make(orders.index); }
}
Routing:
Route::resource('orders', 'OrdersController');
Bây giờ nếu bạn nhấn vào trình duyệt bằng cách sử dụng url này http://localhost:8000/orders
Bạn sẽ gặp lỗi này. Bởi vì vùng chứa đang cố gắng khởi tạo giao diện. Chúng tôi có thể khắc phục điều đó bằng cách tạo một ràng buộc cụ thể cho giao diện của chúng tôi.
Chỉ cần thêm mã dòng này vào route page là chúng ta có thể giải quyết được.
App::bind('App\Repositories\OrderRepositoryInterface', 'App\Repositories\DbOrderRepository');
Bây giờ nếu bạn nhấn vào trình duyệt, bạn sẽ nhận được:
Ở đây, Cần phải đề cập rằng, Chúng tôi không nên giải quyết ràng buộc ứng dụng trong trang tuyến đường. Ở đây tôi chỉ thêm vào cho mục đích ví dụ. Trong dự án chuyên nghiệp của chúng tôi, chúng tôi nên giải quyết vấn đề ràng buộc ứng dụng trong phương thức đăng ký ở AppServiceProvider như được mô tả bên dưới:
$this->app->bind('App\Repositories\OrderRepositoryInterface', 'App\Repositories\DbOrderRepository');
chúng ta có thể định nghĩa một lớp vùng chứa theo cách sau:
class SimpleContainer { protected static $container = []; public static function bind($name, Callable $resolver) { static::$container[$name] = $resolver; } public static function make($name) { if(isset(static::$container[$name])){ $resolver = static::$container[$name] ; return $resolver(); } throw new Exception("Binding does not exist in containeer"); }
}
Ở đây tôi sẽ cố gắng chỉ ra rằng vùng chứa đơn giản giải quyết sự phụ thuộc như thế nào
class LogToDatabase { public function execute($message) { var_dump('log the message to a database :'.$message); }
}
class UsersController { protected $logger; public function __construct(LogToDatabase $logger) { $this->logger = $logger; } public function show() { $user = 'JohnDoe'; $this->logger->execute($user); }
}
Ở đây ràng buộc phụ thuộc.
SimpleContainer::bind('Foo', function() { return new UsersController(new LogToDatabase); });
$foo = SimpleContainer::make('Foo');
print_r($foo->show());
Đầu ra:
string(36) "Log the messages to a file : JohnDoe"
Laravel’s container code :
public function bind($abstract, $concrete = null, $shared = false) { $abstract = $this->normalize($abstract); $concrete = $this->normalize($concrete); if (is_array($abstract)) { list($abstract, $alias) = $this->extractAlias($abstract); $this->alias($abstract, $alias); } $this->dropStaleInstances($abstract); if (is_null($concrete)) { $concrete = $abstract; } if (! $concrete instanceof Closure) { $concrete = $this->getClosure($abstract, $concrete); } $this->bindings[$abstract] = compact('concrete', 'shared'); if ($this->resolved($abstract)) { $this->rebound($abstract); } }
public function make($abstract, array $parameters = []) { $abstract = $this->getAlias($this->normalize($abstract)); if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete, $parameters); } else { $object = $this->make($concrete, $parameters); } foreach ($this->getExtenders($abstract) as $extender) { $object = $extender($object, $this); } if ($this->isShared($abstract)) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); $this->resolved[$abstract] = true; return $object; }
public function build($concrete, array $parameters = []) { if ($concrete instanceof Closure) { return $concrete($this, $parameters); } $reflector = new ReflectionClass($concrete); if (! $reflector->isInstantiable()) { if (! empty($this->buildStack)) { $previous = implode(', ', $this->buildStack); $message = "Target [$concrete] is not instantiable while building [$previous]."; } else { $message = "Target [$concrete] is not instantiable."; } throw new BindingResolutionException($message); } $this->buildStack[] = $concrete; $constructor = $reflector->getConstructor(); if (is_null($constructor)) { array_pop($this->buildStack); return new $concrete; } $dependencies = $constructor->getParameters(); $parameters = $this->keyParametersByArgument( $dependencies, $parameters ); $instances = $this->getDependencies($dependencies,$parameters); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }
Nếu bạn muốn biết thêm chi tiết tất cả các phương pháp về vùng chứa thì bạn có thể xem
vendor/laravel/framwork/src/Illuminate/Container/Container.php
Simple Bindings
$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient'));
});
Binding A Singleton
Các singleton phương pháp liên kết với một lớp hoặc giao diện vào container mà chỉ nên được giải quyết một lúc.
$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient'));
});
Binding Instances
Bạn cũng có thể liên kết một cá thể đối tượng hiện có vào vùng chứa bằng instance phương pháp này. Phiên bản đã cho sẽ luôn được trả về trong các lần gọi tiếp theo vào vùng chứa:
$api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\API', $api);
Nếu không có ràng buộc, lớp Reflection của PHP được sử dụng để giải quyết cá thể và phụ thuộc.
Cảm ơn bạn đã đọc.
link tham khảo: https://medium.com/@NahidulHasan/laravel-ioc-container-why-we-need-it-and-how-it-works-a603d4cef10f