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 {
}
@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