fireproofsocks / dto

Data Transfer Object (DTO) in PHP using JSON Schema
77 stars 12 forks source link

Direct Property Access #11

Closed johannesschobel closed 6 years ago

johannesschobel commented 6 years ago

Hey there, I just stumbled upon your DTO implementation and i really, really, really love it! That is so easy to use! Really great job on this one!

However, i have one question, you may be able to answer me straight away..

Consider the following example. I have a GenericDTO that may be used, if no other one is specified. In order to make it generic (i.e., usable in all kind of situations, i have the following schema:

    protected $schema = [
        'type' => 'object',
        'properties' => [
            'additionalProperties' => true,
        ],
    ];

Now i create a GenericDTO with my data to be passed..

// assume, $data is a (deeply nested) array
$dto = new GenericDTO($data);

While this certainly works, how can i now access the data of an attribute? For example, if i want to get the name, i would like to call $dto->name. However, calling this, outputs the object itself - with the "storage" where my data is located in. If i run $dto->name->toScalar() i get "John Doe". However, toScalar() is not always possible, as i may access arrays or objects as well (because it is generic).

Do you have an idea how to solve this properly? Or am I missing something crucial here? Thanks a lot for your awesome work..

johannesschobel commented 6 years ago

Update:

My best so far, is to override the __get(...) method from your DTO like this:


public function __get($name)
{
    $field = parent::__get($name);
    $type = $field->getStorageType();

    $value = call_user_func([$field, 'to' . Str::ucfirst($type)]);

    return $value;
}

What do you think about this?

fireproofsocks commented 6 years ago

Sorry -- I just saw this (my Github notifications went AWOL). Oiy... no hacks like that should be required. You can access object properties using arrow notation or using the "get" method. For example:

class GenericDTO extends \Dto\Dto
{
    protected $schema = [
        'type' => 'object',
        'properties' => [
            'additionalProperties' => true,
        ],
    ];
}

And then you may instantiate it as such:

$dto = new GenericDTO(['foo' => 'bar', 'some' => ['deep' => ['nested' => ['data']]]]);

You can retrieve an object attribute using arrow notation, or the get() method

print $dto->foo; // bar
print $dto->foo->toScalar(); // bar
print $dto->get('foo'); // bar

The trick is that each node in the DTO is also a DTO. PHP objects implicitly have a toString method, so PHP will figure out how to handle something like $x = "The foo is " . $dto->foo; because from the context, it is clear that a string value is required. PHP doesn't have a toInteger or __toBoolean etc. methods, so sometimes you need to really get the scalar value out of your DTO, so that's what the toScalar() method is for.

You can access the other attributes by name as well, but remember: you are always getting another DTO object in return.

So this returns a huge result with lots of recursion:

print_r($dto->some); // !!! big object with lots of recursion

But using the toArray() method gives you what you probably want:

Array
(
    [deep] => Array
        (
            [nested] => Array
                (
                    [0] => data
                )

        )

)

Similarly, toObject formats the output as an object (think of these as "views" since PHP is a bit ambiguous with its associative arrays and object syntax):

stdClass Object
(
    [deep] => stdClass Object
        (
            [nested] => stdClass Object
                (
                    [0] => data
                )

        )

)

Finally, you can access deeper items in your structure by using the arrow notation (for objects) and bracket notation for accessing elements from an array.

print $dto->some->deep->nested[0]; // prints "data"

Hope that makes things more clear. There are some examples on https://github.com/fireproofsocks/dto/wiki/GettingStarted to help get you started.