10

I've tried to implement polymorphic relationships. They work perfectly... However, I'm trying to reduce my database size as much as possible so... I've this

Table action
|  id  |  realatable_type  |  relatable_id
|  1   |  Lion\People      |  65
|  2   |  Lion\Company     |  13

Obviously I've this

<?php namespace Lion;

class Company extends \Eloquent { ... }
class People extends \Eloquent { ... }

Is there any way to store only "People" or "Company" assuming that the namespace is always going to be "Lion"?

Israel Ortuño
  • 1,084
  • 2
  • 14
  • 33
  • 2
    There is a way of doing that, but it takes quite some work and it's probably not the best solution for your database size problem. Have you considering turning `readable_type` into an `ENUM('Lion\People', 'Lion\Company')` field? – rmobis Nov 09 '13 at 20:09
  • @Raphael_ I have to admit that I was expecting for Eloquent solutions but this one looks like one posibility... :D – Israel Ortuño Nov 09 '13 at 20:11
  • This actually takes even less space than only `'People'` or `'Company'`. If you really want the Eloquent solution, let me know, I'll post it. – rmobis Nov 09 '13 at 20:16
  • @Raphael_ please, that Eloquent solution would be great to be seen ;) – Israel Ortuño Nov 09 '13 at 20:23
  • @Raphael_ Actually that one last solution works! However it's a bit hard to work with back-slashes in MySQL as when typing they have to be double and when editing you have to take care of not destroying anything... Also when editing, all rows loses their index and have to be set again... That looks pretty scary when production – Israel Ortuño Nov 09 '13 at 20:32
  • Ok sorry, no, it does not lose the index, however you have to be really careful when editing as when you edit you only get one backslash, which will be set to nothing when saving. Anyway, this works, if you have also an idea of how to manage this with Eloquent will be so great – Israel Ortuño Nov 09 '13 at 20:34

2 Answers2

23

Since Laravel 4.1, inside your model (in this case Company and People) you can set the protected property $morphClass to whatever you want.

<?php namespace Lion;

class Company extends \Eloquent { 

    protected $morphClass = 'Company';
}

Now in your table you can store the type without the namespace:

 |  id  |  realatable_type  |  relatable_id
 |  2   |  Company          |  13
Rodrigo Gauzmanf
  • 2,517
  • 1
  • 23
  • 26
  • 2
    In Laravel 5.3 the $morphClass property has been removed. Now you need to use `Relation::morphMap(['company' => Company::class]);` – Rodrigo Gauzmanf Aug 05 '16 at 19:20
3

I believe the best solution here (for database size at least) would be to simply change readable_type to ENUM('Lion\Company', 'Lion\People').


That being said, if you really want to handle this on Laravel side, you'll have to create new classes extending from Illuminate\Database\Eloquent\Relations\Morph* ¹ and overwrite their constructors ² as to get only the last value after a dash, on $morphClass. Something like this:

<?php

use \Illuminate\Database\Eloquent\Model;
use \Illuminate\Database\Eloquent\Builder;

class MyMorphOne extends \Illuminate\Database\Eloquent\Relations\MorphOne {
    public function __construct(Builder $query, Model $parent, $type, $id) {
        parent::__construct($query, $parent, $type, $id);

        $this->morphClass = substr($this->morphClass, strrpos($this->morphClass, '\\') + 1);
    }
}

Then, extend your own model or base model, to overwrite morphOne, morphMany and morphToMany methods as to make use of your new extended classes. Something like this:

<?php

class People extends Eloquent {

    // ...

    public function morphOne($related, $name, $type = null, $id = null) {
        $instance = new $related;

        list($type, $id) = $this->getMorphs($name, $type, $id);

        $table = $instance->getTable();

        return new MyMorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id);
    }

}
  1. * = One, Many and ToMany
  2. Which are actually inherited from MorphOneOrMany on MorphOne and MorphMany.
rmobis
  • 26,129
  • 8
  • 64
  • 65