zenithsu / laravel-plus
Laravel-Plus 是一个高效的 Laravel 扩展包,旨在提高开发效率。它集成了强大的注解处理和实用的辅助类,使 Laravel 应用的开发更加方便和灵活。
Requires
- php: ^8.3
- laravel/framework: ^11.0
Requires (Dev)
- pestphp/pest: 3.x-dev
README
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.php
或 api.php
中分别配置路由,这在开发过程中不太方便,因为它需要在不同文件之间切换。
相比之下,Spring Boot 或 Flask 等框架允许使用注解来配置路由,这使得编码过程更加流畅。因此,我封装了基于注解的路由。
首先,您需要在 api.php 中注册,代码如下
use Zenith\LaravelPlus\EasyRouter; EasyRouter::register();
然后,您可以在控制器方法之前使用路由注解
class UserController { #[GetMapping(path: '/login')] public function login() {} }
您可以通过 /api/login
访问此 API。除了 GetMapping
,还支持 PostMapping
、PutMapping
和 DeleteMapping
。
此外,您还可以向控制器添加 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]
注解标记类为服务,这对于自动装配是必需的。