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/v1)  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/v1/introduction)
- [ Support us ](https://spatie.be/docs/laravel-data/v1/support-us)
- [ Requirements ](https://spatie.be/docs/laravel-data/v1/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-data/v1/installation-setup)
- [ Questions and issues ](https://spatie.be/docs/laravel-data/v1/questions-issues)
- [ Changelog ](https://spatie.be/docs/laravel-data/v1/changelog)
- [ About us ](https://spatie.be/docs/laravel-data/v1/about-us)

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

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

As a DTO
--------

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

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

- [ From data to resource ](https://spatie.be/docs/laravel-data/v1/as-a-resource/from-data-to-resource)
- [ Transforming data ](https://spatie.be/docs/laravel-data/v1/as-a-resource/transformers)
- [ Lazy properties ](https://spatie.be/docs/laravel-data/v1/as-a-resource/lazy-properties)

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

- [ Eloquent casting ](https://spatie.be/docs/laravel-data/v1/advanced-usage/eloquent-casting)
- [ Transforming to TypeScript ](https://spatie.be/docs/laravel-data/v1/advanced-usage/typescript)
- [ Creating a cast ](https://spatie.be/docs/laravel-data/v1/advanced-usage/creating-a-cast)
- [ Creating a transformer ](https://spatie.be/docs/laravel-data/v1/advanced-usage/creating-a-transformer)
- [ Creating a rule inferrer ](https://spatie.be/docs/laravel-data/v1/advanced-usage/creating-a-rule-inferrer)
- [ Internal structures ](https://spatie.be/docs/laravel-data/v1/advanced-usage/internal-structures)
- [ Validation attributes ](https://spatie.be/docs/laravel-data/v1/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. [ Magical creation ](#content-magical-creation)
2. [ Optional creation ](#content-optional-creation)
3. [ 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));
```

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

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');
```

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 can only take **one typed parameter** for which you want to create an object
- 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*

When a data object cannot be created using magical methods or the default methods, a `CannotCreateDataFromValue`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.

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;
    }
}
```
