Skip to content

Instantly share code, notes, and snippets.

@andylolz
Created October 14, 2012 23:36
Show Gist options
  • Select an option

  • Save andylolz/3890165 to your computer and use it in GitHub Desktop.

Select an option

Save andylolz/3890165 to your computer and use it in GitHub Desktop.
Fun extending the User model. This is my solution to the problem I posted here: http://gist.github.com/3885906 Thanks to Kindari on laravel IRC
<?php
class Student extends User {
public function reports() {
// I know I said the relationship here is one-to-one..!
// But I don't mind hacking this too much.
return $this->has_many('Report', 'student_id');
}
}
<?php
class Teacher extends User {
public function reports() {
return $this->has_many('Report', 'teacher_id');
}
}
<?php
class User extends Eloquent{
// Sets the table name for this model. The default table name
// is the plural of the model name, so this is as default.
//
// However, it is still required to be defined here because
// of all the subclassing (otherwise Eloquent would start
// looking for tables with the names of the subclasses)
public static $table = 'users';
// Defines all of the properties that should be saved to the
// database. Note that type_id is ommitted from here, as it
// is just a handy variable we keep around but don't want saved.
//
// Though it's not required, it is still worth defining $accessible
// for all classes, as it helps keep track of the current database
// schema.
public static $accessible = array(
'email',
'first_name',
'last_name',
'password',
'status',
'user_type_id'
);
/**** BEGIN EXTRA-PRICKLY CODE! ****/
// Array to store mappings between subclass names and their
// relevant user type IDs.
//
// This isn't really necessary - it's here as a per-request
// cache, to avoid repeated calls to the UserType table
public static $type_id = array();
// Override the default constructor
public function __construct($attributes = array(), $exists = false) {
if(is_subclass_of($this, 'User')) {
$subclass = get_class($this);
// Get the relevant user type ID for this subclass, and
// store it in our per-request cache (the $type_id array)
if(!array_key_exists($subclass, self::$type_id)) {
self::$type_id[$subclass] = UserType::where_desc($subclass)->first()->id;
}
// We can infer the user_type_id of the subclass object,
// so let's not require it to be specified when a new
// instance is created.
$attributes['user_type_id'] = self::$type_id[$subclass];
}
parent::__construct($attributes, $exists);
}
// Override the default attribute setter
//
// We just need to do this to prevent the user type of a subclass
// object being changed
public function set_attribute($key, $value) {
if($key == 'user_type_id' && is_subclass_of($this, 'User')) {
$subclass = get_class($this);
// Note that type_id[subclass] is set in the constructor, so we can
// guarantee we have a key value
if($value != self::$type_id[$subclass]) {
throw new Exception("Can't set the user type of a User subclass.");
}
}
parent::set_attribute($key, $value);
}
// Override the default query
//
// We append a WHERE clause to all queries to subclasses, specifying the
// relevant user type ID. This is probably the most important bit.
public function query() {
if(is_subclass_of($this, 'User')) {
return parent::query()->where('user_type_id', '=', self::$type_id[get_class($this)]);
}
return parent::query();
}
// Downcast a User object to one of its subclasses
public static function construct_from_user($user) {
// Construct a new instance of the subclass
$user_sub = new static;
// One quick integrity constraint to ensure we're not
// downcasting a user to an incompatible type
if($user_sub->user_type_id && $user_sub->user_type_id != $user->user_type_id) {
$err_msg = "That user is not of type " . get_class($user_sub) . ".";
throw new Exception($err_msg);
}
// Ok, now just copy all the object properties over
foreach($user as $k => $v) {
$user_sub->$k = $v;
}
return $user_sub;
}
/**** END EXTRA-PRICKLY CODE! ****/
public function user_type() {
return $this->belongs_to('UserType', 'user_type_id');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment