Skip to content

Instantly share code, notes, and snippets.

@asakurayoh
Created November 17, 2012 22:38
Show Gist options
  • Save asakurayoh/4100877 to your computer and use it in GitHub Desktop.
Save asakurayoh/4100877 to your computer and use it in GitHub Desktop.
Single Table Inheritance in Laravel 4 / Illuminate
<?php namespace App;
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
class Builder extends BaseBuilder{
/**
* Override getModels so it send attributes to the newInstance methods (in newExisting)
*/
public function getModels($columns = array('*'))
{
// First, we will simply get the raw results from the query builders which we
// can use to populate an array with Eloquent models. We will pass columns
// that should be selected as well, which are typically just everything.
$results = $this->query->get($columns);
$connection = $this->model->getConnectionName();
$models = array();
// Once we have the results, we can spin through them and instantiate a fresh
// model instance for each records we retrieved from the database. We will
// also set the proper connection name for the model after we create it.
foreach ($results as $result)
{
$models[] = $model = $this->model->newExisting((array) $result);
$model->setConnection($connection);
}
return $models;
}
}
<?php
// migration file
use Illuminate\Database\Migrations\Migration;
class CreateProductsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function($table)
{
$table->increments('id');
$table->string('title');
$table->string('product_type');
$table->timestamps();
$table->index('product_type');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('products');
}
}
<?php namespace App;
use App\Product as BaseProduct;
class Merchandise extends BaseProduct {
}
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Builder;
class Product extends Model {
protected $table = 'products';
protected $classField = 'product_type';
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
$this->{$this->classField} = get_class($this);
}
public function newInstance($attributes = array(), $exists = false)
{
if($this->classField && isset($attributes[$this->classField])){
$classname = $attributes[$this->classField];
$model = new $classname;
}else{
// May never be use / should not be use in this case, as it should never create a Product!
$model = new static;
}
/**
* Changed this part, so it use fill or setAttributes, if it come from an existing class or not, not directly via construct (which use fill)
*/
if($exists){
$model->setAttributes($attributes);
}else{
$model->fill($attributes);
}
$model->exists = $exists;
return $model;
}
/**
* Override newQuery to use own Builder and add where automaticaly if not base class
*/
public function newQuery()
{
$builder = new Builder($this->newBaseQueryBuilder());
$builder->setModel($this);
if($this->classField && get_class(new static) !== get_class(new self)){
$builder->where($this->classField, get_class($this));
}
return $builder;
}
}
<?php
// seed file
return array(
array(
'title' => 'Book 1',
'product_type' => 'Brainr\Model\Publication',
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
array(
'title' => 'Magazine 2',
'product_type' => 'Brainr\Model\Publication',
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
array(
'title' => 'Table 1',
'product_type' => 'Brainr\Model\Merchandise',
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
array(
'title' => 'Chair 1',
'product_type' => 'Brainr\Model\Merchandise',
'created_at' => new DateTime,
'updated_at' => new DateTime,
),
<?php namespace App;
use App\Product as BaseProduct;
class Publication extends BaseProduct {
}
@elimentz
Copy link

elimentz commented Apr 6, 2013

Can you elaborate on the table structure of both Publication and Merchandise? Do they each have their own separate database table custom attributes or do you keep the attributes of both models in one single Products table?

@Cosmicist
Copy link

@elimentz it's single table inheritance which means it's only one table.

@asakurayoh any idea about concrete table inheritance? I desperately need this, but couldn't find a way to do it.

@intrip
Copy link

intrip commented Jan 6, 2014

i've made a package for laravel 4+ to handle that: https://github.com/intrip/laravel-single-table-inheritance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment