Search code examples
phpdata-transfer-objects

How can I return multiple values from a function that would support IDE autocompletion?


Imagine that you have a function which selects data from a database table, using something like this query:

select count(id), sum(amount) from payments group by amount;

It then passes the results to another function, which loops through the results and returns an array, with each element in the array referring to one row from the result. For example:

private function packageResults(array $results)
{
    $ret = [];
    foreach ($results as $result) {
        // add $result['sum'] and $result['count'] to something, then add it to the $ret array
    }

    return $ret;
}

Now, if in the foreach loop, I add the results to an associative array or StdClass and then add it to $ret, you will have to read my code inside of the foreach to find out the structure (I.E. the properties set on the StdClass, or the keys set on the associative array). What I mean is, since both of these data types are free-form, meaning they don't have their keys/properties documented anywhere, the person using my code first has to dump the output to know what this function returns.

What can I use to tell the user (and their IDE) what the structure of the data I return is, so that they wouldn't have to dig through my code to find it? Are data transfer objects made for this stuff? I'm thinking of data transfer objects because they are classes with their own properties, and this can be used to know what exactly my function returns. However, I'm not sure if this is the right approach from a software design point of view.


Solution

  • Generally, to hint others (IDE too) about constructs your function returns, use @var array $results phpdoc annotation, f.e.

    /** @var BlahBlah $someVar */
    $someVar = new BlahBlah();
    $someVar->... (IDE will show you available things with context help now).
    

    To show type of objects in array that you fill with foreach do:

    /** @var BlahBlah[] $someArray */
    $someArray = array();
    
    $someArray[1] = new BlahBlah(); // (IDE should help you with context help if you use it later
    

    You can also call it where you need it:

    $x = 0;
    foreach ($someArray as $value) {
        /** @var BlahBlah $value */
        $x += $value->getSomeValue(); // (IDE will help you with context help).
    }
    

    As for your case, if you know it will be an array and user of your function will need specific values, you CAN and SHOULD use a new class (DTO, as you wish) for it (I've never been a fan of associative arrays anyway). If you fill the fields of the object properly, you can then hint the user what type you are returning (and those fields can be annotated too, like for example $price will be stdClass, $something will be SomethingElse etc.) and he won't need to look inside your function, since what you are returning him will work as sort of an interface for him.

    Have in mind though that it's not strictly DTO pattern. DTOs are used for expensive communication between f.e. webservices. I use this term here only as a simplification of sorts. If you want to know more about DTO, you should scan google for information about it (there is vast amount wherever you go).

    What you need here is an object being an interface for data of sorts, that will aggregate the values that you need to get from database, operate on and pass to frontend, so more or less it's a POPO that you need (Plain Old PHP Object, equivalent of Java's POJO).