ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Snipe-IT 服务提供者开发指南:在 Provider 中注册验证规则与模型观察者

Snipe-IT 服务提供者开发指南:在 Provider 中注册验证规则与模型观察者 后端企业应用【免费下载链接】snipe-itA free open source IT asset/license management system项目地址https://gitcode.com/GitHub_Trending/sn/snipe-it点击查看免费下载本篇指南聚焦 Snipe-IT开源 IT 资产/授权管理系统中app/Providers/**目录的开发约定详细讲解如何通过ValidationServiceProvider注册命名验证规则以及如何在AppServiceProvider::boot()中挂载模型观察者。读完本文你将掌握 Snipe-IT 自定义验证规则的完整链路Provider 注册 → 模型$rules引用 → 控制器/表单校验触发并理解观察者注册方式与项目约定的边界为什么禁止在模型上使用#[ObservedBy]属性、为什么通用规则不能放进app/Rules。一、app/Providers目录的职责划分app/Providers存放 Laravel 应用级服务提供者。在 Snipe-IT 中这些 Provider 各自承担明确的引导职责本文涉及的两种核心模式分别由两个 Provider 承载Provider职责ValidationServiceProvider注册所有命名验证规则Validator::extend()/extendImplicit()闭包AppServiceProvider在boot()中挂载模型观察者、配置分页器与视图组件等目录下的其他 Provider如 AuthServiceProvider、EventServiceProvider、RouteServiceProvider 等分别负责权限策略绑定、事件监听注册与路由加载本文不展开。二、命名验证规则统一注册在 ValidationServiceProviderSnipe-IT 的约定是新增的命名验证规则一律以Validator::extend()或extendImplicit()闭包的形式写在 ValidationServiceProvider.php 的boot()方法中随后即可在模型或表单请求的$rules数组里以字符串形式直接引用该规则名。2.1 基本注册模式规则闭包接收 Laravel Validator 传入的标准四个参数$attribute被校验的字段名$value被校验字段的当前值$parameters规则字符串中冒号后以逗号分隔的参数数组$validator当前的 Validator 实例可通过$validator-getData()读取同一次请求中的其他字段以email_array为例它校验逗号分隔的收件人列表是否全部是合法邮箱Validator::extend(email_array, function ($attribute, $value, $parameters, $validator) { $value str_replace( , , $value); $array explode(,, $value); $email_to_validate []; foreach ($array as $email) { $email_to_validate[alert_email][] $email; } $rules [alert_email.* email]; $messages [ alert_email.* trans(validation.custom.email_array), ]; $validator Validator::make($email_to_validate, $rules, $messages); return $validator-passes(); });可以看到闭包内部把用户输入拆分为数组后用内置的email规则逐项校验并把错误消息映射到validation.custom.email_array翻译键。2.2 什么时候用extendImplicit()fmcs_company的启发Laravel 默认会跳过字段为空null/缺失时的显式扩展规则因此如果某条规则必须在空值提交时也要触发就必须用Validator::extendImplicit()注册这和内置的required、filled、accepted使用隐式规则是同一原因。源码中fmcs_company就是典型例子——它的职责正是严格多公司模式FMCS下有公司归属的非超级管理员必须选择一个公司不允许提交空值Validator::extendImplicit(fmcs_company, function ($attribute, $value, $parameters, $validator) { $settings Setting::getSettings(); if (! $settings-full_multiple_companies_support) { return true; } if ((bool) $settings-null_company_is_floater) { return true; } if (! empty($value)) { return true; } if (! auth()-check()) { return true; } $actor auth()-user(); if ($actor-isSuperUser()) { return true; } if (! $actor-companies()-exists()) { return true; } return false; });它的通过条件覆盖了完整场景FMCS 关闭、允许浮动空公司、表单已填值、CLI/Seeder 等无认证上下文、超级管理员、无公司归属用户在空伪公司命名空间合法操作。规则同时在Validator::replacer(fmcs_company, ...)中把:attribute替换为公司的翻译文案。2.3 用Validator::replacer()美化错误消息当需要把规则参数嵌入翻译后的错误消息时可以像two_column_unique_undeleted那样注册一个replacerValidator::replacer(two_column_unique_undeleted, function ($message, $attribute, $rule, $parameters) { $message str_replace(:table1, $parameters[0], $message); $message str_replace(:table2, $parameters[2], $message); // Change underscores to spaces for a friendlier display $message str_replace(_, , $message); return $message; });三、常用内置命名规则与参数速查下面按源码中 ValidationServiceProvider.php 的实际实现整理项目中最常用、可直接复用的命名规则及其参数约定。3.1 软删除场景下的唯一性规则Snipe-IT 大量使用 Laravel 软删除deleted_at。内置unique规则无法区分已删除记录与现存记录会导致重复数据无法重新创建因此项目提供了一整套配套规则规则名参数语义源码注释unique_undeleted:table,field表名、字段名仅在与现存未删除记录比较时保证唯一见$rules中大量使用如asset_tag unique_undeleted:assets,asset_tagexists_undeleted:table,column表名、列名默认id校验值指向的行的确存在且未被软删除用于用户输入 ID 的查找如 checkout 目标用法exists_undeleted:users,idunique_undeleted_in_scope:col1,col2,...作用域列名列表在兄弟节点范围内唯一支持树形表与 FMCS 公司内唯一NULL 按标准 SQL 语义自成桶模型侧只需写name unique_undeleted_in_scope:parent_id,company_idtwo_column_unique_undeleted:other_col另一列名两列组合唯一参数自动由 Trait 补齐表名与 ID见下节unique_undeleted内部还含有一个资产序列号的专门分支当校验的是assets.serial且系统设置unique_serial ! 1时直接放行Asset.php 中serial nullable|string|unique_undeleted:assets,serial。模型中的实际用法来自源码Asset.phpasset_tag [required, min:1, max:255, unique_undeleted:assets,asset_tag, not_array]Category.phpname required|min:1|max:255|two_column_unique_undeleted:category_typeCompany.phpname required|max:255|unique_undeleted_in_scope:parent_idtwo_column_unique_undeleted的参数补全依赖 UniqueUndeletedTrait 与 TwoColumnUniqueUndeletedTrait它们会把表名和当前记录 ID 自动前置到规则字符串中所以模型侧只需声明需要参与唯一约束的业务列。3.2 树形结构 / 父子关系规则Location.parent_id、Company.parent_id这类自引用字段需要防止环与深度失控项目按场景拆成多条规则各自给出可读的错误消息规则名语义non_circular:table,pk[,depth]沿父链向上追溯禁止任一祖先等于自身主键depth默认 50防止无限循环parent_must_be_top_level:table,pk被选父节点本身必须是顶级节点其parent_id为 NULL防止层级超过 1 层must_have_no_children:table,pk仅更新时生效如果当前行已有子节点则不允许给它再指定父节点parent_within_scope校验调用方有权限把行迁移到新父节点下按公司作用域比对防止越权改parent_id扩大作用域parent_matches_location_companyFMCS 下Location 的父级必须与子级属于同一公司阻断 GHSA-jmrm-535m-cx3c 类跨租户写入Location.php 的完整组合示例parent_id nullable|exists:locations,id|non_circular:locations,id|parent_matches_location_company,Company.php 的完整组合示例parent_id nullable|integer|exists:companies,id|parent_must_be_top_level:companies,id|must_have_no_children:companies,id|parent_within_scope,3.3 FMCS多公司支持相关规则多公司严格模式full_multiple_companies_support下各业务模型Accessory、Asset、Component、Consumable、Location 等的company_id/location_id普遍链式声明了company_id integer|nullable|exists:companies,id|fmcs_company, location_id exists:locations,id|nullable|fmcs_location,fmcs_company严格模式下强制有公司归属的用户提交非空公司隐式规则见 2.2。fmcs_location当scope_locations_fmcs开启时校验被引用的 Location 的有效公司归属沿父链追溯effectiveFmcsCompanyId()必须落在请求提交的company_id/company_ids作用域内无公司上下文时放行。注意源码中这两条规则都通过withoutGlobalScopes()绕过CompanyableScope查找真实记录——否则作用域会把外部租户的记录隐藏成不存在反而放行了跨租户写入。3.4 自定义字段与表单校验规则Snipe-IT 允许管理员自定义字段并配置正则与选项Provider 中也提供了配套校验valid_regex校验自定义字段里配置的regex:... 字符串在preg_match下不会抛异常仅验证可编译不验证语义。checkboxes/radio_buttons校验提交的多选值/单选值确实存在于 CustomField 定义的选项列表中兼容逗号分隔字符串的旧式提交。not_array拒绝数组值如 Asset.php 中model_id、asset_tag上使用。3.5 用户安全相关规则cant_manage_self禁止用户把自己的id设为manager_id自己当自己的上级。disallow_same_pwd_as_user_fields密码不得与username、email、first_name、last_name相同。letters/numbers/case_diff/symbols密码复杂度检测分别校验字母、数字、大小写混排与符号使用 Unicode 属性正则\pL、\pN等。3.6 其他is_unique_across_company_and_location跨company_idlocation_id双作用域唯一目前用于 Department.phpname required|string|max:255|is_unique_across_company_and_location:departments,name。错误消息通常挂在lang/*/validation.php的custom键下便于多语言覆盖。四、app/Rules的边界只放加密自定义字段规则对象项目明确规定app/Rules目录是加密自定义字段规则对象的专属领地禁止把通用规则对象放进去。查看 app/Rules 目录可见其成员具有明显的一致性——AlphaEncrypted、BooleanEncrypted、CssColor、DateEncrypted、EmailEncrypted、IPEncrypted、IPv4Encrypted、IPv6Encrypted、MacEncrypted、NumericEncrypted、RegexEncrypted、UrlEncrypted、ValidJson、AllowedUploadExtension、ExternalUrl等它们都服务于加密存储的自定义字段的加密后校验需求。这里的取舍逻辑很清晰通用、可复用的业务规则唯一性、树形结构、FMCS 作用域等→ 以闭包形式集中在ValidationServiceProvider字符串规则名在模型$rules中即写即用无需实例化对象。依赖具体加密算法/字段类型的规则加密字段的值必须能解密、格式正确→ 实现为独立的 Rule 类对象Rule::class或new XxxEncrypted()形式使用放在app/Rules。顺带一提UniqueUndeleted.php 也位于该目录它是加密字段默认值场景下对unique_undeleted规则的对象化封装与 Trait 提供的字符串形式互补。五、观察者注册统一放在 AppServiceProvider::boot()项目约定模型观察者通过Model::observe(ModelObserver::class)在 AppServiceProvider.php 的boot()方法中显式挂载禁止在模型类上使用 PHP 8 的#[ObservedBy]属性。当前源码中的完整注册块AppServiceProvider.phpAccessory::observe(AccessoryObserver::class); Asset::observe(AssetObserver::class); AssetModel::observe(AssetModelObserver::class); Component::observe(ComponentObserver::class); Consumable::observe(ConsumableObserver::class); License::observe(LicenseObserver::class); Location::observe(LocationObserver::class); Maintenance::observe(MaintenanceObserver::class); Setting::observe(SettingObserver::class); User::observe(UserObserver::class);对应的观察者实现位于 app/Observers如 AssetObserver.php、UserObserver.php各自监听模型的created/updated/deleting等事件用于维持库存快照、日志记录、关联校验等副作用。统一在 Provider 挂载的优势在于所有观察者注册点集中可见升级/排查时只需检查一个文件同时避免#[ObservedBy]属性与代码库中广泛存在的动态模型扩展、软删除作用域交互时产生意外行为。六、AppServiceProvider 的其余引导工作同文件内的常见约定虽然观察者挂载是boot()的核心但同文件中还包含几项 Snipe-IT 部署时常被问到的引导逻辑一并说明以便理解该文件的完整上下文强制 HTTPS当APP_URL以https://开头或设置了APP_FORCE_TLS时调用$url-forceScheme(https)若未设置APP_ALLOW_INSECURE_HOSTS则用URL::forceRootUrl()固定根 URL防止 Host 头伪造配置异常时输出APP_URL相关的错误日志AppServiceProvider.php。分页器Paginator::useBootstrap()使用 Bootstrap 风格分页。视图组件为layouts.default和partials.impersonation-banner绑定 SidebarComposer 与 ImpersonationBannerComposer。Schema 默认长度Schema::defaultStringLength(191)兼容 MySQL 旧索引长度限制。Http 客户端宏为HttpClientResponse注册throwIfNotJson()宏当同步适配器请求返回非 JSON 内容如误指向供应商 Web 控制台的 HTML 页面时抛出 SyncAdapterVendorException避免Sync complete. 0 hosts, 0 errors式的静默失败。register() 条件注册本地环境注册 Telescope生产环境且配置了 Rollbar token 时注册 RollbarServiceProvider并单例绑定自定义 SCIM 配置 SnipeSCIMConfig 覆盖默认 SCIM 行为AppServiceProvider.php。七、从规则到校验触发一条完整的调用链理解了注册约定后把整条链路串起来便于在实际开发中定位问题注册ValidationServiceProvider::boot()里Validator::extend(xxx, ...)将规则名注册进 Laravel 验证器。声明模型$rules如 Asset.php、Category.php或表单请求类app/Http/Requests中以字符串规则形式引用如unique_undeleted:assets,asset_tag带参数的规则依赖 UniqueUndeletedTrait 等 Trait 自动补齐表名与 ID。触发控制器调用模型校验或$request-validate()时Laravel 按规则字符串执行闭包$validator-getData()让规则能够读取同表单的其他字段如parent_must_be_top_level读取主键、fmcs_location读取company_id。消息校验失败时Validator::replacer()负责把参数渲染进validation.custom.*翻译字符串用户看到可读的错误提示。八、开发约定速查清单新命名验证规则 →Validator::extend()/extendImplicit()闭包写入 ValidationServiceProvider.php 的boot()。需要空值也触发校验的规则如必须选公司→ 用extendImplicit()并在闭包内自行处理各项放行条件。参数型规则表名、字段名、作用域列→ 用冒号 逗号分隔传入必要时配Validator::replacer()美化消息。通用规则对象 → 不要放进app/Rules那里只放加密自定义字段相关 Rule 类。观察者 → 在 AppServiceProvider.php 的boot()用Model::observe(Observer::class)显式注册不要使用#[ObservedBy]属性。多公司/作用域相关的规则在实现中大量使用withoutGlobalScopes()绕过作用域查找真实记录编写类似规则时务必注意这一点否则会形成作用域把外部记录藏起来 → 校验误放行的安全漏洞。赞分享后端企业应用【免费下载链接】snipe-itA free open source IT asset/license management system项目地址https://gitcode.com/GitHub_Trending/sn/snipe-it点击查看免费下载相关推荐ES6-learning类和面向对象编程的完整指南ES6 learning类和面向对象编程的完整指南 欢迎来到ES6 learning项目的终极教程 今天我们将深入探讨JavaScript ES6中最激design_patterns_in_typescript服务发现机制观察者模式与中介者模式的实践design_patterns_in_typescript服务发现机制观察者模式与中介者模式的实践 你是否在开发分布式系统时遇到过服务节点动态上下线难以追踪的示例工程Laravel-Excel 服务提供者解析ExcelServiceProvider 注册流程Laravel Excel 服务提供者解析ExcelServiceProvider 注册流程 服务提供者的核心作用 在 Laravel 框架中服务提供者S后端上一篇强力突破语言障碍Screen Translator让屏幕文字翻译变得如此简单下一篇百度网盘直链解析工具深度解析技术架构与高效下载实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表