Support the ongoing development of Laravel.io →
Eloquent Packages Architecture
Last updated 2 years ago.
0

You can't do that with eloquent or any other active record implantation of ORMs. You're looking for a data mapper implantation of ORM such as Doctrine 2 (are there any :) ?)

If you can explain your use cases and WHY you want to return a genetic data, it would be much easier to understand your concern.

Last updated 2 years ago.
0

I simply say I have a

iUserRepository

and it is implemented by:

UserRepositoryEloquent implements iUserRepository

and

UserRepositoryDoctrine implements iUserRepository

as we know eloquent will return a collection of its eloquent models

and doctrine will return an array of it's orm models

that means that in my views I will get different results each time I switch my iUserRepository implementation, and than the repository loses all it's power.

Now I want to know how do somebody here perform the transfer from eoquent to something simpler, so in the future if I would like to use doctrine it would be easy to move.

Last updated 2 years ago.
0

Well, you can still loop through a collection of eloquent models since they implement iterator interface.

an activerecord implementation of ORM and a data mapper activerocrd are not exchangeable due to their nature.

You can use interfaces for active record objects, but that's not really an ideal solution either.

If you really care about the coupling issue you're worrying about, you probably want to start with Doctrine from the beginning. You can't use POPO with Eloquent.

Last updated 2 years ago.
0

Im sorry I dont understand your answer.... this is what needed to be done in an optimised world.

 interface iUserRepository {
      public function getUser();
 }

 UserRepositoryEloquent implements iUserRepository {

      public function getUser() {
          //just for example
          $user = User::firstOrFail(1);
          //now $user is an eloquent obj....
          //we should not return it as this will break our idea of repository
          
          //we would like to create a generic object from it
          return UserFactory::createFromEloquent($user);
      }

 }

 UserRepositoryDoctrine implements iUserRepository {

      public function getUser() {
          //just for example
         $product = $this->getDoctrine()
             ->getRepository('UserBundle:User')
             ->find(1);
          //now $user is an doctrine model
          //we should not return it as this will break our idea of repository
          
          //we would like to create a generic object from it
          return UserFactory::createFromDoctrine($user);
      }

 }

now the function of the factory: createFromEloquent & createFromDoctrine

get different inputs, but they return same output, and now the repository will return same data to the controller, and the controller wouldn't need to know what kind of implematation is used on the data layer.

Last updated 2 years ago.
0

Could you elaborate why you should not return doctrine model? Your doctrine entities are POPO objects...it's already a generic object IMO.

I really am confused..

Last updated 2 years ago.
0

@moon0326 It is not only doctrine model.... Because with eloquent I'll return eloquent model....

I want to return the exact same value from both implamatations .

So it would be easy to change from eloquent to doctrine with no controller changes

Last updated 2 years ago.
0

@tzookb // It's not possible or not a good idea IMO..

  1. If you return an exact same object from a factory (or decorator), how would you do relationships? Eager loading all the related entities are only option in this case. Lazy loading will work, but then you have to implement a DAL for each ORM. That's the double the work and unnecessary when you have an ORM.

  2. This is just my personal preference..but..I can't imagine my application to work magically when I switch from Eloquent to Doctrine. Eloquent is an active record implementation of ORM where you map an object to a table in a database. You don't have to do that in Doctrine and that's the beauty of it. I can't find a good reason to use Doctrine to do the same thing that I can do in an active record implantation of ORM such as Eloquent.

I would just interface your entities if you want minimal impact when you switch to a new ORM. It's not a perfect solution since you can actually access eloquent specific methods, but this is best bet IMO.

// Eloquent
class User extends Eloquent implemenets UserInterface 

{ function isActive() {

    }	
}

// Doctrine entity
class User implements UserInterface
{
    function isActive()
    {

    }
}
Last updated 2 years ago.
0

Thanks its a nice idea. The point is that I don't want to use eager loading or relationships outside of the repository so I don't care about losing that ability later.

In the end I'll simply return a $eloquent->toArray and finish it.

But there must be a better way.

It is weird I can't find even one example on the web

Last updated 2 years ago.
0

Depending on who decides what 'better way' is , but I haven't seen them yet in java, .net, and php world.

Returning an array is good, but I really think we have to remember that it is only good for views or data access.

if I were to return an array, I would not even bother to use doctrine or eloquent. It's just slow if all you want is an array. I would just use raw query if that's the case.

The fact that you're using an array means that you won't/shouldn't use that array in your business objects. If you use that in a view, I really don't think you even need a repository.

Last updated 2 years ago.
0

This is one of the parts of the repository pattern that I just can't get to grips with. I understand the reasoning behind using it but you come up against these issues a lot.

The idea of abstracting your models behind an interface is so that you can replace the data source behind the scenes and the consumers of that data will be none the wiser. As you have explained though, you are returning an object through this interface that is of a particular type with its own functionality.

There is little point in all your views using calls like $object->relationship to lazy load the objects related data when the you either have to change that if you change your data access method or you have to replicate that functionality in the new data access system.

The obvious solution is to return simple arrays of data from whatever data source you are using but then there is the downside that you no longer have the ease of use and functionality of something like Eloquent.

I have no idea what the correct answer is here but there is a growing backlash against the repository pattern as far as I can see. Once you actually implement it and try to change your data source you soon realise its not quite as painless as you had hoped in 90% of cases.

For very simple use cases the repository pattern makes perfect sense but for everything else I'm not so sure.

Last updated 2 years ago.
0

As sayd above, you can't abstract Eloquent behind a repository, and still have all the functionnality that eloquent provide. In fact, you shouldn't use Eloquent methods directly inside your controller to keep them clean.

A good repository pattern should use Eloquent to fetch data, then return it in a normalized way (ie, toArray). You can find many packages out there to convert Eloquent models to some sort of Entity object.

For a personnal project, I'm actually experimenting converting Eloquent models to stdClass objects (and collections to arrays of course). This include converting nested relations of course. This way, there is a clear separation between Controller side and Model side of repository.

Just as a reference implementation, here is the links to the personnal projet I'm talking about:

It's not yet battle-tested in the field, but it seems to work well for now.

I'll soon (matter of days) export that functionnality as a standalone package because there seems to be a growing interest around this concern. Just let me some time to clean out and test implementation in a real application.

Last updated 2 years ago.
0

I made some tests with this yesterday (my very first tests with repositories, so I'm far from an expert on the subject). I tried making a UserRepository for eloquent and an IUserRepository interface, just like OP did. What I did was to also make a separate UserObject with an IUserObject interface that wraps the eloquent object itself. Then you perform all actions on that object, and retern arrays / collections of them. Like

$userlist = $repository->listUsers(); // Returns an array of UserObject
$user = $userlist[0];
$user->setName('John Doe');  // UserObject has method for accessing fields and relations
$groups = $user->getGroups(); // Get relations and return array of GroupObject, another wrapper
$user->save();  // Also includes methods to store / load data, just directly mapped to Eloquent in this case

This way, if you want to swap from for example Eloquent to Doctrine you swap both the repositories and the object wrappers, and you can keep the controller logic intact.

I just made some quick tests with this and haven't tried it in a live project yet, so there's probably a ton of things I haven't thought of, but it's just an idea.

Last updated 2 years ago.
0

Well, in a repository implementation, the data model (in your case, your UserObject) shouldn't have anything to do with how data is retrieved or saved (ever heard of the term POCO object?)

So in your case:

  • If you plan on using the groups relation on user object, the user must never get groups by himself.

So this code:

$groups = $user->getGroups();

Shouldn't trigger the actual db fetching, just return prefetched data.

So, before you can read data, you must explicity require it with something like:

$repository->listUsers(array('groups')); //list users with eager loading of relation groups

Or

$groups = $grouprepository->getGroupsByUser($user); //get groups by a specific user
  • On the other hand, $user shouldn't have a save method. You should use the repo for that:
$repository->save($user); //validate data, then save user (update or insert)

This is a more decoupled approach of a repository pattern. This also avoid code duplication, and let a better separation of concern: (Eloquent -> low level db and relations, Repository -> persistence and validation, UserObject -> hold data for your controller)

Last updated 2 years ago.
0

@atrakeur - Very good points, I completely agree. I will update my repository/wrapper tests to reflect this :)

Anyway, my point to OP was that you can probably hide eloquent completely from the controller side by using a combination of a repository and model wrappers if you want to be able to swap the underlying storage and/or model classes.

Last updated 2 years ago.
0

Yeah on that point you are totally right.

However, in a perfect case, your wrapper objects must be independent from eloquent too so the convertion have to happen in the repository only. (so there are just some sort of data holder only)

But the big question is the implementation. As you sayd, you can use plain simple arrays. Alternatively you can use stdClass to keep the same syntax as eloquent in controllers/views. And maybe you can use your own custom objects too (but here we have to avoid copy-pasting and writing some proxy meaningless code).

Anyway the choice is up to the OP and how far he want to go. I'm just trying to help a little about the various way to do that.

Last updated 2 years ago.
0

Sign in to participate in this thread!

Eventy

Your banner here too?

tzookb tzookb Joined 9 Feb 2014

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.