zenithsu/laravel-plus

Laravel-Plus 是一个高效的 Laravel 扩展包,旨在提高开发效率。它集成了强大的注解处理和实用的辅助类,使 Laravel 应用的开发更加方便和灵活。

v1.12.4 2024-08-27 10:39 UTC

This package is auto-updated.

Last update: 2024-09-13 08:47:26 UTC


README

Laravel-Plus-Logo

PHP Version Laravel Version Setup Automated Download statistics License

Laravel Plus

Laravel 是一个优雅的框架,极大地简化了开发过程。然而,没有任何框架能够真正“即插即用”地适用于所有用例;根据个人习惯和项目需求进行定制通常是必要的。

Laravel Plus 通过结合 Java Spring Boot 的 AOP 概念和广泛使用 PHP 8 属性来解决这个问题,从而简化开发过程。

该项目目前正在开发中。请在生产环境中谨慎使用。

安装

该包可以通过 Composer 安装,地址为 https://packagist.org.cn/packages/zenithsu/laravel-plus

composer require zenithsu/laravel-plus

简易路由

在 Laravel 中,需要在 web.phpapi.php 中分别配置路由,这在开发过程中不太方便,因为它需要在不同文件之间切换。

相比之下,Spring Boot 或 Flask 等框架允许使用注解来配置路由,这使得编码过程更加流畅。因此,我封装了基于注解的路由。

首先,您需要在 api.php 中注册,代码如下

use Zenith\LaravelPlus\EasyRouter;

EasyRouter::register();

然后,您可以在控制器方法之前使用路由注解

class UserController
{
    #[GetMapping(path: '/login')] 
    public function login() {}
}

您可以通过 /api/login 访问此 API。除了 GetMapping,还支持 PostMappingPutMappingDeleteMapping

此外,您还可以向控制器添加 Prefix 注解,为控制器内的所有方法统一添加路由前缀。

use Zenith\LaravelPlus\Attributes\Routes\GetMapping;
use Zenith\LaravelPlus\Attributes\Routes\PostMapping;
use Zenith\LaravelPlus\Attributes\Routes\Prefix;

#[Prefix(path: '/user')]
class UserController
{
    #[GetMapping(path: '/login')]
    public function login() {}
    
    #[PostMapping(path: '/register')]
    public function register() {}
}

请求

在 Laravel-Plus 中,受 SpringBoot 的 RequestBody 注解的启发,您可以使用一个类来携带来自主体的参数

use Zenith\LaravelPlus\Middlewares\RequestBodyInjector;

abstract class Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
           RequestBodyInjector::class, 
        ];
    }
}

然后,您可以使用 RequestBody 注解来注入来自主体的参数

use Zenith\LaravelPlus\Attributes\Routes\GetMapping;
use Zenith\LaravelPlus\Attributes\Requests\RequestBody;
use Zenith\LaravelPlus\Bean;

class UserController extends Controller
{
    #[GetMapping(path: '/login')]
    public function login(#[RequestBody] RegisterParams $params) {}
}

// The RegisterParams class must extend the Bean class.
class RegisterParams extends Bean
{
    // The modifiers must be public or protected.
    protected string $username;
    
    protected string $password;
}

验证器

在 Laravel 中,参数验证不是一个难题。然而,通过使用注解,可以使它变得更加简单。

首先,您需要启用参数验证中间件

use Zenith\LaravelPlus\Middlewares\RequestParamsDefaultValueInjector;

abstract class Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            RequestParamsDefaultValueInjector::class
            ParameterValidation::class,
        ];
    }
}

然后,您可以使用 Param 注解来验证参数

use Zenith\LaravelPlus\Attributes\Validators\Param;

class UserController extends Controller
{
    #[GetMapping(path: '/login')]
    #[Param(key: 'username', rules: 'required|string|max:32', message: 'Username is required.')]
    public function login() {}
}

除正则表达式外,rule 支持 Laravel 的内置规则。

对于特别复杂的规则,建议使用自定义验证器

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class PasswordRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $isPass = strlen($value) >= 8 && preg_match('/[a-zA-Z]/', $value) &&
            preg_match('/\d/', $value) &&
            preg_match('/[^a-zA-Z\d]/', $value);
        if (! $isPass) {
            $fail('The :attribute must be least 8 characters and contain at least one letter, one number and one special character.');
        }
    }
}

在上面的示例中,我为常见的密码验证编写了一个自定义规则

use Zenith\LaravelPlus\Attributes\Validators\Param;

class UserController
{
    #[GetMapping(path: '/login')]
    #[Param(key: 'username', rules: PasswordRule::class, message: 'password is error')]
    public function login() {}
}

默认情况下,所有参数都是必需的。您可以使用 required 参数将它们设置为可选的,并使用 default 参数设置默认值

use Zenith\LaravelPlus\Attributes\Validators\Param;
use Zenith\LaravelPlus\Attributes\Requests\RequestBody;

class UserController extends Controller
{

    #[Param(key: 'page', rule: 'integer|min:1|max:100', default: 1, required: false, message: 'page is error')]
    public function queryList(#[RequestBody] QueryParams $params) 
    {
        dump($params->getPage());       // output: 1
    }
}

Bean

长期依赖,PHP 程序员习惯于使用强大的数组作为所有数据的载体。这不是一种优雅的做法,并且有以下问题

  • 数组键容易拼写错误,当这些错误被发现时,已经是在运行时了。
  • 编码过程不流畅;您总是需要暂停以思考下一个键是什么。
  • 它违反了单一职责原则,通常将所有数据放在一个巨大的数组中。
  • 它降低了代码的可扩展性、可读性和健壮性...

因此,我引入了 Bean 的概念。Bean 是一个具有强类型属性的载体,使您在编码过程中获得更好的提示

use Zenith\LaravelPlus\Bean;

/**
 * @method getUsername()
 * @method setUsername()
 * @method getPassword()
 * @method setPassword()
 */
class RegisterParams extends Bean
{
    protected string $username;
    
    protected string $password;
}

new RegisterParams(['username' => 'bob', 'password' => 'passw0rd']);

您可以使用数组初始化Bean,这是最常用的方法。当然,有时您也可以将一个Bean转换为另一个Bean,并且它会过滤掉不匹配的字段

use Zenith\LaravelPlus\Bean;

$bean = new Bean();
class Bar extends Bean {  
    // some properties  
}
Bar::fromBean($bean)

您可以将Bean轻松地转换为数组或JSON,默认情况下将使用snake case命名。您可以通过使用usingSnakeCase参数来关闭此功能

use Zenith\LaravelPlus\Bean;

$bean = new Bean();
$arr = $bean->toArray(usingSnakeCase: false)
$json = $bean->toJson(usingSnakeCase: true);

有时,您可能需要比较两个Bean

use Zenith\LaravelPlus\Bean;
(new Bean())->equals($bean2);

通常,我们需要对客户端传递的数据进行初步工作,例如类型转换

use Zenith\LaravelPlus\Bean::
use Zenith\LaravelPlus\Attributes\TypeConverter;

class User extends Bean
{
    
    #[TypeConverter(value: BoolConverter::class)]
    protected BoolEnum $isGuest;
}

class BoolConverter
{
    public function convert(bool|string $value): BoolEnum
    {
        if ($value === 'YES' || $value === 'yes' || $value === 'y' || $value === 'Y') {
            return BoolEnum::TRUE;
        }
        if ($value === 'NO' || $value === 'no' || $value === 'N' || $value === 'n') {
            return BoolEnum::FALSE;
        }

        return $value ? BoolEnum::TRUE : BoolEnum::FALSE;
    }
}

您甚至可以进行XSS过滤。

Beans的一个特别有用的功能是它们支持嵌套

use Zenith\LaravelPlus\Bean;

class User extends Bean
{
    protected Company $company;
}

class Company extends Bean
{
    protected string $name;
}

它甚至支持数组嵌套

use Zenith\LaravelPlus\Bean;
use Zenith\LaravelPlus\Attributes\BeanList;

/**
 * @method Company[] getCompanies()
 */
class User extends Bean
{
    /**
     * @var Company[]
     */
    #[BeanList(value: Company::class)]
    protected array $companies;
}

$user = new User(['companies' => [['name' => 'Zenith'], ['name' => 'Google']]]);
foreach ($user->getCompanies() as $company) {
    dump($company->getName());
}

自动装配

在Java Spring Boot框架中,使用@Autowired注解来自动注入依赖项。在Laravel-Plus中,我们可以使用#[Autowired]注解来实现相同的效果。

use Zenith\LaravelPlus\Traits\Injectable;

class UserController
{
    use Injectable;
  
    #[Autowired]
    private UserService $userService;
    
    public function register()
    {
        $this->userService->register(); 
    }
}

use Zenith\LaravelPlus\Attributes\Service;

#[Service]
class UserService
{
    public function register() {}
}

#[Autowired]注解可用于属性。使用#[Service]注解标记类为服务,这对于自动装配是必需的。