Simplify VarietyRepository¶
Priority: 🟠HIGH Status: Planning Related QA Analysis: qa-analysis-overview.md
Problem Statement¶
Repository/VarietyRepository.php:132 has excessive complexity in query building:
Violations¶
- Method:
findWithFiltersAndPagination()(line 132) - Cyclomatic Complexity: 15/10 (50% over threshold)
- NPath Complexity: 1,215/250 (386% over threshold!)
Impact¶
- Query Correctness: Complex query building prone to bugs
- Performance: Difficult to optimize complex queries
- Maintainability: Hard to add new filters or modify query logic
- Testing: Difficult to test all filter combinations
Guideline Violations¶
- SOLID - Single Responsibility Principle: Method doing too much
- Repository Pattern: Business logic creeping into data access layer
Root Cause Analysis¶
Based on method name, findWithFiltersAndPagination() likely handles:
- Multiple filter types (variety name, region, processing, etc.)
- Conditional query building based on active filters
- Join logic for related entities
- Pagination logic (limit, offset, ordering)
- Possibly sorting options
- Complex WHERE clause construction
- Count queries for pagination
High NPath (1,215) suggests many conditional branches for different filter combinations.
Proposed Refactoring Strategy¶
Step 1: Analyze Current Method¶
- Document all supported filters
- Identify query building patterns
- Map conditional logic
- Review join requirements
- Check for duplication
Step 2: Extract Query Builder Pattern¶
Create dedicated query builder:
class VarietyQueryBuilder
{
private QueryBuilder $qb;
public function create(): self;
public function withNameFilter(?string $name): self;
public function withRegionFilter(?int $regionId): self;
public function withProcessingFilter(?array $methods): self;
public function withPagination(int $page, int $limit): self;
public function withSorting(string $field, string $direction): self;
public function build(): Query;
public function count(): int;
}
Step 3: Extract Filter Specifications¶
Use Specification pattern for complex filters:
interface VarietySpecification
{
public function apply(QueryBuilder $qb): void;
}
class NameSpecification implements VarietySpecification { ... }
class RegionSpecification implements VarietySpecification { ... }
Step 4: Simplify Repository Method¶
Reduce complexity by using builder:
public function findWithFiltersAndPagination(
VarietyFilterCriteria $criteria
): PaginatedResult {
$builder = $this->createQueryBuilder()
->withFilters($criteria)
->withPagination($criteria->page, $criteria->limit)
->withSorting($criteria->sortBy, $criteria->direction);
return new PaginatedResult(
results: $builder->build()->getResult(),
total: $builder->count(),
page: $criteria->page,
limit: $criteria->limit
);
}
Step 5: Create Filter Criteria DTO¶
Encapsulate filter parameters:
class VarietyFilterCriteria
{
public function __construct(
public readonly ?string $name = null,
public readonly ?int $regionId = null,
public readonly ?array $processingMethods = null,
public readonly int $page = 1,
public readonly int $limit = 20,
public readonly string $sortBy = 'name',
public readonly string $direction = 'ASC'
) {}
}
Success Criteria¶
findWithFiltersAndPagination()cyclomatic complexity < 10- NPath complexity < 250
- Method < 30 lines
- Clear separation of query building logic
- Easy to add new filters
- Improved testability
- No regression in query results or performance
- Query performance maintained or improved
Testing Strategy¶
Before Refactoring¶
- Add tests for all current filter combinations
- Test pagination edge cases
- Test sorting options
- Benchmark query performance
After Refactoring¶
- Unit test query builder
- Unit test specifications
- Verify integration tests pass
- Benchmark query performance (ensure no regression)
- Test new filter additions (verify ease of extension)
Risk Assessment¶
Medium Risk:
- Repository is core data access layer
- Query changes could affect results
- Performance impact possible
- Many filter combinations to test
Mitigation:
- Comprehensive test coverage before refactoring
- Performance benchmarks before/after
- Test with production-like data volumes
- Verify SQL generated is equivalent
- Use database query profiling
Estimated Effort¶
Medium:
- Analysis & test coverage: 1 day
- Query builder implementation: 1.5 days
- Specification pattern (if used): 1 day
- Repository refactoring: 0.5 day
- Testing & verification: 1 day
- Performance testing: 0.5 day
- Total: 5.5 days
Implementation Order¶
- Add comprehensive tests for current behavior
- Benchmark current query performance
- Create query builder
- Implement filter specifications (optional)
- Refactor repository method
- Verify tests pass
- Benchmark performance
- Document new approach
Alternative Approaches¶
Option A: Query Builder (Recommended)¶
- Fluent interface for query construction
- Easy to test and extend
- Clear separation of concerns
Option B: Specification Pattern¶
- More flexible for complex business rules
- Reusable specifications
- More overhead, may be over-engineering
Option C: Criteria Object Only¶
- Simpler than builder/specification
- Less flexible for future extensions
- May not reduce complexity enough
Recommendation: Start with Query Builder (Option A), add Specifications if needed later.
Dependencies¶
None - can be addressed independently
Related Issues¶
Repository/RegionRepository.php:89also has complexity (CC: 11)- May have similar query building issues
- Can apply same refactoring pattern
- Address after VarietyRepository
Notes¶
- High NPath (1,215) indicates many filter combinations - comprehensive testing critical
- Query builder pattern is well-established in Doctrine/Symfony
- Consider creating base query builder for reuse across repositories
- May reveal opportunities for query optimization
- Could improve API filter response times
- Pattern can be replicated for other repositories (RegionRepository, etc.)