When considering an interface for document management on different types of entities (Employees, Customers, Companies and departments, for example) on an enterprise system, one might presume it is necessary to manage each type of document separately. This approach would however be repetitive because each document model would presumably consist of the same data structure. You can eliminate this repetition using a Laravel polymorphic relation, resulting in all documents being managed via a single Document model.
Incidentally, although the models found in this post using Laravel 5-specific syntax, it may be compatible with Laravel 4 since it supports polymorphic relations.
Let's work through an example that would use polymorphic relations to add document capabilities to the Employee
and Customer
models. Begin by creating a new model named Document
, by creating a file named Document.php
and placing it in the project's app directory:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Document extends Model { protected $fillable = []; }
Next, generate the associated database table that will store document data:
$ php artisan make:migration create_documents_table --create=documents Created Migration: 2018_04_11_205601_create_documents_table
Next, open up the newly generated migration file and modify the up()
method to look like this:
Schema::create('documents', function(Blueprint $table) { $table->increments('id'); $table->string('file_name'); $table->string('file_path'); $table->string('description'); $table->integer('documentable_id'); $table->string('documentable_type'); $table->timestamps(); });
Finally, save the changes and run the migration:
$ php artisan migrate Migrated: 2018_04_11_205601_create_documents_table
Because the Document
model serves as a central repository for documents associated with multiple different entities, we require a means for knowing both which model and which record ID is associated with a particular document. The documentable_type
and documentable_id
fields serve this purpose. For instance, if a document is associated with an employee, and the employee record associated with the document has a primary key of 22, then the document's documentable_type
field will be set to App\Employee
and the documentable_id
to 22.
Logically you'll want to attach other fields to the documents
table if you plan on for instance assigning ownership to documents via the Employee
model, or would like to include a name for each document.
Next, open the Document
model and add the following method:
class Document extends Model { public function documentable() { return $this->morphTo(); } }
The morphTo
method defines a polymorphic relationship. Personally I find the name to be a poor choice; when you read it just think "belongs To" but for polymorphic relationships, since the record will belong to whatever model is defined in the documentable_type
field. This defines just one side of the relationship; you'll also want to define the inverse relation within any model that will be documentable
, creating a method that determines which model is used to maintain the documents, and referencing the name of the method used in the polymorphic model:
class Employee extends Model { public function documents() { return $this->morphMany('App\Document', 'documentable'); } } class Customer extends Model { public function documents() { return $this->morphMany('App\Document', 'documentable'); } }
With these two methods in place, it's time to begin using the polymorphic relation! The syntax for adding, removing and retrieving documents
is straightforward; in the following example we'll attach a new document to an employee and customer:
$employee = Employee::find(22); $document = new Document(); $document->file_name = 'passport-front.png'; $document->description = 'Passport Front Page'; $employee->documents()->save($document);
and attach a document to customer model:
$customer = Customer::find(39); $document = new Document(); $document->file_name = 'invoice-may-2018.pdf'; $document->description = 'Invoice for May 2018'; $customer->documents()->save($document);
After saving the document, review the database and you'll see a record that looks like the following:
mysql> select * from documents; +----+--------------------+----------------+---------------------+------------+------------+ | id | file_name | documentable_id| documentable_type | created_at | updated_at | +----+--------------------+----------------+---------------------+------------+------------+ | 1 | passport-front.png | 22 | App\Employee | 2018-... | 2018-... | +----+--------------------+----------------+---------------------+------------+------------+ +----+--------------------+----------------+---------------------+------------+------------+ | 2 | ..ice-may-2018.pdf | 39 | App\Customer | 2018-... | 2018-... | +----+--------------------+----------------+---------------------+------------+------------+
The employee's documents are just a collection, so you can easily iterate over it. You'll retrieve the employee within the controller per usual:
public function index() { $employee = Employee::find(1); return view('employee.show')->with('employee', $employee); }
In the corresponding view you'll iterate over the documents collection:
@foreach ($employee->documents as $document) <p> {{ $document->file_name }} </p> <p> {{ $document->file_path }} </p> <p> {{ $document->description }} </p> @endforeach
Polymorphic relations can no doubt save a great deal of time and effort when you'd like models to share a particular feature such as documents, notes or contacts on an enterprise system.
If you have any other questions, experience or insights on "Creating , Using Laravel polymorphic relations" please feel free to leave your thoughts in the comments bellow which might be helpful to someone some day!
Be the first one to write a response :(
{{ reply.member.name }} - {{ reply.created_at_human_readable }}