Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a good practice to extend Laravel models more than one layer?

I'm reworking a project on Laravel 5.1

What I realize is that the old classes have become much complicated and do not really follow the 'single responsibility' principle anymore.

So I'm planning to do such:

<?php

class User extends Model
{

}

class SocialUser extends User
{

}

So I have a few questions,

  1. Is it possible to achieve that?
  2. If yes, then does the SocialUser class link back to the same database table which is Users and would it conflict with the User model itself?
  3. Is this all a good design practice at the first place? Or I better make use of traits?

Thank you.

like image 436
Matt Avatar asked Nov 04 '15 09:11

Matt


Video Answer


2 Answers

What you’re doing (extending the User model) is perfectly fine, and an approach I use myself in projects.

For example, if an application I’m building has shop-like functionality, then I may create a Customer model that extends my User model, and contains say, order-related relations:

class Customer extends User
{
    public function orders()
    {
        return $this->hasMany(Order::class, 'customer_id');
    }

    public function worth()
    {
        return $this->orders()->sum(function ($order) {
            return $order->total();
        });
    }
}

In a recent project, I’ve been working on email campaign functionality and created a Recipient class that extends the User model to add campaign-related methods:

class Recipient extends User
{
    public function campaigns()
    {
        return $this->belongsToMany(Campaign::class, 'recipient_id');
    }
}

Because both of these classes extend the User model, I get all of those (and Eloquent) methods:

$customers = Customer::with('orders')->get();

So long as you set the table in your base User model, any classes that inherit it will use that same table, even though the model may be named differently (i.e. Customer, Recipient, Student etc).

like image 179
Martin Bean Avatar answered Oct 26 '22 11:10

Martin Bean


IMHO I would go for the Repository pattern. It make's a lot of sense in your situation.

I would do the following:

interface UserRepository {

    public function find($id); 
    public function getAll(); 
    public function create(array $attributes);
    public function destroy($id);

    //you get the point

}

class CoreUserRepository implements UserRepository
{

     //implement the interface rules

}

class SocialUserRepository extends CoreUserRepository
{

     //implement the specific logic related to a SocialUser

}

Update

As Mjh described in the comments simply implementing the interface on all UserTypeRepository caused repetition - probably not what you want!

By extending your CoreUser you avoid repetition & maintain a design that will work for your situation.

Although, in your case it could be argued that you are still following SRP because everything in the User model is relating to a user, it's only the type of user which is differing.

Why go for the Repository Pattern?

  • You are ensuring you have a contractual agreement that all User Repositories need to implement.

  • Code is easier to maintain.

  • Business and data access logic can be tested separately

Should you extend your User model?

Here you are in danger of model pollution. While you can do anything with a model - not everything is a good idea.

Defining relationships on this approach would be a headache due to the confusion caused.

like image 23
jakehallas Avatar answered Oct 26 '22 10:10

jakehallas