Laravel宏中使用函数:无需扩展Eloquent Builder拆分搜索逻辑
没问题,我刚好有几个实用的方案能帮你解决这个问题——不用完全扩展Eloquent Builder就能把复杂的search逻辑拆分成清晰的小函数:
方案一:用工具类封装拆分逻辑(最推荐)
把拆分后的逻辑放到单独的工具类里,让每个方法专注处理一部分逻辑,然后在macro回调里调用这些方法。这样既保持了代码的模块化,又不会污染Builder的作用域。
首先创建一个工具类,比如BuilderSearchHelper:
class BuilderSearchHelper { /** * 处理基础字段搜索 */ public function handleBasicSearch($builder, string $searchBy) { $builder->where('name', 'like', "%{$searchBy}%") ->orWhere('email', 'like', "%{$searchBy}%"); } /** * 处理关联模型的搜索逻辑 */ public function handleRelatedSearch($builder, string $searchBy) { $builder->orWhereHas('category', function ($query) use ($searchBy) { $query->where('title', 'like', "%{$searchBy}%"); }); } /** * 处理复杂的过滤条件 */ public function handleAdvancedFilters($builder, string $searchBy) { // 这里可以添加更复杂的逻辑,比如多条件组合、范围查询等 $builder->orWhere(function ($query) use ($searchBy) { $query->where('status', '=', 'active') ->where('created_at', '>=', now()->subMonth()); }); } }
然后在服务提供者的boot方法里调用这个工具类:
use Illuminate\Database\Eloquent\Builder; public function boot() { Builder::macro('search', function (string $searchBy) { $searchHelper = new \App\Helpers\BuilderSearchHelper(); // 依次调用拆分后的逻辑方法 $searchHelper->handleBasicSearch($this, $searchBy); $searchHelper->handleRelatedSearch($this, $searchBy); $searchHelper->handleAdvancedFilters($this, $searchBy); // 记得返回Builder实例,保持链式调用 return $this; }); }
这个方案的优点是逻辑完全解耦,每个方法职责单一,后期维护、测试都非常方便。
方案二:绑定服务提供者的方法到Builder作用域
如果不想额外创建工具类,想把拆分的方法留在服务提供者里,可以利用PHP的闭包绑定功能,把服务提供者的私有方法绑定到Builder实例的作用域中,这样方法里的$this就会指向Builder对象。
示例代码:
use Illuminate\Database\Eloquent\Builder; class AppServiceProvider extends ServiceProvider { public function boot() { Builder::macro('search', function (string $searchBy) { // 将服务提供者的handleBasicSearch方法绑定到当前Builder实例 $basicSearch = \Closure::fromCallable([$this, 'handleBasicSearch'])->bindTo($this, Builder::class); $basicSearch($searchBy); // 同理绑定其他拆分方法 $relatedSearch = \Closure::fromCallable([$this, 'handleRelatedSearch'])->bindTo($this, Builder::class); $relatedSearch($searchBy); return $this; }); } /** * 基础搜索逻辑 */ private function handleBasicSearch(string $searchBy) { // 这里的$this已经是Eloquent Builder实例了 $this->where('name', 'like', "%{$searchBy}%"); } /** * 关联模型搜索逻辑 */ private function handleRelatedSearch(string $searchBy) { $this->orWhereHas('category', function ($query) use ($searchBy) { $query->where('title', 'like', "%{$searchBy}%"); }); } }
这个方案适合逻辑拆分不多、不想额外建类的场景,把所有逻辑都留在服务提供者里,但依然保持代码的整洁。
方案三:用Trait复用拆分的逻辑
如果你的搜索逻辑需要在多个macro或者项目的其他地方复用,可以用Trait来封装这些方法,然后直接在macro里调用(不用全局修改Builder类)。
首先创建Trait:
trait SearchableBuilder { /** * 静态方法接收Builder实例,处理基础搜索 */ public static function handleBasicSearch($builder, string $searchBy) { $builder->where('name', 'like', "%{$searchBy}%"); } /** * 处理关联搜索 */ public static function handleRelatedSearch($builder, string $searchBy) { $builder->orWhereHas('category', function ($query) use ($searchBy) { $query->where('title', 'like', "%{$searchBy}%"); }); } }
然后在服务提供者里调用:
use Illuminate\Database\Eloquent\Builder; public function boot() { Builder::macro('search', function (string $searchBy) { // 直接调用Trait里的静态方法,传入当前Builder实例 \App\Traits\SearchableBuilder::handleBasicSearch($this, $searchBy); \App\Traits\SearchableBuilder::handleRelatedSearch($this, $searchBy); return $this; }); }
这个方案适合需要复用搜索逻辑的场景,Trait可以在多个地方被调用,避免重复代码。
内容的提问来源于stack exchange,提问作者Norgul
相关产品推荐
相关产品推荐

