我希望上一篇文章有意义。依赖注入确实是一个简单的概念。我觉得在你说你知道“Laravel 中的依赖注入”之前,你应该完整地理解它。
在我们再次进入依赖注入之前,你应该了解 Laravel 的服务容器。Laravel 中的服务容器是什么?它只是一个管理依赖项的工具,并且正是这个工具实际执行了依赖项注入。
在上一篇文章中,我们查看了一个极其简单的依赖注入示例。
<?php
class Driver {
private Car $car;
public function __construct(Car $car)
{
$this->car = $car;
}
}
有趣的是,如果你省略了实现细节,我们可以键入提示我们的类并将其注入到我们的路由或控制器方法中。
class Car
{
//
}
Route::get('/', function (Car $car) {
die(get_class($car));
});
路由会自动解析类,这非常神奇。您不必指定如何执行此操作。它就是这样做的。最频繁注入的类是Request
. 当我们提交一个表单时,Laravel 会做它的事情并加载我们的Request $request
对象和我们所有的表单数据,这样我们就可以访问一个输入字段(比如email
)$request->email
。
您只需输入提示Request $request
即可开始。
<form action="/personalcars" method="post" enctype="multipart/form-data">
@csrf
<div class="flex ...">
<label class="mb-2 ..." for="year">Year</label>
<input
class="border ...0"
type="number"
name="year"
id="year"
min="1920"
max="{{ date('Y') + 1 }}"
placeholder="2003"
>
</div>
</div>
use Illuminate\Http\Request;
Route::post('/personalcars/', function (Request $request) {
return $request->year;
});
无需担心如何引入Request
或如何解决它,您只需键入 hint 它,Laravel 会自动为您解决依赖关系。你可以在控制器中做同样的事情。我们实际上已经看到了它的实际应用。
<?php
namespace App\Http\Controllers;
use App\Models\Image;
use App\Models\PersonalCar;
use App\Models\PersonalCarBrand;
use App\Models\PersonalCarModel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class PersonalCarController extends Controller
{
// ...
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*/
public function update(Request $request, $id)
{
$validated = $request->validate([
'year' => 'required|integer',
'make' => 'required|max:255',
'model' => 'required|max:255',
'is_manual' => 'required|boolean',
'exterior_color' => 'required|max:255',
'purchase_amount' => 'numeric',
'current_value' => 'numeric',
'sales_amount' => 'numeric|nullable',
'date_purchased' => 'required|date',
'date_sold' => 'date|nullable',
'file' => 'image|mimes:jpg,jpeg,png,gif|max:2048|nullable',
]);
$car = PersonalCar::find($id);
$brand = PersonalCarBrand::firstOrCreate([
'name' => $request->make,
], [
'slug' => str_replace(" ", "-", strtolower($request->make))
]);
$model = PersonalCarModel::firstOrCreate([
'name' => $request->model,
], [
'slug' => str_replace(" ", "-", strtolower($request->model))
]);
$car->year = $request->year;
$car->brand()->associate($brand);
$car->model()->associate($model);
$car->is_manual = $request->is_manual;
$car->exterior_color = $request->exterior_color;
$car->purchase_amount = $request->purchase_amount;
$car->current_value = $request->current_value;
$car->sales_amount = $request->sales_amount == 0 ? 0 : $request->sales_amount;
$car->date_purchased = $request->date_purchased;
$car->date_sold = $request->date_sold;
$car->save();
if ( $request->file('file') !== null ) {
$image_path = $request->file('file')->store('images', 'public');
$image = Image::create([
'url' => $image_path,
'alt' => $request->year . " " . $brand->name . " " . $model->name,
]);
$car->images()->attach($image);
}
return redirect()->to('/personalcars/' . $id . '/edit')->with('status', 'Your car has been updated.');
}
}
在我们的update
方法中,我们简单地进行了类型提示Request $request
,Laravel 自动解决了我们的依赖关系。我们也通过了$id
我们的路线。我们不需要通过$request
。我们只是对其进行了类型提示,然后 Laravel 解决了它。
Route::prefix('/personalcars')->group(function() {
// ...
Route::put('/{id}', [PersonalCarController::class, 'update']);
});
为什么我们要这样做呢?为什么 Laravel 要我们使用依赖注入而不是仅仅实例化Request
对象?
一个类可能依赖于另一个类,而那个类又依赖于它自己的类,等等。在您有机会使用它们之前,您必须实例化每个类并确保一切正常。通过依赖注入,Laravel 的服务容器在幕后负责实例化过程。
在 Laravel 中使用依赖注入的例子
既然这个概念有点浮现在您的脑海中,让我们看看是否可以编写一个更具体的示例。
我将从创建路由和控制器开始。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DependencyInjectionTestController extends Controller
{
public function index(Request $request)
{
//
}
}
use App\Http\Controllers\DependencyInjectionTestController;
Route::get('/dependency-injection', [DependencyInjectionTestController::class, 'index']);
在我们的index
方法中,我们现在可以用来$request->input
显示我们传递给路由的名称。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DependencyInjectionTestController extends Controller
{
public function index(Request $request)
{
return $request->input('name');
}
}
要测试我们的路线,我们只需要访问如下路线:
http://0.0.0.0/dependency-injection?name=Dino
输出将是Dino
。
接下来,让我们创建一个服务并注入它。我将创建一个名为 的目录Services
并添加名为 的服务CapitalizeStringService
。
正如您可能已经猜到的那样,这个服务类只会将一个字符串大写。
<?php
namespace App\Services;
class CapitalizeStringService
{
public function capitalize($string)
{
return strtoupper($string);
}
}
现在,让我们将此服务注入index
到DependencyInjectionTestController
.
<?php
namespace App\Http\Controllers;
use App\Services\CapitalizeStringService;
use Illuminate\Http\Request;
class DependencyInjectionTestController extends Controller
{
public function index(Request $request, CapitalizeStringService $capitalizeStringService)
{
$upper = $capitalizeStringService->capitalize( $request->input('name') );
return $upper;
}
}
我们在这里所做的是首先name
从 URL 中检索参数,然后将其传递给我们的CapitalizeStringService::capitalize
方法。该capitalize
方法将字符串转换为大写并将其返回。然后我们返回生成的字符串。如果我们刷新页面,我们会得到:DINO
。很酷。我们不必实例化我们的CapitalizeStringService
类。我们只是对其进行了类型提示,而 Laravel 的服务容器负责处理。
如果我们不想类型提示并且我们不想自己直接实例化怎么办?我们已经谈到了服务容器,但我们还没有真正看过这个例子。我们只需要使用app()
全局辅助函数。
<?php
namespace App\Http\Controllers;
use App\Services\CapitalizeStringService;
use Illuminate\Http\Request;
class DependencyInjectionTestController extends Controller
{
public function index(Request $request, CapitalizeStringService $capitalizeStringService)
{
$upper = $capitalizeStringService->capitalize( $request->input('name') );
return $upper;
}
public function test()
{
$request = app()->make(Request::class);
$capitalizeStringService = app()->make(CapitalizeStringService::class);
$upper = $capitalizeStringService->capitalize( $request->input('name') );
return $upper;
}
}
要获得此方法,我们将使用以下路线:
Route::get('/dependency-injection/test', [DependencyInjectionTestController::class, 'test']);
我们将在地址栏中输入以下 URL。
http://0.0.0.0/dependency-injection/test?name=Dino
它仍然有效。我们仍然得到DINO
。那是服务容器在幕后做它的事情。但是,您无需执行该过程,只需对类进行类型提示,Laravel 就会为您处理。在大多数情况下,由于自动依赖注入,您永远不会以这种方式与服务容器交互。
你想什么时候?如果你创建了一个实现接口的类,或者你正在编写 Laravel 包。就我们的目的而言,只要我们了解 Laravel 中的依赖注入是如何工作的,这对我们来说就足够了。