scafera/file
Scafera File is a Laravel package for handling file uploads and storage with a simple, consistent API. It helps manage validation, disk configuration, and file lifecycle tasks so you can add reliable file management to your app quickly.
Install the package with Composer and publish any config if the README instructs it:
composer require vendor/package
php artisan vendor:publish --tag=package-config
Start with the README introduction block added in v1.0.1, then locate the list of stable rule IDs. The rule IDs are the main integration surface: they let you refer to a validator without depending on its PHP class name or inline validation string.
First use case: add one package rule to a form request and surface its stable ID in your API error response.
public function rules(): array
{
return [
'email' => ['required', 'email', new PackageEmailRule()],
];
}
Then map the rule ID to an application error code in a config file or response transformer. Do not start by replacing every existing validation rule; adopt stable IDs where you need stable contracts: API clients, frontend error handling, translations, and tests.
Form requests as the default integration point. Use the package rule objects directly in rules(). Keep the stable rule ID nearby in config or a dedicated registry so the form request does not become the only place the ID is known.
'email' => ['required', new PackageEmailRule()],
Central rule ID registry. Maintain a config map from rule ID to message key, HTTP status, and public error code:
return [
'package.email.invalid' => [
'message' => 'validation.email.invalid',
'code' => 'EMAIL_INVALID',
'status' => 422,
],
];
This is where the v1.0.1 stable IDs pay off. Your API contract becomes the rule ID, not the exception message.
API error responses. Customize Laravel’s validation exception handling so each failed rule includes its stable ID:
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'errors' => collect($validator->errors()->messages())
->map(fn ($messages, $field) => [
'field' => $field,
'messages' => $messages,
// attach rule IDs from your registry or package metadata
]),
], 422));
}
If the package exposes rule IDs through validator metadata, use that directly. If not, derive them from your own field-to-rule registry. Do not parse translated messages.
Config-driven rule sets. For endpoints with different validation profiles, enable or disable rules by stable ID:
'rules' => [
'signup' => ['package.email.invalid', 'package.password.weak'],
],
Then resolve IDs to rule instances through a small factory. This keeps feature flags and plan-based validation out of controllers.
Testing. Use stable IDs in data providers and assertions:
yield 'invalid email' => ['package.email.invalid', ['email' => 'not-an-email']];
Assert both the field error and the public error code. This protects you when the package changes messages or internal class names.
Translations. Use the rule ID as the translation key suffix:
// lang/en/validation.php
'package.email.invalid' => 'The email address is not valid.',
This is cleaner than tying translations to package class names.
Composition with Laravel rules. Stable IDs do not replace Laravel’s rule composition. Continue using bail, sometimes, required_if, and Rule objects. Treat the package rule as one entry in the chain.
package.length.min may not capture the configured minimum. Include parameters in your error payload if clients need to render dynamic messages.failedValidation() or the exception handler. Test the actual JSON shape.$validator->errors()->keys() gives fields. Check $validator->failed() for rule metadata, but verify the package actually exposes IDs there; the release notes only say IDs were added to validators, not to the failure array.php artisan config:cache during deployment. A stale config cache can silently drop new rule IDs.package.email.invalid.$validator->errors()->toArray() for messages, $validator->failed() for failed rules, and your registry for the public ID. If an ID is missing from an API response, the gap is almost always in your exception formatting, not the package.How can I help you explore Laravel packages today?