createMock(RequestInterface::class); $mock_request->method('input') ->willReturnCallback(function (string $key, $default = null) use ($request_params) { return $request_params[$key] ?? $default; }); $mock_request->method('getAttribute') ->willReturnCallback(function (string $key, $default = null) { if ($key === 'scope_type') { return 'all'; } return $default; }); // 创建匿名具体子类 $controller = new class ($filters) extends AbstractDataController { private array $testFilters; public function __construct(array $filters) { $this->testFilters = $filters; } protected function getModelClass(): string { return Product::class; } protected function getListFields(): array { return ['*']; } protected function getDetailFields(): array { return ['*']; } protected function getAllowedFilters(): array { return $this->testFilters; } /** * 暴露 applyFilters 用于测试 */ public function testApplyFilters(Builder $query): void { $this->applyFilters($query); } /** * 暴露 applyDataScope 用于测试 */ public function testApplyDataScope(Builder $query): void { $this->applyDataScope($query); } }; // 通过反射注入 mock request $reflection = new \ReflectionClass(AbstractDataController::class); $parent = $reflection->getParentClass(); $prop = $parent->getProperty('request'); $prop->setAccessible(true); $prop->setValue($controller, $mock_request); return $controller; } /** * 创建一个带 scope 属性的 controller */ protected function createControllerWithScope(string $scope_type, array $scope_ids): AbstractDataController { $mock_request = $this->createMock(RequestInterface::class); $mock_request->method('input') ->willReturn(null); $mock_request->method('getAttribute') ->willReturnCallback(function (string $key, $default = null) use ($scope_type, $scope_ids) { if ($key === 'scope_type') { return $scope_type; } if ($key === 'scope_ids') { return $scope_ids; } return $default; }); $controller = new class () extends AbstractDataController { public function __construct() {} protected function getModelClass(): string { return Product::class; } protected function getListFields(): array { return ['*']; } protected function getDetailFields(): array { return ['*']; } protected function getAllowedFilters(): array { return []; } public function testApplyDataScope(Builder $query): void { $this->applyDataScope($query); } }; $reflection = new \ReflectionClass(AbstractDataController::class); $parent = $reflection->getParentClass(); $prop = $parent->getProperty('request'); $prop->setAccessible(true); $prop->setValue($controller, $mock_request); return $controller; } /** * 从 Builder 提取 where 条件,返回可断言的结构 */ protected function extractWheres(Builder $query): array { return $query->getQuery()->wheres; } protected function createBuilder(): Builder { // 使用 Product 模型创建一个干净的 Builder(不会实际执行查询) return Product::query()->newQuery(); } // ========== exact 筛选 ========== public function test_apply_filters_exact_match(): void { $controller = $this->createController( ['company_id' => 'exact'], ['company_id' => '100'] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('company_id', $wheres[0]['column']); $this->assertSame('=', $wheres[0]['operator']); $this->assertSame('100', $wheres[0]['value']); } public function test_apply_filters_exact_skips_null_value(): void { $controller = $this->createController( ['company_id' => 'exact'], [] // 无参数 ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(0, $wheres); } public function test_apply_filters_exact_skips_empty_string(): void { $controller = $this->createController( ['company_id' => 'exact'], ['company_id' => ''] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(0, $wheres); } // ========== like 筛选 ========== public function test_apply_filters_like_match(): void { $controller = $this->createController( ['name' => 'like'], ['name' => '手机'] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('name', $wheres[0]['column']); $this->assertSame('ilike', $wheres[0]['operator']); $this->assertSame('%手机%', $wheres[0]['value']); } // ========== date_from 筛选 ========== public function test_apply_filters_date_from(): void { $controller = $this->createController( ['created_date_from' => 'date_from'], ['created_date_from' => '2025-01-01'] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('created_date', $wheres[0]['column']); $this->assertSame('>=', $wheres[0]['operator']); $this->assertSame('2025-01-01', $wheres[0]['value']); } // ========== date_to 筛选 ========== public function test_apply_filters_date_to(): void { $controller = $this->createController( ['created_date_to' => 'date_to'], ['created_date_to' => '2025-12-31'] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('created_date', $wheres[0]['column']); $this->assertSame('<=', $wheres[0]['operator']); $this->assertSame('2025-12-31 23:59:59', $wheres[0]['value']); } // ========== 多条件组合 ========== public function test_apply_filters_multiple_conditions(): void { $controller = $this->createController( [ 'company_id' => 'exact', 'name' => 'like', 'status_id' => 'exact', ], [ 'company_id' => '1', 'name' => 'iPhone', 'status_id' => '2', ] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(3, $wheres); } public function test_apply_filters_mixed_with_empty_values(): void { $controller = $this->createController( [ 'company_id' => 'exact', 'name' => 'like', 'status_id' => 'exact', ], [ 'company_id' => '1', 'name' => '', // 空值应跳过 // status_id 缺失也应跳过 ] ); $query = $this->createBuilder(); $controller->testApplyFilters($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); // 仅 company_id } // ========== DataScope 过滤 ========== public function test_apply_data_scope_all_adds_no_conditions(): void { $controller = $this->createControllerWithScope('all', []); $query = $this->createBuilder(); $controller->testApplyDataScope($query); $wheres = $this->extractWheres($query); $this->assertCount(0, $wheres); } public function test_apply_data_scope_store_adds_store_id_where_in(): void { $controller = $this->createControllerWithScope('store', [10, 20, 30]); $query = $this->createBuilder(); $controller->testApplyDataScope($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('store_id', $wheres[0]['column']); $this->assertSame('In', $wheres[0]['type']); $this->assertSame([10, 20, 30], $wheres[0]['values']); } public function test_apply_data_scope_platform_adds_platform_id_where_in(): void { $controller = $this->createControllerWithScope('platform', [1, 2]); $query = $this->createBuilder(); $controller->testApplyDataScope($query); $wheres = $this->extractWheres($query); $this->assertCount(1, $wheres); $this->assertSame('platform_id', $wheres[0]['column']); $this->assertSame('In', $wheres[0]['type']); $this->assertSame([1, 2], $wheres[0]['values']); } }