shureban/laravel-object-mapper
Laravel object mapper maps JSON strings, arrays, and FormRequest data into typed PHP objects (DTOs) using reflection. Use inheritance, a trait, or ObjectMapper directly; supports readonly DTOs and static constructors. Works out of the box via auto-discovery.
#[FindModel] attribute; without it ImplicitModelLookupException is thrown.
Set object_mapper.implicit_model_lookup => true to restore the v1 behavior globally.Shureban\LaravelObjectMapper\Attributes\SetterName (internal helper) moved to
Shureban\LaravelObjectMapper\Support\SetterName — the Attributes namespace now holds
real PHP attributes only.#[MapFrom('key')] with dot notation, #[Ignore], #[CastWith(Type::class)],
#[ArrayOf(Item::class, depth: N)], #[DateFormat('d.m.Y')],
#[EnumFallback(Enum::Case)], #[FindModel].new ObjectMapper(User::class) builds the instance via constructor
mapping; MappableTrait::from($data) / fromMany($data); missing required parameters are
reported all at once via MissingConstructorValueException.ObjectMapper::mapArrayOf(User::class, $jsonOrArray).->strict() aggregates unknown keys, lossy coercions, missing required
properties and all conversion errors into one MappingFailedException (getErrors()).toArray()/toJson() on the trait and a standalone Serializer —
reverse name mapping, enum/date/model/collection handling, serialize_snake_case config.MapsFromRequest resolve from the current
request automatically.from($value) factory when
the target class defines one (private constructors supported).Support\ClassMetadata) — properties,
setters and resolved types are computed once per class, not once per mapping.assign_explicit_null — assign explicit nulls to nullable properties (default off).Focus: predictable error handling. Every malformed input now throws a subclass of
Shureban\LaravelObjectMapper\Exceptions\ObjectMapperException instead of a raw PHP
TypeError / ValueError / Error / ArgumentCountError.
InvalidJsonStructureException — mapFromJson() now rejects JSON that decodes to a scalar or null
('null', '123', '"str"', 'true') instead of crashing with a TypeError.InvalidDateTimeValueException — thrown by Carbon/DateTime mapping for empty, non-string and unparseable values
(previously: TypeError on empty values, raw Exception on garbage dates).InvalidEnumValueException — thrown for unknown enum backing values, non-scalar values and pure (non-backed) enums
(previously: raw ValueError / Error).InvalidModelKeyException — thrown when the value for an Eloquent-typed property is not int|string
(previously: TypeError after an accidental Model::find([...])).InvalidValueTypeException — thrown when an array/object arrives for a string/int/float property
(previously: silent garbage like "Array", 1, 0).DateTimeInterface values are accepted for Carbon/DateTime properties.mergeConfigFrom() — the package now works without publishing the config.phpunit.xml, tests/TestCase.php (no host application required), CI workflow.#[Ignore] is respected in constructor mapping: an ignored promoted (or plain) constructor
parameter is never filled from the input data — it falls back to its default value.
The attribute now targets constructor parameters as well as properties.MapsFromRequest controller injection resolves the FormRequest declared in the route action's
signature and maps from its validated() data. Only when the route declares no FormRequest does
the DTO fall back to the raw request->all().ObjectMapper builds a fresh instance per map*() call instead of
returning the first instance (and its data) for every later call.Serializer: object/stdClass-typed properties keep their dynamic properties; nested objects
providing their own toArray() (or JsonSerializable) are delegated to it; Collection
properties keep string keys.[@var](https://github.com/var) Type [] $items (space between the type and []) is recognized as array notation again.settags() setter is found for the tags property, as in v1.#[DateFormat] resets unspecified date fields: a date-only format produces 00:00:00
instead of leaking the current wall-clock time. The attribute also works when the date type
comes from phpDoc only (untyped property with [@var](https://github.com/var) \DateTime).CustomType conversion order keeps the v1 contract: the single-argument constructor wins over
a public static from() factory; from() is used only when its signature fits the value.
Incompatible payloads (private constructor, wrong argument type) throw ObjectMapperException
subclasses instead of leaking raw Error/TypeError. An array value for a class whose public
constructor has required parameters is built via constructor mapping (nested readonly DTOs).null for a present value assigns null to nullable properties
(v1 parity for custom types); non-nullable properties keep their default (Eloquent lookup miss).#[MapFrom('parent.child')] keys fall back to the snake_case form of every segment
(parent_key.child_key), matching the non-dotted lookup rules.#[ArrayOf] mapping; scalar ArrayOf items are
validated against the declared item type; constructor-phase missing values are aggregated into
MappingFailedException (with unknown keys) instead of escaping as
MissingConstructorValueException; a failed conversion no longer produces an extra false
"no value provided" error for the same property.[@var](https://github.com/var) regex contained an accidental BEL-to-'z' character
range, so a standard multiline docblock (* [@var](https://github.com/var) int with no trailing space) captured the type as
"int\n" and crashed the whole mapping. Now supported: multiline [@var](https://github.com/var) int, [@var](https://github.com/var) ?int,
[@var](https://github.com/var) int|null (maps as int), leading-backslash FQCNs (normalized, so [@var](https://github.com/var) \Carbon\Carbon
resolves to the box type). [] in a docblock description no longer changes the array nesting level,
and $word in the description no longer hijacks the source-key name.Illuminate\Support\Carbon-typed properties crashed with TypeError on every valid date —
CarbonType now instantiates Illuminate\Support\Carbon (assignable to both Carbon classes).int|string) crashed the whole mapping even when the data
did not contain that key. Property types are now resolved lazily: UnknownPropertyTypeException
is thrown only when a value for the unsupported property actually arrives.use imports whose class name contains digits (use App\Dto\Item2;) were not resolvable from phpDoc
short names (ClassExtraInformation regex rewritten).int) are accepted for Carbon/DateTime properties (previously: date-parse crash).BoolType now recognizes 'yes', 'on', 'TRUE', 'True', 1.0 as true (previously false).Error: Cannot instantiate abstract class;
now throw InvalidValueTypeException.ObjectMapper::map() returned (not threw) UnknownDataFormatException for unknown data formats.use Str; root alias replaced with Illuminate\Support\Str — the package no longer depends on
the host app's class aliases.int|string) crashed with Error: Call to undefined method getName();
now throw UnknownPropertyTypeException.Error: Call to private method;
non-public setters are now ignored (the value is assigned directly).CustomType: an array value for a class whose constructor has required parameters crashed with
ArgumentCountError; a scalar value for a class without a constructor crashed with TypeError.
Both now throw WrongConstructorParametersNumberException (as the README always claimed).Model::find() returning null crashed with TypeError; the property is now
left untouched.Property::getDefaultValue() replaced falsy defaults (0, '', false) with type defaults (?: → ??).config('app.timezone') missing no longer produces a DateTimeZone(null) deprecation.composer.json: declared the real runtime dependency (illuminate/support), dev dependencies,
"type": "library".ValueError/TypeError around mapping calls should catch
ObjectMapperException (or a specific subclass) instead.string/int/float property now throws instead of silently
producing "Array"/1/0.How can I help you explore Laravel packages today?