Creating a data object | laravel-data | Spatie

 SPATIE

  Laravel Data
===============

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-data](https://spatie.be/docs/laravel-data/v2)  As-a-data-transfer-object  Creating a data object

 Version   v4   v3   v2   v1

 Other versions for crawler [v4](https://spatie.be/docs/laravel-data/v4) [v3](https://spatie.be/docs/laravel-data/v3) [v2](https://spatie.be/docs/laravel-data/v2) [v1](https://spatie.be/docs/laravel-data/v1)

- [ Introduction ](https://spatie.be/docs/laravel-data/v2/introduction)
- [ Support us ](https://spatie.be/docs/laravel-data/v2/support-us)
- [ Requirements ](https://spatie.be/docs/laravel-data/v2/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-data/v2/installation-setup)
- [ Questions and issues ](https://spatie.be/docs/laravel-data/v2/questions-issues)
- [ Changelog ](https://spatie.be/docs/laravel-data/v2/changelog)
- [ About us ](https://spatie.be/docs/laravel-data/v2/about-us)

Getting started
---------------

- [ Quickstart ](https://spatie.be/docs/laravel-data/v2/getting-started/quickstart)

As a DTO
--------

- [ Creating a data object ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/creating-a-data-object)
- [ Optional properties ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/optional-properties)
- [ Nesting ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/nesting)
- [ Collections ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/collections)
- [ Casts ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/casts)
- [ From a request ](https://spatie.be/docs/laravel-data/v2/as-a-data-transfer-object/request-to-data-object)

As a resource
-------------

- [ From data to resource ](https://spatie.be/docs/laravel-data/v2/as-a-resource/from-data-to-resource)
- [ Including and excluding properties ](https://spatie.be/docs/laravel-data/v2/as-a-resource/lazy-properties)
- [ Wrapping ](https://spatie.be/docs/laravel-data/v2/as-a-resource/wrapping)
- [ Transforming data ](https://spatie.be/docs/laravel-data/v2/as-a-resource/transformers)

Advanced usage
--------------

- [ Eloquent casting ](https://spatie.be/docs/laravel-data/v2/advanced-usage/eloquent-casting)
- [ Transforming to TypeScript ](https://spatie.be/docs/laravel-data/v2/advanced-usage/typescript)
- [ Working with dates ](https://spatie.be/docs/laravel-data/v2/advanced-usage/working-with-dates)
- [ Normalizers ](https://spatie.be/docs/laravel-data/v2/advanced-usage/normalizers)
- [ Pipeline ](https://spatie.be/docs/laravel-data/v2/advanced-usage/pipeline)
- [ Use with Inertia ](https://spatie.be/docs/laravel-data/v2/advanced-usage/use-with-inertia)
- [ Use with Livewire ](https://spatie.be/docs/laravel-data/v2/advanced-usage/use-with-livewire)
- [ Creating a cast ](https://spatie.be/docs/laravel-data/v2/advanced-usage/creating-a-cast)
- [ Creating a transformer ](https://spatie.be/docs/laravel-data/v2/advanced-usage/creating-a-transformer)
- [ Creating a rule inferrer ](https://spatie.be/docs/laravel-data/v2/advanced-usage/creating-a-rule-inferrer)
- [ Internal structures ](https://spatie.be/docs/laravel-data/v2/advanced-usage/internal-structures)
- [ Validation attributes ](https://spatie.be/docs/laravel-data/v2/advanced-usage/validation-attributes)

      You are viewing the documentation for **an older version** of this package. You can check the version you are using with the following command:

 `                                    composer show spatie/laravel-data                                                                                                                                                                                                                                    `

Creating a data object
======================

###  On this page

1. [ Mapping property names ](#content-mapping-property-names)
2. [ Magical creation ](#content-magical-creation)
3. [ Optional creation ](#content-optional-creation)
4. [ Creation without magic methods ](#content-creation-without-magic-methods)
5. [ Advanced creation ](#content-advanced-creation)
6. [ Quickly getting data from Models, Requests, ... ](#content-quickly-getting-data-from-models-requests-)

Let's get started with the following simple data object:

```
class SongData extends Data
{
    public function __construct(
        public string $title,
        public string $artist,
    ) {
    }
}
```

Since this is just a simple PHP object, it can be initialized as such:

```
new SongData('Never gonna give you up', 'Rick Astley');
```

But with this package, you can initialize the data object also with an array:

```
SongData::from(['title' => 'Never gonna give you up', 'artist' => 'Rick Astley']);
```

You can use the `from` method to create a data object from nearly anything. For example, let's say you have an Eloquent model like this:

```
class Song extends Model{

}
```

You can create a data object from such a model like this:

```
SongData::from(Song::firstOrFail($id));
```

Data can also be created from JSON strings:

```
SongData::from('{"name" : "Never Gonna Give You Up","artist" : "Rick Astley"}');
```

The package will find the required properties within the model and use them to construct the data object.

Although the PHP 8.0 constructor properties look great in data objects, it is perfectly valid to use regular properties without a constructor like so:

```
class SongData extends Data
{
    public string $title;
    public string $artist;
}
```

Mapping property names
--------------------------------------------------------------------------------------------------------------------------

Sometimes the property names in the array from which you're creating a data object might be different. You can define another name for a property when it is created from array using attributes:

```
class ContractData extends Data
{
    public function __construct(
        public string $name,
        #[MapInputName('record_company')]
        public string $recordCompany,
    ) {
    }
}
```

Creating the data object can now be done as such:

```
SongData::from(['name' => 'Rick Astley', 'record_company' => 'RCA Records']);
```

Changing all property names in a data object to snake\_case in the data the object is created from can be done as such:

```
#[MapInputName(SnakeCaseMapper::class)]
class ContractData extends Data
{
    public function __construct(
        public string $name,
        public string $recordCompany,
    ) {
    }
}
```

You can also use the `MapName` attribute when you want to combine input (see [transforming data objects](https://spatie.be/docs/laravel-data/v2/as-a-resource/from-data-to-resource#mapping-property-names)) and output property name mapping:

```
#[MapName(SnakeCaseMapper::class)]
class ContractData extends Data
{
    public function __construct(
        public string $name,
        public string $recordCompany,
    ) {
    }
}
```

Magical creation
--------------------------------------------------------------------------------------------------------

It is possible to overwrite or extend the behaviour of the `from` method for specific types. So you can construct a data object in a specific manner for that type. This can be done by adding a static method starting with 'from' to the data object.

For example, we want to change how we create a data object from a model. We can add a `fromModel` static method that takes the model we want to use as a parameter:

```
class SongData extends Data
{
    public function __construct(
        public string $title,
        public string $artist,
    ) {
    }

    public static function fromModel(Song $song): self
    {
        return new self("{$song->title} ({$song->year})", $song->artist);
    }
}
```

Now when creating a data object from a model like this:

```
SongData::from(Song::firstOrFail($id));
```

Instead of the default method, the `fromModel` method will be called to create a data object from the found model.

You're truly free to add as many from methods as you want. For example, you could add one to create a data object from a string:

```
class SongData extends Data
{
    public function __construct(
        public string $title,
        public string $artist,
    ) {
    }

    public static function fromString(string $string): self
    {
        [$title, $artist] = explode('|', $string);

        return new self($title, $artist);
    }
}
```

From now on, you can create a data object like this:

```
SongData::from('Never gonna give you up|Rick Astley');
```

It is also possible to use multiple arguments in a magical creation method:

```
class SongData extends Data
{
    public function __construct(
        public string $title,
        public string $artist,
    ) {
    }

    public static function fromMultiple(string $title, string $artist): self
    {
        return new self($title, $artist);
    }
}
```

Now we can create the data object like this:

```
SongData::from('Never gonna give you up', 'Rick Astley');
```

There are a few requirements to enable magical data object creation:

- The method must be **static and public**
- The method must **start with from**
- The method cannot be called **from**

When the package cannot find such a method for a type given to the data object's `from` method. Then the data object will try to create itself from the following types:

- An *Eloquent model* by calling `toArray` on it
- A *Laravel request* by calling `all` on it
- An *Arrayable* by calling `toArray` on it
- An *array*

This list can be extended using extra normalizers, find more about it [here](https://spatie.be/docs/laravel-data/v2/advanced-usage/normalizers).

When a data object cannot be created using magical methods or the default methods, a `CannotCreateData`exception will be thrown.

Optional creation
-----------------------------------------------------------------------------------------------------------

It is impossible to return `null` from a data object's `from` method since we always expect a data object when calling `from`. To solve this, you can call the `optional` method:

```
SongData::optional(null); // returns null
```

Underneath the optional method will call the `from` method when a value is given, so you can still magically create data objects. When a null value is given, it will return null.

Creation without magic methods
--------------------------------------------------------------------------------------------------------------------------------------------------

You can ignore the magical creation methods when creating a data object as such:

```
SongData::withoutMagicalCreationFrom($song);
```

Advanced creation
-----------------------------------------------------------------------------------------------------------

Internally this package is using a pipeline to create a data object from something. This pipeline exists of steps which transform properties into a correct structure and it can be completely customized. You can read more about it [here](/docs/laravel-data/v2/advanced-usage/pipeline).

Quickly getting data from Models, Requests, ...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

By adding the `WithData` trait to a Model, Request or any class that can be magically be converted to a data object, you'll enable support for the `getData` method. This method will automatically generate a data object for the object it is called upon.

For example, let's retake a look at the `Song` model we saw earlier. We can add the `WithData` trait as follows:

```
class Song extends Model{
    use WithData;

    protected $dataClass = SongData::class;
}
```

Now we can quickly get the data object for the model as such:

```
Song::firstOrFail($id)->getData(); // A SongData object
```

We can do the same with a FormRequest, we don't use a property here to define the data class but use a method instead:

```
class SongRequest extends Request
{
    use WithData;

    protected function dataClass(): string
    {
        return SongData::class;
    }
}
```

Now within a controller where the request is injected, we can get the data object like this:

```
class SongController
{
    public function __invoke(SongRequest $request): SongData
    {
        $data = $request->getData();

        $song = Song::create($data);

        return $data;
    }
}
```
