RoboRobok's avatar

How to handle listing multiple model types?

I have some documents in database. They come in different types, so I have several models representing documents. Now, what if I need to list them all by column, let's say user_id?

My initial database design had each type of the document reside in its own table, like invoices, proofs etc. I also had separate documents table with user_id, document_id and type, using polymorphic relations.

This is a little troublesome though, because I need to use user_id very often for each document type and fetching it through morphing each time is a little much.

How would you guys define this relation?

0 likes
10 replies
D9705996's avatar

I would handle this using polymorphic relationships. You would have a document model with a belongsTo relationship with user and a morphsTo.

Each of your sub types then has a morphOne relationship to the document model.

RoboRobok's avatar

@d9705996 the question is, if I add user_id column to documents table, can I fetch particular type by user? Something like this:

$invoices = User::find(3)->invoices;

The only reason I use polymorphic relation here is to be able to fetch all user's documents. But I will also need to fetch particular document type. Would be great if I could avoid duplicating user_id, which would be the case if I added user_id to both documents and invoices, proofs etc.

I'm not sure how to define this relationship to be able to do the following:

  1. Get all user's documents of cetain type (like Invoice)
  2. Get all user's documents ordered by a column (for example oldest to newest)
  3. Get particular document (for example Invoice #37) with its user and all data.
D9705996's avatar

@ROBOROBOK - You could invert you logic and do something like

Invoices::forUser(auth()->user());

Rather than implement forUser on each document type create an abstract document type class with a local scope method and extend this class in all your sub types.

Something like this assuming your polymorphic relation is called document (not tested so ymmv)

    public function scopeForUser($query, User $user) 
    {
        return $query->where('document.user_id, $user->id);
    }

You will need to tinker to fit your setup but the general approach should work

RoboRobok's avatar

Will that make it possible to fetch all different document types grouped for a user?

D9705996's avatar

For that scenario you would want to go from user to the documents

User::find(3)->documents
    ->groupBy('document_type);
RoboRobok's avatar

When I said „grouped” I didn’t mean to use groupBy xD I meant all together, with being able to do things like „taking 10 newest documents of this user”. That’s not possible that way, because I would need to fetch all and trim the result, which will be super slow.

D9705996's avatar

You can do this from the document class

Document::where('user_id', 3)
  ->latest()
  ->take(10)
  ->get();
RoboRobok's avatar

And how do I make a collection of particular documents out of it?

D9705996's avatar

What do you mean by particular documents? Your polymorphic relationship between document and the subtypes will allow you to get the related sub type

RoboRobok's avatar

Right. Are you sure about this though?

$query->where('document.user_id', $user->id);

I don't think you can nest where like this. It produces the error:

Unknown column 'document.user_id' in 'where clause

Please or to participate in this conversation.