Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Object Transformer Bundle Laravel Package

it-bens/object-transformer-bundle

Symfony bundle integrating it-bens/object-transformer, letting you map and transform objects through configurable transformers. Define transformations as services and reuse them across your app to keep DTO mapping, conversions, and data shaping clean and decoupled.

View on GitHub
Deep Wiki
Context7

Getting Started

it-bens/object-transformer is a framework‑agnostic, attribute‑driven object‑to‑object mapper: you annotate the source and target classes, and it copies/mutates properties for you. It replaces hand‑written UserResource::from($user) style boilerplate without dragging in a full mapper.

composer require it-bens/object-transformer

Nothing to publish, no config file, and (as of 0.2.0) no Laravel service provider or auto‑discovery — that is the first thing to know. You instantiate it yourself or bind it in a provider.

Where to look first: the README's "Getting started" example and the Attributes\ namespace. Skim the attribute list before writing anything — the package is entirely driven by which attributes you put where, and 90% of confusion is "why didn't this property get mapped".

First use case — validated request → DTO → model. The highest-value first step in a Laravel app is a read-only output DTO for an API endpoint:

use ItBens\ObjectTransformer\TransformableObject as Transformable;
use ItBens\ObjectTransformer\TransformedObject as Transformed;

class UserDto
{
    public Transformable $id;
    public Transformable $email;
    public Transformable $name;
}

$dto = (new ObjectTransformer())->transform($user, UserDto::class);

If you only need "map these three fields into a readonly DTO", you are done in five minutes. Everything below is about scaling that up.

Implementation Patterns

Symmetric naming is the default contract. Mark source properties as transformable and target properties as transformed; properties with matching names map automatically. Keep names aligned and the mapper stays attribute-free.

class EloquentUser
{
    public Transformable $id;
    public Transformable $email;
}

class UserDto
{
    public Transformed $id;
    public Transformed $email;
}

Renames and omissions. Use the property‑name attribute on the target side when the shape differs (full_name → name), and simply omit a target property (or mark it ignored) rather than fighting the mapper with nulls. Be explicit — a property you forgot to annotate is silently skipped, not an error.

Custom property transformers are the extension point for anything the mapper cannot infer: Carbon → ISO string, enum → scalar, cents → formatted money, model → ID. Implement the package's PropertyTransformer contract and attach it to the target property with #[Transformer(...)]. Write these as tiny, single-purpose, unit-testable classes — they are the only part of your mapping that is genuinely your code.

class IsoDateTransformer implements PropertyTransformer
{
    public function transform(mixed $value): string
    {
        return $value->toIso8601String();
    }
}

Nested objects compose. If a target property is typed as another annotated DTO, the mapper recurses. Build a UserDto → AddressDto → CountryDto tree of small classes rather than one flat 40‑property DTO; this is the pattern that keeps the attribute graph readable.

Collections: map at the edge. For lists, array_map/->map() over loaded models into the DTO and return the array. Do not try to have the transformer walk an Eloquent Collection — it is not a Laravel package and does not know about Collection, LengthAwarePaginator, or Resource.

Laravel glue worth writing once (in the app, not the package):

// AppServiceProvider::register()
$this->app->singleton(ObjectTransformer::class);

// app/Support/TransformsTo.php
trait TransformsTo
{
    public function to(string $class): object
    {
        return app(ObjectTransformer::class)->transform($this, $class);
    }
}

// App\Models\User
class User extends Model { use TransformsTo; }

// Controller
return UserDto::collection(User::query()->paginate()->getCollection()->map->to(UserDto::class));

Where it fits vs. Laravel's own tools:

  • JsonResource — still better for conditional fields (whenLoaded, when()). Use object-transformer for DTOs that feed into resources, jobs, events, or service boundaries.
  • FormRequest — good fit: validated() array → input DTO → Model::create($dto->toArray()), giving you a typed boundary between HTTP and domain.
  • Eloquent casts / accessors — keep those on the model; the transformer should not be re-implementing business logic.
  • Nested eager loading is still your job — call ->load('address.country') before transforming.

Gotchas and Tips

0.2.0 is a pre‑1.0 release. Under semver's 0.x rules, minor bumps may contain breaking changes. Pin it ("it-bens/object-transformer": "0.2.*"), read the diff before any bump, and keep every call site behind the three lines in TransformsTo so an upgrade is a one-file change.

Silent skips are the #1 debugging trap. A property that isn't mapped throws nothing — you get null or an Error/TypeError on an uninitialized typed property much later. When something is missing, first verify (a) the attribute is on the correct side, (b) the names match or are explicitly mapped, (c) the target type is compatible. Reduce to a two-property repro before opening an issue.

Typed properties must be initialized. Because Laravel models are untyped-ish bags of attributes while DTOs are strictly typed, mismatches (a missing column, a nullable DB column hitting a non-nullable string) surface as TypeError, not as a friendly mapping error. Make DTO properties nullable where the column is nullable, and validate upstream.

Dates do not convert themselves. Carbon, enums, backed enums, and money values all need an explicit PropertyTransformer. Expect to write three or four of these on day one; they become your reusable "scalar adapters" library.

Do not hand it an Eloquent model with lazy relationships. The mapper only touches what you marked; if the relation isn't loaded it will either null out or lazy-load (N+1) or fail in preventLazyLoading() apps. Load first, transform second, and never transform inside a loop over a collection without eager loading.

Circular and deep graphs will bite. User → Team → Members → Team recursion has no depth guard you control from the attribute layer. Transform down into DTOs only — never annotate a bidirectional relationship on both sides.

No container injection into transformers. Because there's no service provider, a PropertyTransformer is instantiated by the package, not the container, so __construct dependencies (repositories, Config, NumberFormatter) won't resolve. Keep property transformers pure and dependency-free, or resolve dependencies inside the class via app() — the latter is a smell but pragmatic.

Performance: reflection-based mapping per call. Fine for one DTO in a controller; not for a 20k-row export. Bulk paths should use toArray() + DB::insert/chunk and reserve the transformer for the API surface.

Testing strategy: unit-test each PropertyTransformer in isolation, and snapshot one representative source→target mapping per DTO so an upgrade to 0.3.0 fails loudly in CI rather than silently in production.

Escape hatch. If the attribute graph for a class grows past ~10 attributes, stop and write a plain factory method (UserDto::fromModel()). The package pays off on simple, uniform shapes; it becomes a liability on conditional mapping — and JsonResource already solves conditionals better.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core