VuePressVuePress
首页
  • 基础
  • UI
  • JavaScript
  • CSS
  • postcss
  • Vue3
  • Vue的设计与实现
  • 前端常用插件
  • PHP
  • Laravel
  • Linux
  • 线性代数
Category
AI
jiyun
Timeline
首页
  • 基础
  • UI
  • JavaScript
  • CSS
  • postcss
  • Vue3
  • Vue的设计与实现
  • 前端常用插件
  • PHP
  • Laravel
  • Linux
  • 线性代数
Category
AI
jiyun
Timeline
  • 基础知识

    • 表单验证
    • 限流1
  • 请求

    • request
  • 认证

    • auth
  • 验证

    • fail
  • migrations

    • migration
  • eloquent

    • eloquent
    • updateOrCreate
  • seeder

    • seeder
    • seeder2
  • 套件

    • precognition
  • 进阶

    • Cache 缓存
  • 最佳实践

    • CRUD 最佳实践
    • 将用户定位到他的前缀域名
    • 服务类仓储类查询类
    • 接口和实现
      • 使用 Laravel 的自动注册功能
      • 接口(Interfaces)
      • 实现(Implementations)
        • 例子
      • 服务提供者(Service Providers)
        • 创建服务提供者
      • 依赖注入和服务容器
      • 使用接口和实现的好处
      • 总结:
      • 对于小型项目,是否有必要使用接口?
    • laravel 中 trait 引导机制
    • 使用阿里云oss上传图片
  • plugins

    • IDE Helper Generator for Laravel

在 AppServiceProvider 中注册接口和实现的绑定是 Laravel 的一种常见做法,但你提出了一个很好的问题。让我解释一下原因,并提供一些替代方案:

  1. 为什么在 AppServiceProvider 中注册:
    • AppServiceProvider 是 Laravel 应用的主要服务提供者,它在应用启动时就会被加载。
    • 它提供了一个集中的地方来注册所有的服务绑定,使得管理这些绑定变得更加容易。
    • 对于小型到中型的应用来说,这通常是一个足够好的解决方案。
  2. 替代方案:
    • 创建专门的 RepositoryServiceProvider
    • 如果你的应用有很多仓库,或者你想要更好地组织你的服务提供者,你可以创建一个专门的 RepositoryServiceProvider:php artisan make:provider RepositoryServiceProvider
    • 然后在 config/app.php 中注册这个新的服务提供者:
      'providers' => [
          // 其他服务提供者...
          App\Providers\RepositoryServiceProvider::class,
      ],
      

使用 Laravel 的自动注册功能

如果你的仓库实现总是一对一地对应接口,你可以利用 Laravel 的自动注册功能。在 AppServiceProvider 的 register 方法中添加:

public function register()
{
    $this->app->register(RepositoryServiceProvider::class);
}

接口(Interfaces)

接口在 Laravel 和 PHP 中定义了一个类应该实现的方法的契约。它们不包含任何实现,只定义了方法的签名。

优点:

  • 提供了一个清晰的契约,定义了类应该做什么,而不是怎么做。
  • 允许多个类实现相同的接口,提高了代码的灵活性。
  • 便于进行单元测试,因为可以轻松地模拟接口。

例子:

namespace App\Contracts\Repositories;

interface UserRepositoryInterface
{
    public function all();
    public function find($id);
    public function create(array $data);
}

实现(Implementations)

实现是接口的具体实现。它们提供了接口中定义的方法的实际代码。

例子

namespace App\Repositories;

use App\Contracts\Repositories\UserRepositoryInterface;

class UserRepository implements UserRepositoryInterface
{
    public function all()
    {
        // 实现获取所有用户的逻辑
    }

    public function find($id)
    {
        // 实现查找特定用户的逻辑
    }

    public function create(array $data)
    {
        // 实现创建用户的逻辑
    }
}

服务提供者(Service Providers)

服务提供者是 Laravel 应用程序的中心,负责绑定服务到容器、注册事件、中间件等。它们是应用程序的引导机制。

主要功能:

  • 注册:绑定服务到服务容器
  • 引导:在应用程序启动时执行一些操作
  • Laravel 中有两种主要类型的服务提供者:
  • 延迟提供者:只在服务被请求时才会被加载
  • 即时提供者:在每次请求时都会被加载

创建服务提供者

php artisan make:provider RepositoryServiceProvider

服务提供者结构:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        // 在这里进行绑定
        $this->app->bind(
            \App\Contracts\Repositories\UserRepositoryInterface::class,
            \App\Repositories\UserRepository::class
        );
    }

    public function boot()
    {
        // 在这里进行引导操作
    }
}

注册服务提供者:

在 config/app.php 文件中添加:

'providers' => [
    // 其他提供者...
    App\Providers\RepositoryServiceProvider::class,
],

依赖注入和服务容器

Laravel 的服务容器负责管理类的依赖和执行依赖注入。当你在类的构造函数中类型提示一个接口时,Laravel 会自动解析并注入该接口的实现。

例子:

class UserController extends Controller
{
    protected $users;

    public function __construct(UserRepositoryInterface $users)
    {
        $this->users = $users;
    }

    // ...
}

在这个例子中,Laravel 会自动注入 UserRepository 的实例,因为我们在服务提供者中绑定了 UserRepositoryInterface 到 UserRepository。

使用接口和实现的好处

  • 解耦:代码依赖于抽象(接口)而不是具体实现,使得更换实现变得容易。
  • 可测试性:可以轻松地模拟接口进行单元测试。
  • 可维护性:接口提供了清晰的契约,使得代码更容易理解和维护。
  • 灵活性:可以轻松地切换不同的实现,而不需要修改使用该接口的代码。

总结:

Laravel 的接口、实现和服务提供者共同工作,提供了一个强大的依赖注入系统。这个系统允许你编写松耦合、可测试和可维护的代码。通过使用这些特性,你可以构建出更加灵活和可扩展的应用程序。

对于小型项目,是否有必要使用接口?

对于小型项目,是否使用接口确实是一个需要权衡的问题。让我们来分析一下使用和不使用接口的优缺点,以帮助你做出决定:

不使用接口的优点:

  1. 简单直接:代码结构更简单,更容易理解和维护。
  2. 开发速度快:不需要为每个类创建对应的接口,可以更快地开发功能。
  3. 文件更少:不需要额外的接口文件,项目结构更加精简。

使用接口的优点:

  1. 更好的抽象:接口提供了清晰的契约,定义了类应该实现的方法。
  2. 提高可测试性:使用接口可以更容易地进行单元测试和模拟。
  3. 未来的灵活性:如果项目将来需要扩展,接口可以提供更好的扩展性。
  4. 强制规范:接口可以强制开发者遵守某些规范,有助于保持代码质量。

考虑到你的项目是小型项目,我的建议是:

  1. 如果你是唯一的开发者,并且项目预期不会有大的扩展:
    1. 可以不使用接口,直接实现具体的类。
    2. 专注于编写清晰、简洁的代码和良好的文档。
  2. 如果项目可能会扩大,或者有多人协作:
    1. 考虑在关键的、可能需要多种实现的部分使用接口。
    2. 例如,可以为数据访问层(如仓库类)创建接口,因为这些部分最有可能在未来需要不同的实现(如切换数据库、添加缓存等)。
  3. 折中方案:
    1. 开始时不使用接口,但保持良好的代码组织和解耦。
    2. 如果发现某些部分确实需要更好的抽象或可能有多种实现,再为这些部分添加接口。

示例:不使用接口的简化版本

// [WarehouseRepository.php]
<?php

namespace App\Repositories;

use App\Models\Company;
use Illuminate\Pagination\LengthAwarePaginator;

class WarehouseRepository
{
    public function getWarehousesForCompany(Company $company, array $criteria = []): LengthAwarePaginator
    {
        $query = $company->warehouses()->orderBy('sort', 'asc')->orderBy('created_at', 'desc');

        if (isset($criteria['search'])) {
            $query->where('name', 'like', '%' . $criteria['search'] . '%');
        }

        return $query->paginate($criteria['per_page'] ?? 15);
    }
}
// [WarehouseService.php]
<?php

namespace App\Services;

use App\Models\Company;
use App\Repositories\WarehouseRepository;
use Illuminate\Pagination\LengthAwarePaginator;

class WarehouseService
{
    protected $warehouseRepository;

    public function __construct(WarehouseRepository $warehouseRepository)
    {
        $this->warehouseRepository = $warehouseRepository;
    }

    public function getWarehousesForCompany(Company $company, array $criteria = []): LengthAwarePaginator
    {
        return $this->warehouseRepository->getWarehousesForCompany($company, $criteria);
    }
}

这种方法简化了代码结构,但仍然保持了服务和仓库的分离,这有助于代码的组织和可能的未来扩展。记住,好的代码组织和清晰的职责分离比使用接口更重要。如果你的代码结构良好,将来需要添加接口也会相对容易。

总之,对于小型项目,可以先不使用接口,专注于编写清晰、可维护的代码。如果项目开始变大或者你发现某些部分确实需要更好的抽象,再考虑引入接口。

Last Updated:
Contributors: BaronYan
Prev
服务类仓储类查询类
Next
laravel 中 trait 引导机制