validate($attribute, $value, $fail); return; } if (! array_key_exists('image', $value)) { $fail('The :attribute.image field is required.'); return; } $valid = true; (new ImageOrBase64Rule)->validate( "{$attribute}.image", $value['image'], function (string $message) use ($fail, &$valid): void { $valid = false; $fail($message); } ); if (! $valid) { return; } if (isset($value['crops']) && is_array($value['crops'])) { foreach (['desktop', 'mobile'] as $variant) { $crop = $value['crops'][$variant] ?? null; if (! is_array($crop)) { $fail("The :attribute.crops.{$variant} field must be an object."); continue; } $this->validateRange("{$attribute}.crops.{$variant}", 'crop_horizontal', $crop, $fail); $this->validateRange("{$attribute}.crops.{$variant}", 'crop_vertical', $crop, $fail); } return; } // Backwards compatibility with the original single-crop payload. $this->validateRange($attribute, 'crop_horizontal', $value, $fail); $this->validateRange($attribute, 'crop_vertical', $value, $fail); } private function validateRange(string $attribute, string $axis, array $value, Closure $fail): void { $range = $value[$axis] ?? null; if (! is_array($range)) { $fail("The :attribute.{$axis} field must be an object."); return; } $start = $range['start_percentage'] ?? null; $end = $range['end_percentage'] ?? null; if ( ! is_numeric($start) || ! is_numeric($end) || (float) $start < 0 || (float) $end > 100 || (float) $start >= (float) $end ) { $fail( "The :attribute.{$axis} field must satisfy " .'0 <= start_percentage < end_percentage <= 100.' ); } } }