5

I'm trying to implement the Repository pattern in a Laravel 5 app by following this article. In it, the repository implementation converts the object for the specific data source (in this case Eloquent) to an stdClass so that the app uses a standard format and doesn't care about the data source.

To convert a single Eloquent object they do this:

/**
* Converting the Eloquent object to a standard format
* 
* @param mixed $pokemon
* @return stdClass
*/
protected function convertFormat($pokemon)
{
    if ($pokemon == null)
    {
        return null;
    }

    $object = new stdClass();
    $object->id = $pokemon->id;
    $object->name = $pokemon->name;

    return $object;
}

Or, as someone in the comments pointed out, this could also work:

protected function convertFormat($pokemon)
{
    return $pokemon ? (object) $pokemon->toArray() : null;
}

But then, what happens when I want to cast an entire collection of Eloquent objects to an array of ** stdClass **? Do I have to loop through the collection and cast each element separately? I feel this would be a big hit on performance, having to loop and cast every element every time I need a collection of something and it also feels dirty.

Laravel provides Eloquent\Collection::toArray() which turns the entire collection to an array of arrays. I suppose this is better, but still not stdClass

The benefits of using a generic object would be that I could do this in my code

echo $repo->getUser()->name;

Instead of having to do this:

echo $repo->getUser()['name'];
Vic
  • 2,655
  • 3
  • 18
  • 28
  • Eeeww! Arrays! Loss of type hints, models and methods... Create a custom User class if you don't want expose the Eloquent model, don't go with generic arrays. – sisve Jul 15 '15 at 17:41
  • Interesting suggestion. Sounds nice but then, everytime I needed a new model I would have to create a model entity, an interface, a repository that implements that interface and now _also_ a generic class to be returned by the repository? All the logic that this new generic User class would have is already inside the repository anyways, why the need for yet _another_ abstraction layer? In the end, I just need to access the data and the generic object class is quite enough for this, I think. Or would this User class just be an empty wrapper with no methods and/or attributes? @SimonSvensson – Vic Jul 15 '15 at 17:55
  • 1
    Your repositories should only have logic regarding database access, how data is loaded (and perhaps persisted). Stuff like $user->setPassword('...') is part of your domain model, and does not belong to your repository. But you would still need a call to $repo->persist($user) unless you also implement some kind of object tracking. I would say that you should use Doctrine instead of Eloquent, but that's a larger change than just skipping the arrays. Anyhow, have you tried using `array_map` for your actual question? What's the problem with looping through everything? – sisve Jul 15 '15 at 18:36
  • Well, I was thinking maybe it would affect performance to do that. I had also forgotten about ``array_map``. That does work perfectly. Now I'm thinking about what you said though and it makes a lot of sense. I have a followup question: does it make sense to put the domain model logic in the service layer (using service-oriented architecture here)? Then, my UserService would contain the business logic and would be the only one dealing with the repository. My app would just call on the Service and that would make the UserService that generic User class that you mentioned, woudln't it? – Vic Jul 15 '15 at 20:44
  • by the way @SimonSvensson, you can put that ``array_map`` as an answer and I'll accept it (if there aren't any performance issues related with it). – Vic Jul 15 '15 at 20:45

4 Answers4

3

Using eloquent you can do something like this:

/**
 * Gets the project type by identifier.
 *
 * @param string $typeIdentifier
 *
 * @return object
 *
 */
public function getTypeByIdentifier($typeIdentifier)
{
    $type =  ProjectType::where(
        'type_identifier', $typeIdentifier
    )->first();

    return (object) $type->toArray();
}

All my factories etc accept stdClass so that it's uniform. In eloquent you can either do as above as Eloquent already has a toArray() function which is needed for serialisation, but you can also easily extend Model (Illuminate\Database\Eloquent) to have this method available to all your eloquent models. I suggest that you extend the model so that you can also automate this collections and not just single records.

Because I use the repository pattern with Eloquent I normally create an abstract EloquentRepository which extends the Eloquent Model methods and also obviously allows us to add new methods such as this one.

Keith Mifsud
  • 1,599
  • 1
  • 16
  • 26
1

You can do something like this way,

For example there is User class

$user = User::find(1)->toArray();

//this is code for convert to std class
$user = json_encode($user);
$user = json_decode($user);

json_decode by default return stdClass object.

I hope this will help.

Jaykesh Patel
  • 2,130
  • 1
  • 14
  • 18
0

Yes, you would need to loop through the collection and cast every object. You can save a few lines of code by using array_map.

sisve
  • 19,501
  • 3
  • 53
  • 95
0

You can use the getQuery() method to convert/cast the \Illuminate\Database\Eloquent\Builder to \Illuminate\Database\Query\Builder.

return $this->model->getQuery()->get();

will return a collection (or an array before 5.3) of stdClass objects.

return $this->model->where('email', $email)->getQuery()->first();

will return a stdClass object.

There is no need to fetch eloquent models and convert them one by one.

Paul Spiegel
  • 30,925
  • 5
  • 44
  • 53
  • what about if you eager load relationships? This way the eager loaded relationship will be lost – Helder Lucas Aug 12 '17 at 15:30
  • @HelderLucas The relations would not even be loaded. And I didn't see this being part of the question. However, if you need the relations, you would either eager load them on your own, or live with the overhead of eloquent. – Paul Spiegel Aug 13 '17 at 00:54