The implemented annotations include routing, permission menu, concurrency lock, validator, and dependency injection
crastlin/laravel-annotation_v2 is a Laravel package for the implemented annotations include routing, permission menu, concurrency lock, validator, and dependency injection.
It currently has 3 GitHub stars and 142 downloads on Packagist (latest version v2.0.1beta).
Install it with composer require crastlin/laravel-annotation_v2.
Discover more Laravel packages by crastlin
or browse all Laravel packages to compare alternatives.
Last updated
从PHP8开始已经对注解(Attribute) 原生支持了,这有利于我们创建更快更好用的注解工具利器,为我们的编码工作带来更高的效率。之前开发的 PHP7.x + Laravel5.8.x 注解插件 在leanKu上受到不少朋友的关注,但大部分朋友已经全面切到PHP8+,希望能发布PHP8系列注解插件,虽然现在很多PHPer都转战Golang了,但PHP在我心中依旧占一席之地!所以我依然希望能为PHP开源尽点微薄之力,希望它越来越好,重回巅峰时刻。
composer require crastlin/laravel-annotation_v2:v1.2.1
Tips: 也可以在composer.json的 require内定义:
"crastlin/laravel-annotation_v2": "^v1.2.1"
sudo -u www php artisan annotation:config
配置项在以下具体功能中会详情说明
注意:根据实现的功能的需要,程序会生成缓存文件,以便更快的运行。文件根目录在config/annotation.php中的annotation_path项,默认目录在根目录的data目录,需要创建该目录,并且授权读写权限
Tips: 需要在类上配置 Controller 注解,否则将被排除扫描
namespace App\Http\Controllers\Portal;
use \Illuminate\Routing\Controller;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Route;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Route("index")]
function home()
{
// todo
}
}
/data/routes目录下生成对应路由文件Route::controller(App\Http\Controllers\Portal\IndexController::class)->group(function(){
Route::any('index', 'home')->name('index');
});
method来指定请求类型,例如namespace App\Http\Controllers\Portal;
use \Illuminate\Routing\Controller;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Route;
use Crastlin\LaravelAnnotation\Enum\Method;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Route("index", method: Method::POST)]
function home()
{
// todo
}
}
或者使用PostMapping
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("index")]
function home()
{
// todo
}
}
Route::controller(App\Http\Controllers\Portal\IndexController::class)->group(function(){
Route::post('index', 'home')->name('index');
});
Route支持的参数有:
pathControllers目录下的层级生成,如以上示例,未定义path时,地址为:/Portal/Index/homemethodPOST / GET / REQUEST等,默认为any类,支持的类型在包目录 Enum\Method 枚举类中,可以根据您的需要使用methodsMethod枚举对象namewherepath定义了,/index/{id},可配置where: ['id' => '[0-9]+'] ,更多条件路由配置可使用
Annotation/Attributes/Route 目录下的条件(包含Where)路由注解更多的路由注解,请查看Router接口的实现类,包括常用的
PostMapping/GetMapping/AnyMapping等
Group注解,实现路由分组 #[Group("api")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("index")]
function home()
{
// todo
}
#[PostMapping("article")]
function news()
{
// todo
}
}
Route::prefix('api')
->controller(App\Http\Controllers\Portal\IndexController::class)->group(function(){
Route::post('index', 'home')->name('api.home');
Route::post('article', 'news')->name('api.news');
});
-- 对应的路由地址:/api/index 和 /api/article
#[Group("api", middleware: "checker")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("index")]
function home()
{
// todo
}
#[PostMapping("article")]
function news()
{
// todo
}
}
// 在App\Http\Kernel.php中的属性:$routeMiddleware 配置 中间件映射
protected $routeMiddleware = [
'checker' => \App\Http\Middleware\Checker::class,
];
Route::prefix('api')
->middleware(array (
0 => 'checker',
))
->controller(App\Http\Controllers\Portal\IndexController::class)->group(function(){
Route::post('index', 'home')->name('api.home');
Route::post('article', 'news')->name('api.news');
});
中间件还可使用
Middleware注解绑定,中间件名称可以直接指定中间件文件地址,多个中间件可以定义成数组,例如
#[Group("api")]
#[Middleware([\App\Http\Middleware\Check::class, \App\Http\Middleware\ParamFilter::class])]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("index")]
function home()
{
// todo
}
#[PostMapping("article")]
function news()
{
// todo
}
}
Group 的参数:domain 或者使用 Domain 注解 #[Group("api", middleware: "checker")]
#[Domain("xxx.com")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("index")]
function home()
{
// todo
}
#[PostMapping("article")]
function news()
{
// todo
}
}
Tips:
Group支持类注解和方法注解,可以配合Domain/Middleware/Domain注解使用
可以在config/annotation.php的route配置项中,配置root_group项,可实现模块化分组
return [
'route' => [
// .....
'root_group' => [
// 用户模块
'User' => ['prefix' => 'user', 'domain' => 'user.xxx.com', 'middleware' => 'user.check'],
// 管理后台
'Admin' => ['prefix' => 'admin', 'domain' => 'admin.xxx.com', 'middleware' => 'admin.check']
],
// ... 更多模块
],
// ....
];
资源路由(Restful Api)是常用的接口实现方式,其以固定的请求方式和路由参数组合标准,可快速注册路由,可以使用注解:
ResourceMapping和ApiResourceMapping生成一组资源路由,实现接口:Crastlin\LaravelAnnotation\Extra\ResourceInterface,快速生成对应的方法
#[ResourceMapping("product")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller implements ResourceInterface
{
function index()
{
var_dump("=== is index cate ===");
}
function create()
{
var_dump("=== is create cate ===");
}
function store()
{
var_dump("=== is store cate ===");
}
function show(int $id)
{
var_dump("=== is show cate {$id} ===");
}
function edit(int $id)
{
var_dump("=== is edit cate {$id} ===");
}
function update(int $id)
{
var_dump("=== is update cate {$id} ===");
}
function destroy(int $id)
{
var_dump("=== is destroy cate {$id} ===");
}
}
config/annotation.php中修改(route > auto_create_case ),默认在开发环境自动生成,可以执行以下命令主动生成路由sudo -u www php artisan annotation:route
Tips: 建议在生产环境配置hook,每次发版完成后,自动更新路由
在开发后台时,经常会需要使用到功能菜单和角色权限分配的功能,使用注解的好处在于,开发时定义好菜单树和权限节点信息,无需在数据库繁琐添加,只需要使用生成命令,快速将注解的菜单树和权限节点保存到数据库,方便环境切换和移植,为开发者整理菜单节约宝贵的时间。
Tree 配置一个根节点,并使用 Node 配置一个子节点#[Tree("首页")]
#[Controller]
class IndexController
{
#[Node("数据")]
#[PostMapping("data_list")]
function dataList()
{
// todo
}
#[Node("新闻")]
#[PostMapping("articles")]
function newsList()
{
// todo
}
}
sudo -u www php artisan annotation:node
node表,用于保存节点数据sudo -u www php artisan annotation:node_store
生成对应的节点如下:
Tips: 每个控制器可配置成一个根节点,控制器内的所有方法默认都是该根节点的子节点。
parent参数自定义父节点#[Tree("首页")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Node("数据")]
#[PostMapping("data")]
function dataList()
{
// todo
}
#[Node(name: "数据详情", parent: "dataList")]
#[PostMapping]
function detail()
{
// todo
}
#[Node("新闻")]
#[PostMapping("articles")]
function newsList()
{
// todo
}
#[Node("新闻详情", parent: "newsList")]
#[PostMapping("article")]
function news()
{
// todo
}
}
生成对应的节点如下:
====== 通用(Tree / Node)注解参数 ======
name
节点名称,用权限于节点名称配置显示、前端菜单名称显示
sort
菜单排序,存在多个根(相对)节点时,设置该参数排序,默认按生成顺序排序
isMenuNode
是否设置为菜单,用于前端菜单显示配置,Tree注解默认为菜单节点,Node注解则默认隐藏菜单。
isAuthNode
是否需要验证权限,用于角色权限验证配置,Tree注解默认不验证,Node注解默认需要鉴权。
preNamedSubMethods
定义需要前置名称的方法名,多个用逗号分隔。如果控制器为多态实现,方法在父类中定义时,如果需要在父类方法增加前置名称(子类的Tree注解定义),则需在子类Tree注解定义该参数。
virtualNode
定义根节点虚拟节点方法名称,默认为:defaultPage
checkMode
检测模式,配置强验证模式时(NodeMode::STRICT_MODE)生成节点时,检查当前控制器是否存在未配置Node注解的方法,否则抛出
AnnotationException 异常错误
====== 以下是 Node 注解参数 ======
parent
定义父节点名称,默认为当前控制器的 virtualNode 节点,如果父节点不在当前控制器,则parent配置控制器目录 {controller}/{action},例如:parent: "User/index",如果是跨模块。则需配置 {prefix}/{controller}/{action}, 例如:parent: "portal/User/index"。
code
定义节点分类,用于日志、按钮级权限配置,code是一个Enum类型,可定义类型在 Crastlin\LaravelAnnotation\Enum\NodeCode 。
icon
定义前端主菜单图标,一般为前端样式名称,或图标url地址
remark
节点备注,用于角色授权时,节点显示
ignore
忽略该节点,配置为 true 时,则该节点跳过生成
delete
删除该节点,用于废弃的方法,删除权限节点
component
组件名称,用于前端Vue项目中组件页面的引用
Tips: 执行生成节点后,修改方法名将会产生无用的节点数据,无法删除,建议方法确定好后,再执行命令生成,否则只能清空 node 表中的数据,重新生成。
#[Tree("动物园")]
abstract class Animal extends \Illuminate\Routing\Controller
{
#[Node("主页", isMenuNode: true)]
#[PostMapping]
function index()
{
// todo
}
#[Node("观看时间", isMenuNode: true)]
#[PostMapping]
function schedule()
{
// todo
}
}
#[Tree("长颈鹿", preNamedSubMethods: "index,schedule")]
#[Controller]
class GiraffeController extends Animal
{
}
#[Tree("老虎", preNamedSubMethods: "index,schedule")]
#[Controller]
class TigerController extends Animal
{
}
经常会遇到这样的场景,当同一个请求(公共级 / 用户级 / 项目级等)需要限制并发,可以使用
SyncLock,注解控制器方法,达到限制并发的效果,SyncLock采用的是 redis 的 set 实现方式,redis配置默认使用的Laravelenv配置项,可自行在生成的config/annotation.php中配置。
SyncLock 注解#[Tree("首页")]
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Node("数据")]
#[PostMapping("data_list")]
function dataList()
{
// todo
}
#[Node("新闻")]
#[PostMapping("articles")]
function newsList()
{
// todo
}
#[SyncLock(suffix: "{post.id}")]
#[Node("更新", parent: "newsList")]
#[PostMapping]
function updateNews()
{
// todo
}
}
updateNews 方法,当并发相同 id 请求时,则会响应正在更新中的提示expire
加锁有效期,超过有效期后,将自动解除该锁,当请求内有异常未处理,或者配置了 once: true时,会在一个加锁周期后解除。
name
锁名称,不配置时,默认为当前{模块名}_{控制器名}_{方法名}
once
定义限制在锁有效期内只能请求一次,请求完不会自动释放锁
code
锁定被阻断访问时返回的响应code码,在 Extra/ResponseCode 枚举中已定义了常用的代码,可自定义枚举类并实现接口 Extra/ResponseCodeEnum 。
msg
锁定被阻断访问时返回的响应错误信息。
response
自定义响应数据格式,数组类型,以json格式响应。
prefix
自定义锁前置key
suffix
自定义锁名后缀,可通过花括号取请求变量,例如使用 post请求的id 为后缀,则使用 {post.id},已支持的请求类型有:input / get / post / header / query / date
suffixes
组合后缀定义,可配置多个后缀名或请求变量。
如果需要按登录身份进行并发限制,则可以使用
SyncLockByTokensuffix参数可通过参数或配置文件config/annotation.php中的interceptor->lock->token配置,默认配置为:{header.token}
Validation注解快速开启一个验证器#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Validation(field: "message",rule: "required", attribute: "留言", message: ":attribute不能为空")]
#[PostMapping("submitMsg")]
function saveMessage()
{
// todo
}
}
namespace App\Http\Controllers\Portal;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("submitMsg")]
function saveMessage(Request $request)
{
$validator = Validator::make($request->all(), [
'message' => 'required',
], [
'message.required' => ':attribute不能为空',
],[
'message' => '留言',
]);
if($validator->fails())
return ['code' => 500, 'msg' => $validator->errors()->first()];
// todo
}
}
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Validation(rules: [
"username" => ["required", "alpha_dash"],
"message" => ["required"]
], messages: [
'username.required' => ":attribute不能为空",
'username.alpha_dash' => ":attribute只能是大小写字母、数字或下划线",
'message.required' => ":attribute不能为空",
], attributes: [
"username" => "用户名",
"message" => "留言",
])
]
#[PostMapping("submitMsg")]
function saveMessage()
{
// todo
}
}
创建验证类需要继承基类
Crastlin\LaravelAnnotation\Extra\Validate,该验证基类已实现了很多常用的方法,以及增加了callback验证器,可实现自定义方法实现验证
app目录下创建验证器目录Validate,并创建MessageCheck类namespace App\Validate;
use Crastlin\LaravelAnnotation\Extra\Validate;
class MessageCheck extends Validate
{
protected array $rules = [
"username" => ["required", "alpha_dash"],
"message" => 'required|callback:checkSafeTags',
], $attributes = [
"username" => "用户名",
"message" => "留言",
];
protected function checkSafeTags() :bool
{
if(str_contains($this->data['message'],'<script'))
return false;
return true;
}
}
Validate验证基类集成了大部分错误提示消息模板,可以省去每次定义
Validation注解,并设置参数:class 为 \App\Validate\MessageCheck::classnamespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Extra\Validate;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Validation(class: \App\Validate\MessageCheck::class)]
#[PostMapping("submitMsg")]
function saveMessage()
{
// todo
}
}
namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Extra\Validate;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("submitMsg")]
function saveMessage(Request $request)
{
$validate = Validate::make(\App\Validate\MessageCheck::class, $request->all());
if($validate->fails())
return ['code' => 500, 'msg' => $validate->errors()->first()];
// todo
}
}
Crastlin\LaravelAnnotation\Extra\Validate使用匿名类扩展实现验证示例namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Extra\Validate;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("submitMsg")]
function saveMessage(Request $request)
{
$validate = new class extends Validate{
protected array $rules = [
"username" => ["required", "alpha_dash"],
"message" => 'required|callback:checkSafeTags',
], $attributes = [
"username" => "用户名",
"message" => "留言",
];
protected function checkSafeTags() :bool
{
if(str_contains($this->data['message'],'<script'))
return false;
return true;
}
};
$validator = $validate->setData($request->all())->validate();
if($validator->fails())
return ['code' => 500, 'msg' => $validator->errors()->first()];
// todo
}
}
Crastlin\LaravelAnnotation\Attributes\Validation目录下namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Attributes\Validation\Required;
use Crastlin\LaravelAnnotation\Attributes\Validation\Regex;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[Required("username", attribute: "用户名")]
#[Regex("username", ruleValue: "~^\w+$~", attribute: "用户名")]
#[Required("message", attribute: "留言")]
#[PostMapping("submitMsg")]
function saveMessage()
{
// todo
}
}
namespace App\Http\Controllers\Portal;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
#[Controller]
class IndexController extends \Illuminate\Routing\Controller
{
#[PostMapping("submitMsg")]
function saveMessage(Request $request)
{
$validator = Validator::make($request->all(), [
'username' => ['required', 'regex:~^\w+$~'],
'message' => 'required',
], [
'required' => ':attribute不能为空',
'username.regex' => ':attribute格式不正确',
],[
'username' => '用户名',
'message' => '留言',
]);
if($validator->fails())
return ['code' => 500, 'msg' => $validator->errors()->first()];
// todo
}
}
Crastlin\LaravelAnnotation\Extra\BaseController类中的InvokeTrait,包括后续要讲到的依赖注入功能以及自定义类验证、依赖注入的实现。namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Between;
use Crastlin\LaravelAnnotation\Extra\BaseController;
#[Controller]
class IndexController extends BaseController
{
#[PostMapping("submitMsg/{id}")]
function saveMessage(#[Between(ruleValue: "1,2")] int $id)
{
// todo
}
}
id长度必须为1到2位的验证Tips: 参数验证器和方法验证器一样,可以定义多个同时生效
Crastlin\LaravelAnnotation\Extra\BaseControllerInvokeTrait,并使用类调用方式实现中间方法转发调用,(即魔术方法__invoke的使用)Service接口层和Impl类实现层,通过匿名代理类,实现懒加载的效果,性能更强(推荐使用)Tips:
InvokeTrait中已经定义好常用的:请求数据data,响应码resCode,错误信息errText、 返回数据result,以及对应的get方法,可以使用统一方法返回值(bool)和类响应标准,有利于开发效率和标准化,减少BUG,强烈推荐使用!
环境配置注入、配置注入、请求参数注入namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Env;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Value;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Input\All;
use Crastlin\LaravelAnnotation\Extra\BaseController;
#[Controller]
class IndexController extends BaseController
{
// 环境配置注入
#[Env("APP_ENV")]
protected string env;
// config注入
#[Value("app.debug")]
protected bool $debug;
// 请求参数注入,还可以使用Input/Get/Post/Query等
#[All]
protected array $params;
#[PostMapping("submitMsg/{id}")]
function saveMessage()
{
// todo
}
}
namespace App\Http\Middleware;
use Crastlin\LaravelAnnotation\Facades\Injection;
use Closure;
use Illuminate\Http\Request;
class TokenCheck
{
function handle(Request $request, Closure $next)
{
// var_dump('===== is Token Middleware ====');
Injection::bind('params', $request->all());
Injection::bind('header.token', $request->header("token"));
return $next($request);
}
}
Autowired或带参数的Inject注解注入namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Extra\BaseController;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Inject;
#[Controller]
class IndexController extends BaseController
{
#[Autowired]
protected array $params;
#[Inject("header.token")]
protected string $token
#[PostMapping("submitMsg/{id}")]
function saveMessage()
{
// todo
}
}
namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Extra\BaseController;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Inject;
#[Controller]
class IndexController extends BaseController
{
#[PostMapping("submitMsg/{id}")]
function saveMessage(#[Autowired] array $params, #[Inject("header.token")] string $token)
{
// todo
}
}
namespace App\Http\Middleware;
use Crastlin\LaravelAnnotation\Facades\Injection;
use Closure;
use Illuminate\Http\Request;
class TokenCheck
{
function handle(Request $request, Closure $next)
{
// var_dump('===== is Token Middleware ====');
Injection::bind('params', $request->all());
$token = $request->header("token");
Injection::bind('header.token', $token);
$userId = TokenAuth::getUserId($token);
$user = User::find($userId);
if(!$user)
return response()->json(['code' => 400, 'msg' => '用户不存在']);
Injection::bind('model.user', $user);
return $next($request);
}
}
app/Service/UserService.php,并 use Crastlin\LaravelAnnotation\Utils\Traits\InvokeTrait namespace App\Service;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Inject;
use Crastlin\LaravelAnnotation\Extra\ResponseCode;
use Crastlin\LaravelAnnotation\Utils\Traits\SingletonTrait;
use Crastlin\LaravelAnnotation\Utils\Traits\InvokeTrait;
use App\Model\User;
class UserService
{
use SingletonTrait, InvokeTrait;
#[Inject("model.user")]
protected ?User $user;
function updateAvatar(#[Autowired] string $avatar): bool
{
var_dump($this->user);
$this->user->avatar = $avatar;
$this->user->save();
$this->resCode = ResponseCode::SUCCESS;
return true;
}
}
namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Numeric;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Required;
use Crastlin\LaravelAnnotation\Extra\BaseController;
use Crastlin\LaravelAnnotation\Facades\Injection;
use App\Service\UserService;
#[Controller]
class UserController extends BaseController
{
#[Autowired]
protected array $params;
#[Required("user_id")]
#[Numeric("user_id")]
#[PostMapping("updateAvatar")]
function updateAvatar()
{
return $this->callService(UserService::class, 'updateAvatar');
}
}
以上示例中,
updateAvatar方法参数$avatar注入规则:自定义绑定数据 > 请求参数(form 或 json)avatar字段
使用接口依赖注入前先要定义好
Service层和Impl实现层
app/Service/UserService.phpnamespace App\Service;
use Crastlin\LaravelAnnotation\Extra\BaseService;
/**
* @mixin App\Service\Impl\User
*/
interface UserService extends BaseService
{
function updateAvatar(string $avatar = ''): bool;
}
app/Service/Impl/User.php,并且在该实现类标记Service注解namespace App\Service\Impl;
use App\Service\UserService;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Service;
use Crastlin\LaravelAnnotation\Extra\BaseImplement;
#[Service]
class User extends BaseImplement implements UserService
{
#[Inject("model.user")]
protected ?User $user;
function updateAvatar(#[Autowired] string $avatar = ''): bool
{
var_dump($this->user);
$this->user->avatar = $avatar;
$this->user->save();
$this->resCode = ResponseCode::SUCCESS;
return true;
}
function myOrders(): bool
{
// todo
return true;
}
}
namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Numeric;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Required;
use Crastlin\LaravelAnnotation\Extra\BaseController;
use Crastlin\LaravelAnnotation\Facades\Injection;
use App\Service\UserService;
#[Controller]
class UserController extends BaseController
{
#[Autowired]
protected UserService $userService;
#[Required("user_id")]
#[Numeric("user_id")]
#[PostMapping("updateAvatar")]
function updateAvatar()
{
if(!$this->userService->updateAvatar()){
var_dump($this->userService->getError());
var_dump($this->userService->getResCode());
}else{
var_dump($this->userService->getResult());
}
}
}
Qualifier注解指定namespace App\Http\Controllers\Portal;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Autowired;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Qualifier;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Numeric;
use Crastlin\LaravelAnnotation\Annotation\Attributes\Validation\Required;
use Crastlin\LaravelAnnotation\Extra\BaseController;
use Crastlin\LaravelAnnotation\Facades\Injection;
use App\Service\UserService;
#[Controller]
class UserController extends BaseController
{
#[Autowired]
#[Qualifier(\App\Service\Impl\User::class)]
protected UserService $userService;
#[Required("user_id")]
#[Numeric("user_id")]
#[PostMapping("updateAvatar")]
function updateAvatar()
{
if(!$this->userService->updateAvatar()){
var_dump($this->userService->getError());
var_dump($this->userService->getResCode());
}else{
var_dump($this->userService->getResult());
}
}
}
Tips 使用接口类型注入只能使用
Autowired注解,且实现类必须标记Service注解,否则会被排除。使用Qualifier指定实现类可以是类名或类命名空间地址。如果非工厂模式,Service层可以不定义任何方法,只需在头部增加@mixin App\Service\Impl\User则Idea会自动映射方法提示