| name | State Comparison Patterns |
| description | Model helper methods for clean state checking instead of verbose Spatie Model States comparisons (63% code reduction) |
| allowed-tools | Read, Write, Edit, Grep, Glob |
State Comparison Patterns
Use clean helper methods instead of verbose Spatie Model States comparisons.
When to Use
- Checking submission or card states in application code
- Writing conditional logic based on states
- Querying database for specific states
- Refactoring verbose state comparisons
The Problem
❌ Verbose Spatie Pattern (Don't Use)
// 63% more code than necessary!
(new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)
(new ($submission->state)($submission))->equals(SubmissionState::COMPLETED)
Issues:
- Verbose and hard to read
- Repeated boilerplate
- Error-prone (easy to make typos)
- Difficult to maintain
✅ Clean Helper Methods (Always Use)
// DRY, SOLID, KISS principles
$card->isCardQualityCheck()
$submission->isCompleted()
Benefits:
- 63% code reduction
- Improved readability
- Type safety
- Single source of truth
- Zero duplication
Model Helper Methods
SubmissionTradingCard Model
Location: app/Models/SubmissionTradingCard.php
State Check Methods
// Check specific state
$card->isCardState(CardState::ASSESSMENT) // true/false
$card->hasCardStateIn([CardState::ASSESSMENT, CardState::IN_PROGRESS]) // true/false
// Convenience methods for each state
$card->isCardReceived() // CardState::RECEIVED
$card->isCardAssessment() // CardState::ASSESSMENT
$card->isCardInProgress() // CardState::IN_PROGRESS
$card->isCardQualityCheck() // CardState::QUALITY_CHECK
$card->isCardLabelSlab() // CardState::LABEL_SLAB
$card->isCardCompleted() // CardState::COMPLETED
$card->isCardCancelled() // CardState::CANCELLED
Query Scopes
// Filter by single state
SubmissionTradingCard::whereCardState(CardState::ASSESSMENT)->get();
// Filter by multiple states
SubmissionTradingCard::whereCardStateIn([
CardState::ASSESSMENT,
CardState::IN_PROGRESS,
])->get();
Submission Model
Location: app/Models/Submission.php
State Check Methods
// Check specific state
$submission->isSubmissionState(SubmissionState::COMPLETED) // true/false
$submission->hasSubmissionStateIn([SubmissionState::COMPLETED, SubmissionState::SHIPPED]) // true/false
// Convenience methods for each state
$submission->isDraft() // SubmissionState::DRAFT
$submission->isSubmitted() // SubmissionState::SUBMITTED
$submission->isReceived() // SubmissionState::RECEIVED
$submission->isAssessment() // SubmissionState::ASSESSMENT
$submission->isCompleted() // SubmissionState::COMPLETED
$submission->isShipped() // SubmissionState::SHIPPED
$submission->isCancelled() // SubmissionState::CANCELLED
// Terminal state check (completed, shipped, or cancelled)
$submission->isTerminalState()
Query Scopes
// Filter by single state
Submission::whereSubmissionState(SubmissionState::COMPLETED)->get();
// Filter by multiple states
Submission::whereSubmissionStateIn([
SubmissionState::COMPLETED,
SubmissionState::SHIPPED,
SubmissionState::CANCELLED,
])->get();
Common Usage Patterns
Single State Check
// ❌ WRONG: Verbose pattern
if ((new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)) {
// ...
}
// ✅ CORRECT: Clean helper
if ($card->isCardQualityCheck()) {
// ...
}
Multiple States Check
// ❌ WRONG: Repeated verbose checks
if ((new ($submission->state)($submission))->equals(SubmissionState::COMPLETED) ||
(new ($submission->state)($submission))->equals(SubmissionState::SHIPPED) ||
(new ($submission->state)($submission))->equals(SubmissionState::CANCELLED)) {
// ...
}
// ✅ CORRECT: Array syntax
if ($submission->hasSubmissionStateIn([
SubmissionState::COMPLETED,
SubmissionState::SHIPPED,
SubmissionState::CANCELLED,
])) {
// ...
}
// ✅ EVEN BETTER: Terminal state helper
if ($submission->isTerminalState()) {
// ...
}
Database Queries
// ❌ WRONG: Manual state filtering
$cards = SubmissionTradingCard::all()->filter(function ($card) {
return (new ($card->card_state)($card))->equals(CardState::ASSESSMENT);
});
// ✅ CORRECT: Query scope
$cards = SubmissionTradingCard::whereCardState(CardState::ASSESSMENT)->get();
// ✅ CORRECT: Multiple states
$cards = SubmissionTradingCard::whereCardStateIn([
CardState::ASSESSMENT,
CardState::IN_PROGRESS,
])->get();
Conditional Logic
// ❌ WRONG: Nested ternaries with verbose checks
$status = (new ($card->card_state)($card))->equals(CardState::COMPLETED)
? 'done'
: ((new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK) ? 'reviewing' : 'processing');
// ✅ CORRECT: Match expression with helpers
$status = match (true) {
$card->isCardCompleted() => 'done',
$card->isCardQualityCheck() => 'reviewing',
default => 'processing',
};
View Logic
<!-- ❌ WRONG: Verbose Blade syntax -->
@if((new ($submission->state)($submission))->equals(SubmissionState::COMPLETED))
<span class="badge-success">Completed</span>
@endif
<!-- ✅ CORRECT: Clean helper -->
@if($submission->isCompleted())
<span class="badge-success">Completed</span>
@endif
Implementation
Helper Method Pattern
Generic Check:
public function isCardState(string $state): bool
{
return $this->card_state === $state;
}
public function hasCardStateIn(array $states): bool
{
return in_array($this->card_state, $states, true);
}
Convenience Methods:
public function isCardQualityCheck(): bool
{
return $this->isCardState(CardState::QUALITY_CHECK);
}
Query Scopes:
public function scopeWhereCardState($query, string $state)
{
return $query->where('card_state', $state);
}
public function scopeWhereCardStateIn($query, array $states)
{
return $query->whereIn('card_state', $states);
}
Refactoring Guide
Step 1: Find Verbose Patterns
# Search for verbose Spatie comparisons
./vendor/bin/sail grep -r "new (\$.*->.*state)" app/
./vendor/bin/sail grep -r "->equals(.*State::" app/
Step 2: Replace with Helpers
Before:
if ((new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)) {
// Process quality check
}
After:
if ($card->isCardQualityCheck()) {
// Process quality check
}
Step 3: Test
# Run tests to verify behavior unchanged
./scripts/dev.sh test
Benefits
Code Reduction
Before (100 lines):
if ((new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)) {
// Line 1
}
if ((new ($submission->state)($submission))->equals(SubmissionState::COMPLETED)) {
// Line 2
}
// ... 98 more lines with verbose patterns
After (37 lines):
if ($card->isCardQualityCheck()) {
// Line 1
}
if ($submission->isCompleted()) {
// Line 2
}
// ... 35 more lines with clean helpers
Savings: 63% code reduction
Readability
Verbose: (new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)
Clean: $card->isCardQualityCheck()
Developer Experience: 10x more readable, self-documenting
Type Safety
Helper methods provide IDE autocomplete:
$card->is // ← IDE suggests:
// isCardReceived()
// isCardAssessment()
// isCardInProgress()
// isCardQualityCheck()
// ... etc.
Single Source of Truth
Change state comparison logic in ONE place:
// If state comparison logic changes, update helper only
public function isCardQualityCheck(): bool
{
// Could add additional conditions here
return $this->isCardState(CardState::QUALITY_CHECK)
&& $this->quality_check_passed === null;
}
All usages automatically updated!
Common Pitfalls
❌ WRONG: Still using verbose pattern
if ((new ($card->card_state)($card))->equals(CardState::QUALITY_CHECK)) {
// This defeats the purpose of helper methods!
}
❌ WRONG: Manual state string comparison
if ($submission->state === 'completed') {
// Don't use magic strings!
}
❌ WRONG: Inconsistent naming
// Don't create your own naming convention
if ($card->isQC()) { // Abbreviation unclear
// Use isCardQualityCheck() instead
}
Documentation Links
- Model Helpers Guide:
docs/features/state-machine/MODEL-HELPERS.md - State Machine Guide:
docs/features/state-machine/ - Spatie Model States: https://github.com/spatie/laravel-model-states
- Service Architecture:
docs/SERVICE-ARCHITECTURE.md