PHP OOP实现类Laravel Eloquent链式调用按需返回结果
类Laravel Eloquent链式调用实现方案
问题背景
我受Laravel Eloquent启发,正在编写一个简易的PHP数据库操作类,已知Laravel支持如下链式调用写法:
// 直接获取全量结果 $run = DB::table('users')->where('id', 3)->where('level', 2)->get(); // 获取结果后统计总数 $run = DB::table('users')->where('id', 3)->where('level', 2)->get()->count(); // 获取结果后取第一条 $run = DB::table('users')->where('id', 3)->where('level', 2)->get()->first(); // 获取结果后提取指定字段转数组 $run = DB::table('users')->where('id', 3)->where('level', 2)->get()->pluck('id')->toArray();
目前我已经实现了get()后接count()、first()的逻辑,但不知道怎么让get()直接调用时返回全量结果,最初设想是类似如下的分支判断逻辑:
function __construct() { if(当get()之后没有接续调用其他方法时){ // 返回默认结果集 return $this->results(); } else{ // 执行后续链式调用逻辑 } }
核心实现原理
首先要明确一个认知:PHP执行代码是从左到右逐段解析的,调用get()的瞬间,PHP根本不知道后面会不会接续调用其他方法,你伪代码里写的「判断get()后有没有后续调用」的逻辑在PHP层面根本无法实现。
Laravel的链式调用从来不是在同一个类里做分支判断,核心逻辑是不同阶段返回不同类的实例:
- 调用
table()/from()/where()这类拼接查询条件的方法时,返回「查询构造器类」自身的实例,所以可以持续拼接查询条件 - 调用
get()方法时,不直接返回原生数组结果,而是返回一个集合(Collection)类实例,所有对结果集的操作(count、first、pluck等)都封装在集合类中 - 集合类的方法如果是对结果集做加工,就返回加工后的新集合实例,继续支持链式调用;如果是输出最终值(比如count()返回数字、toArray()返回原生数组),链式调用自然终止。
补充说明:你猜测的
->get()->pluck('id')->toArray()->first()写法在Laravel中实际会报错,toArray()返回的是原生PHP数组,不存在可调用的first()方法,只有集合实例才能调用集合封装的方法。
具体实现代码
你只需要把查询构造逻辑和结果处理逻辑拆成两个独立的类,就能兼容所有调用场景:
1. 查询构造器类(即你当前编写的DB类)
所有拼接SQL的方法执行完逻辑后返回$this支持链式拼接,get()方法执行SQL拿到结果集后,不直接返回数组,而是返回集合类实例:
class DB { protected $table; protected $wheres = []; public static function from(string $table): self { $instance = new self(); $instance->table = $table; return $instance; } public function where(string $column, $value): self { $this->wheres[] = [$column, '=', $value]; return $this; } public function get(): Collection { // 这里替换成你实际执行SQL、拿到结果集数组的逻辑 $results = $this->executeQuery(); // 返回集合实例,而不是原生数组 return new Collection($results); } // 你的实际SQL执行逻辑 protected function executeQuery(): array { // 省略SQL拼接、PDO查询逻辑,最终返回查询到的二维数组 return []; } }
2. 集合类(封装结果集操作)
构造方法接收查询到的结果数组,所有结果处理方法都在这个类中实现:
class Collection { protected $items = []; public function __construct(array $items) { $this->items = $items; } // 返回结果总数 public function count(): int { return count($this->items); } // 返回第一条结果 public function first() { return $this->items[0] ?? null; } // 提取指定字段,返回新集合实例支持继续链式 public function pluck(string $column): self { $plucked = array_column($this->items, $column); return new self($plucked); } // 返回原生数组结果 public function toArray(): array { return $this->items; } }
适配直接调用get()的场景
如果你需要get()返回的集合实例可以直接像数组一样遍历、或者直接输出转JSON,可以给Collection类实现PHP内置的预置接口:
// 实现IteratorAggregate接口支持遍历,实现JsonSerializable接口支持JSON序列化 class Collection implements \IteratorAggregate, \JsonSerializable { // ... 原有代码不变 public function getIterator() { return new \ArrayIterator($this->items); } public function jsonSerialize() { return $this->items; } }
实现后你可以直接遍历get()返回的结果,和使用原生数组体验完全一致。
内容的提问来源于stack exchange,提问作者WEB TM
相关产品推荐
相关产品推荐

