> For the complete documentation index, see [llms.txt](https://docs.opensoutheners.com/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opensoutheners.com/home/laravel-data-mapper/data-objects.md).

# Data objects

Mapping arrays or JSON strings into structured classes.

As saw on the [Usage page](/home/laravel-data-mapper/getting-started/usage.md) this package can map values to different types of data objects, but isn't limited to just mapping single values.

Mapping objects can sometimes become complex in special situations, this package covers some of them referenced here.

## Mapping

Every feature described in the [Usage page](/home/laravel-data-mapper/getting-started/usage.md#mappers) is still available on every property of a object.

In case we want to use the `through` method here on an object class we can use [PHP Generics](https://phpstan.org/blog/generics-by-examples) as the following:

```php
use Illuminate\Support\Collection;

enum UserRole: int
{
    case Admin = 1;
    
    case Editor = 2;
    
    case Moderator = 3;
}

class UserObject
{
    /**
     * @param Collection<UserRole> $roles
     */
    public function __construct(public string $email, public Collection $roles) {}
}

map(['email' => 'taylor@example.com', 'roles' => '1,2'])->to(UserObject::class);
```

Extending the example from above even further:

```php
use App\Data\UserObject;
use Illuminate\Support\Collection;

class CreateTeamWithUsersData
{
    /**
     * @param Collection<UserObject> $users
     */
    public function __construct(public string $name, public Collection $users) {}
}

map([
    'name' => 'Developers',
    'users' => [
        ['email' => 'taylor@example.com', 'roles' => '1,2'],
        ['email' => 'ruben@example.com', 'roles' => '3'],
    ],
])->to(CreateTeamWithUsersData::class);
```

## Property attributes

### Inject

Injects anything that is accessible through the [Laravel Container](https://laravel.com/docs/container).

```php
use App\Data\UserObject;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\Inject;

class CreateTeamWithUsersData
{
    /**
     * @param Collection<UserObject> $users
     */
    public function __construct(
        public string $name,
        public Collection $users,
        #[Inject('env')]
        public string $env,
    ) {}
}
```

### Authenticated

{% hint style="info" %}
This is an extension of Laravel's built-in [Contextual attribute binding](https://laravel.com/docs/container#contextual-attributes) cause the built-in attributes doesn't work with promoted parameters.
{% endhint %}

This acts as the Inject attribute but adding the current authenticated user.

```php
use App\Data\UserObject;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\Authenticated;

class CreateTeamWithUsersData
{
    /**
     * @param Collection<UserObject> $users
     */
    public function __construct(
        public string $name,
        public Collection $users,
        #[Authenticated]
        public ?User $currentUser = null,
    ) {}
}
```

### Model with

Map model instances eager loading the specified relationships.

```php
use App\Models\User;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\ModelWith;

class CreateTeamAddingExistingUsersData
{
    /**
     * @param Collection<User> $users
     */
    public function __construct(
        public string $name,
        #[ModelWith(['teams', 'ownedTeams'])]
        public Collection $users,
    ) {}
}
```

### Resolve model

Map model instance resolving it from route param and/or property key in case there are multiple types of models accepted (using [custom polymorphic names](https://laravel.com/docs/eloquent-relationships#custom-polymorphic-types), full class otherwise):

```php
use App\Models\User;
use App\Models\Team;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\ResolveModel;

class CreateArticleWithOwner
{
    public function __construct(
        public string $name,
        public string $content,
        #[ResolveModel(morphTypeFrom: 'owner_type')]
        public User|Team $owner,
        public string $ownerType,
    ) {}
}
```

## Class attributes

### As type

This is used for TypeScript code generation which change the type name when the TypeScript code is generated:

```php
use OpenSoutheners\LaravelDataMapper\Attributes\AsType;

#[AsType('CreateUserForm')]
class UserObject
{
    /**
     * @param Collection<UserRole> $roles
     */
    public function __construct(public string $email, public Collection $roles) {}
}
```

This can be used anywhere in any class that is exportable to TypeScript code.

### Normalise properties

By default properties are being normalised so `user_id` becomes `user` or `user_role` to `userRole` although this seems pretty opinionated it improves the readability of our objects.

{% hint style="info" %}
This can be also configured globally (also disabled) through the package config file that may be published following the [Quickstart](/home/laravel-data-mapper/readme.md#publish-config-file) page guide.
{% endhint %}

So in case we disabled normalise properties globally we could enable selectively using the attribute:

```php
use App\Models\User;
use App\Models\Team;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\ResolveModel;
use OpenSoutheners\LaravelDataMapper\Attributes\NormaliseProperties;

#[NormaliseProperties]
class CreateArticleWithOwner
{
    public function __construct(
        public string $name,
        public string $content,
        #[ResolveModel(morphTypeFrom: 'owner_type')]
        public User|Team $owner,
        public string $ownerType,
    ) {}
}
```

## Usage in controllers

Mapping these data directly to objects in PHP is very useful but this package also has some functionality so these objects can be directly used in our Laravel application controllers:

```php
use App\Models\User;
use App\Models\Team;
use Illuminate\Support\Collection;
use OpenSoutheners\LaravelDataMapper\Attributes\ResolveModel;
use OpenSoutheners\LaravelDataMapper\Contracts\RouteTransferableObject;

class CreateArticleWithOwner implements RouteTransferableObject
{
    public function __construct(
        public string $name,
        public string $content,
        #[ResolveModel(morphTypeFrom: 'owner_type')]
        public User|Team $owner,
        public string $ownerType,
    ) {}
}

class ArticleController
{
    public function store(CreateArticleWithOwner $data)
    {
        // Your controller logic
    }
}
```

{% hint style="info" %}

{% endhint %}

### Validating

When they're injected into the controllers we loss the ability to validate the data that users are sending into these objects, so that's why we can use the `Validate` class attribute:

```php
use Illuminate\Foundation\Http\FormRequest;
use OpenSoutheners\LaravelDataMapper\Attributes\Validate;

class CreateArticleWithOwnerFormRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            'name' => ['required', 'string', 'max:250'],
            'content' => ['required', 'string', 'min:120'],
            'owner_id' => ['required', 'numeric'],
            'owner_type' => ['required', 'string'],
        ];
    }
}

#[Validate(CreateArticleWithOwnerFormRequest::class)]
class CreateArticleWithOwner
{
    public function __construct(
        public string $name,
        public string $content,
        #[ResolveModel(morphTypeFrom: 'owner_type')]
        public User|Team $owner,
        public string $ownerType,
    ) {}
}

class ArticleController
{
    public function store(CreateArticleWithOwner $data)
    {
        // Your controller logic with validated data
    }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.opensoutheners.com/home/laravel-data-mapper/data-objects.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
