I am attempting to create a clean cut service layer, whereby the service layer acts upon one or more repositories, and each repositories acts on its own eloquent model.
For example, I may have:
ForumService
|
+-- PostRepo extends PostInterface
| |
| +-- Post (Eloquent)
|
+-- UserRepo extends UserInterface
|
+-- User (Eloquent)
Each service defines it's required dependencies via ioc
. So, something like:
// MessageService
// ..
public function __construct(UserInterface $userRepository,
MessageInterface $messageRepository) {
// ..
}
My repositories are resolved via their bindings in their respective service providers, such as:
class UserRepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app>bind(
'App\Models\Repositories\User\UserInterface',
'App\Models\Repositories\User\UserRepository');
}
}
This all works just fine. Each service gets the repositories it requires.
To keep the service layer clear of any specific dependency on eloquent, anything that leaves a repo is a simple, immutable, data object.
Key points in everyday language:
However I can't come up with a clean pattern to associate
eloquent models to each other at the service or repo layer.
Given the Post
model has a belongsTo(User::class)
relationship, how do I cleanly create that relationship at the Post
repository layer.
I have tried:
public function associate($authorId)
{
$post->author()->associate($authorId);
}
But associate
expects a user
eloquent object, not just an id. I could do:
public function associate($authorId)
{
$post->from()->associate($userRepo->findEloquent($authorId));
}
But I feel like I am surfacing a eloquent model up into a repo that shouldn't be acting on it.
The easy way:
public function assignToAuthor($postId, $authorId)
{
$post = $this->find($postId); // or whatever method you use to find by id
$post->author_id = $authorId;
}
Now, the above implies that you know the foreign key author_id
of the relation. In order to abstract it just a bit, use this:
public function assignToAuthor($postId, $authorId)
{
$post = $this->find($postId);
$foreignKey = $post->author()->getForeignKey();
$post->{$foreignKey} = $authorId;
}
Mind, that you still need to save
the $post
model, but I suppose you already know that.
Depending on your implementation of the simple, immutable, data object that you use, you could also allow passing the objects instead of raw ids. Something between the lines:
public function assignToAuthor($postId, $authorId)
{
if ($postId instanceof YourDataOject) {
$postId = $postId->getId();
}
if ($authorId instanceof YourDataOject) {
$authorId = $authorId->getId();
}
// ...
}
What I've done in the past that has brought some sanity to this situation for me was do things similar to what you are doing in your second associate
method and prefix the repository with Eloquent
so in the event I use something besides Eloquent
, I just create a new implementation of the repository.
So in this case, I'd end up with class EloquentUserRepository implements UserInterface
. I usually end up with some public methods which take and return only primitives and possibly some private methods which would be coupled to Eloquent so what I end up doing then is dropping those public methods into a AbstractUserRepository
, or a trait if it makes more sense, to keep the code DRY.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With