Skip to main content

StoreFlowTransitionRequest

Form request class for validating FlowTransition creation data. This request handles complex validation including state relationships, transition types, and uniqueness constraints.

Namespace​

JobMetric\Flow\Http\Requests\FlowTransition\StoreFlowTransitionRequest

Overview​

The StoreFlowTransitionRequest validates incoming data when creating a new FlowTransition entity. It ensures:

  • Required fields are present
  • Flow ID exists and is valid
  • Multi-language translations are valid
  • State relationships are valid (from/to states)
  • Transition uniqueness within flow
  • Business rules are enforced (START state rules, terminal state rules, etc.)

Validation Rules​

Required Fields​

FieldRuleDescription
flow_idrequired|integer|exists:flows,idID of the parent flow
translationrequired|arrayMulti-language translation data
translation.{locale}required|arrayTranslation data for each active locale
translation.{locale}.namerequired|stringTransition name in each locale (must be unique within flow)

Optional Fields​

FieldRuleDescription
fromnullable|integer|exists:flow_states,idSource state ID (nullable for generic input)
tonullable|integer|exists:flow_states,idTarget state ID (nullable for generic output)
slugnullable|string|max:255|regex:/^[a-z0-9-]+$/|unique:flow_transitions,slugURL-friendly identifier

Transition Types​

The request supports four transition types:

1. Specific Transition (from → to)​

Both from and to are specified:

'from' => 1, // State ID
'to' => 2, // State ID

2. Self-Loop Transition (from → from)​

Same state for both from and to:

'from' => 1,
'to' => 1, // Same state

Note: Not allowed for START states.

3. Generic Input Transition (null → to)​

No source state, can enter from any state:

'from' => null,
'to' => 2,

4. Generic Output Transition (from → null)​

No target state, can exit to any state:

'from' => 1,
'to' => null,

Note: Not allowed from terminal states.

Cross-Field Validation​

The request includes extensive custom validation in withValidator():

At Least One Required​

At least one of from or to must be set:

// ✅ Valid
'from' => 1, 'to' => 2,
'from' => null, 'to' => 2,
'from' => 1, 'to' => null,

// ❌ Invalid
'from' => null, 'to' => null,

START State Rules​

  1. Cannot Self-Loop: START state cannot have self-loop transitions

    // ❌ Invalid if from is START state
    'from' => $startId,
    'to' => $startId,
  2. Cannot Point To: No transition can point to START state

    // ❌ Invalid
    'to' => $startId,
  3. Only One Exit: Only one transition can exit from START state

    // If START already has a transition, cannot create another
  4. First Transition: First transition in flow must start from START

    // If this is the first transition, from must be START

Terminal State Rules​

Terminal states cannot have generic output transitions:

// ❌ Invalid if from is terminal state
'from' => $terminalStateId,
'to' => null,

Uniqueness Rules​

The combination (flow_id, from, to) must be unique:

// ❌ Invalid - Duplicate transition
// If flow 1 already has: from=1, to=2
// Cannot create another: from=1, to=2

Translation Validation​

Name Uniqueness​

The translation.{locale}.name field is validated for uniqueness within the same flow:

  • Checks if a transition with the same name already exists in the same flow and locale
  • Prevents duplicate transition names within a flow
  • Trims whitespace before validation

Usage Examples​

Specific Transition​

$requestData = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Approve Order'],
],
'from' => 1, // Pending state
'to' => 2, // Approved state
];

Generic Input Transition​

$requestData = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Enter Processing'],
],
'from' => null, // Can enter from any state
'to' => 3, // Processing state
];

Generic Output Transition​

$requestData = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Cancel Order'],
],
'from' => 1, // Pending state
'to' => null, // Can exit to any state
];

Self-Loop Transition​

$requestData = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Refresh Status'],
],
'from' => 2, // Processing state
'to' => 2, // Same state (self-loop)
];

With Slug​

$requestData = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Approve Order'],
],
'from' => 1,
'to' => 2,
'slug' => 'approve-order', // URL-friendly identifier
];

Context Management​

The request supports external context injection:

$request = new StoreFlowTransitionRequest();
$request->setContext(['flow_id' => 123]);

Static Rules Method​

rulesFor()​

Provides programmatic validation:

$input = [
'flow_id' => 1,
'translation' => [
'en' => ['name' => 'Transition'],
],
'from' => 1,
'to' => 2,
];

$context = ['flow_id' => 1];

$rules = StoreFlowTransitionRequest::rulesFor($input, $context);

Error Handling​

Validation Errors​

{
"message": "The given data was invalid.",
"errors": {
"flow_id": [
"The selected flow id is invalid."
],
"from": [
"The first transition must start from START state."
],
"to": [
"Terminal states cannot have generic output transitions."
]
}
}

Best Practices​

  1. Understand Transition Types: Choose the right type for your use case

    // Specific: Most common, explicit state changes
    'from' => 1, 'to' => 2,

    // Generic Input: Flexible entry point
    'from' => null, 'to' => 2,

    // Generic Output: Flexible exit point
    'from' => 1, 'to' => null,
  2. Follow START State Rules: Always start from START state for first transition

    // ✅ Good - First transition
    'from' => $startStateId, 'to' => 2,
  3. Use Meaningful Slugs: Create URL-friendly slugs for API access

    // ✅ Good
    'slug' => 'approve-order'

    // ❌ Bad
    'slug' => 'transition_1'
  4. Respect Terminal States: Don't create generic outputs from terminal states

    // ❌ Invalid
    'from' => $terminalStateId, 'to' => null,