MultiTranslationTypeObjectRequest Trait
The MultiTranslationTypeObjectRequest trait builds validation rules and human-friendly attribute labels for multi-locale translation payloads under the root key translation. It is intended for projects that define translatable fields via a Typeify schema (JobMetric\Translation\Typeify\Translation) and expose available locales through Language::all().
When to Use MultiTranslationTypeObjectRequest
Use MultiTranslationTypeObjectRequest when you need:
- Schema-driven multi-locale validation: Define translation fields using Typeify schemas for all active locales
- Automatic locale coverage: Automatically validates translations for all locales from
Language::all() - Custom field validation: Each translatable field can have its own validation rules per locale
- Primary field uniqueness: Enforces uniqueness for a primary field across all locales
- Custom field uniqueness: Mark specific fields as unique per locale with automatic rule generation
- Complex validation requirements: Define custom validation strings for each field in the schema
- Structured multi-locale forms: Forms that require translations in all active locales with schema-driven validation
Example scenarios:
- Complex e-commerce product forms requiring translations for all active languages with custom validation
- CMS content management with structured field definitions and mandatory multi-locale translations
- Multi-language blog post creation with schema-driven validation for all supported languages
- Content forms where all locales must be provided with custom validation rules per field
- Applications using Typeify schemas for multi-locale form generation and validation
This document focuses solely on request-side validation and labels. No database or tests are discussed here. All examples include expected output you can compare against during development.
What It Does
For each locale (from Language::all()), the trait produces:
- A container rule for
translation.{locale}(array) - A primary field rule (e.g.,
nameortitle) consisting of:stringTranslationFieldExistRule(per-locale uniqueness for that field)
- Rules for additional fields derived from your Typeify schema:
- If a field is marked unique (via
customField->params['unique'] = true), it receives both its custom validation string and aTranslationFieldExistRuleinstance. - Otherwise, it receives its custom validation string (defaulting to
string|nullable|sometimeswhen not provided).
- If a field is marked unique (via
It can also build user-friendly attribute labels for your validator messages, mapping each translation.{locale}.{field} key to a readable label defined in the Typeify schema (via customField->label).
Quick Start
1) Use the trait inside a FormRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Collection;
use JobMetric\Translation\Http\Requests\MultiTranslationTypeObjectRequest;
use JobMetric\Translation\Typeify\Translation as TypeTranslation;
class StorePostRequest extends FormRequest
{
use MultiTranslationTypeObjectRequest;
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$rules = [
'slug' => 'required|string',
];
// Build your Typeify translation schema collection.
// Typically you already have these objects; here we craft them inline for illustration.
/** @var Collection<int, TypeTranslation> $schema */
$schema = collect([
// Primary field will be provided separately via $field_name.
// Below are *additional* fields.
new TypeTranslation([
'customField' => (object) [
'params' => ['uniqName' => 'summary', 'unique' => false],
'validation' => 'string|nullable|max:500',
'label' => 'validation.attributes.summary',
]
]),
new TypeTranslation([
'customField' => (object) [
'params' => ['uniqName' => 'slug', 'unique' => true],
'validation' => 'string|required|min:3',
'label' => 'validation.attributes.slug',
]
]),
]);
// IMPORTANT: method name is `renderMultiTranslationFiled` (with a trailing "d").
$this->renderMultiTranslationFiled(
$rules,
$schema, // Typeify items defining fields
\App\Models\Post::class, // owner model FQCN
'title', // primary field per locale (uniqueness enforced)
null, // object_id (null on create)
null, // parent_id (optional scope)
[] // parent_where (optional extra constraints)
);
return $rules;
}
public function attributes(): array
{
$attributes = [
'slug' => 'Slug',
];
// Build attribute labels per-locale using the same Typeify schema:
$schema = collect([
new TypeTranslation([
'customField' => (object) [
'params' => ['uniqName' => 'summary'],
'validation' => 'string|nullable|max:500',
'label' => 'validation.attributes.summary',
]
]),
new TypeTranslation([
'customField' => (object) [
'params' => ['uniqName' => 'slug'],
'validation' => 'string|required|min:3',
'label' => 'validation.attributes.slug',
]
]),
]);
$this->renderMultiTranslationAttribute($attributes, $schema);
return $attributes;
}
}
Note: Keep the exact method name
renderMultiTranslationFiledas implemented in the trait.
Example: Resulting rules() Output
Assumptions for this example:
Language::all()yields locales:en,fa- The primary field is
title(uniqueness per locale) - The Typeify schema defines two additional fields:
summary→ not unique, validation:string|nullable|max:500slug→ unique, validation:string|required|min:3
Code (excerpt from rules()):
$rules = [
'slug' => 'required|string',
];
$this->renderMultiTranslationFiled(
$rules,
$schema,
\App\Models\Post::class,
'title',
null,
null,
[]
);
return $rules;
Expected Output (pretty-printed):
Array
(
[slug] => required|string
[translation] => array
[translation.en] => array
[translation.en.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.en.summary] => string|nullable|max:500
[translation.en.slug] => Array
(
[0] => string|required|min:3
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.fa] => array
[translation.fa.title] => Array
(
[0] => string
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
[translation.fa.summary] => string|nullable|max:500
[translation.fa.slug] => Array
(
[0] => string|required|min:3
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
)
Example: Resulting attributes() Output
Using the same schema and locales, renderMultiTranslationAttribute maps each field to a label (via customField->label if provided), per locale.
Code (excerpt from attributes()):
$attributes = [
'slug' => 'Slug',
];
$this->renderMultiTranslationAttribute($attributes, $schema);
return $attributes;
Expected Output (pretty-printed):
Array
(
[slug] => Slug
[translation.en.summary] => validation.attributes.summary
[translation.en.slug] => validation.attributes.slug
[translation.fa.summary] => validation.attributes.summary
[translation.fa.slug] => validation.attributes.slug
)
If a
labelis not provided for a field, the trait uses the field'suniqNameas the fallback label text.
Handling the Primary Field
The primary field ($field_name, e.g., title) is always added with:
stringTranslationFieldExistRulefor per-locale uniqueness
You do not need to include the primary field in your Typeify schema list; the trait adds it explicitly for every locale.
Adding/Marking Unique Custom Fields
To enforce uniqueness on additional fields, set customField->params['unique'] = true in your Typeify item. The trait will attach a TranslationFieldExistRule to that field, per locale, alongside the provided validation string.
Example Typeify entry:
new TypeTranslation([
'customField' => (object) [
'params' => ['uniqName' => 'slug', 'unique' => true],
'validation' => 'string|required|min:3',
'label' => 'validation.attributes.slug',
]
])
Expected part of rules():
[translation.en.slug] => Array
(
[0] => string|required|min:3
[1] => JobMetric\Translation\Rules\TranslationFieldExistRule Object (...)
)
End-to-End Flow (Minimal)
- Build a
Collection<TypeTranslation>where each item provides:customField->params['uniqName'](required)- optional
customField->params['unique'](bool) - optional
validation(string, defaults tostring|nullable|sometimes) - optional
label(used to build attributes)
- Call
renderMultiTranslationFiled($rules, $schema, $className, $primaryField, ...)inrules(). - Call
renderMultiTranslationAttribute($attributes, $schema)inattributes(). - Done: your validator now understands
translation.{locale}.{field}for each locale, including the primary field with uniqueness and any extra custom fields with per-field validation.
Summary
MultiTranslationTypeObjectRequest makes multi-locale validation schema-driven:
- Locale-aware rules under
translation.{locale} - Per-locale uniqueness for a primary field
- Custom per-field validation from Typeify (with optional uniqueness)
- Clean, predictable attribute labels for error messages
Use the examples above as a template in your FormRequest classes to validate complex translation payloads consistently across locales.
Related Documentation
- HasTranslation - Core trait for multilingual models
- TranslationFieldExistRule - Validation rule for unique translations
- TranslationArrayRequest - Simpler array-based validation
- MultiTranslationArrayRequest - Multi-locale array validation
- TranslationTypeObjectRequest - Single or dynamic locale Typeify validation
- TranslationResource - JSON resource for API responses
- TranslationCollectionResource - JSON resource for translation collections