Inheritance, extending a model

Hi,

This is a very general question and I’m hoping someone can enlighten me as to the possible approaches to this situation within the context of Yii. I’m not exactly sure how to explain this, so please bear with me. Any thoughts would be appreciated.

Here’s the situation: I’m building a small app for students to publish portfolios.

To do this, I need to have Courses and Users. But there are two types of Users, Students and Teachers.

When I first started this, I made a single database table (User) with an IS_STUDENT boolean field. But within the application, I don’t want people to think about “Users”. I want to treat Teachers and Students like totally different object types.

I could create a separate database table for each (Teacher & Student) and CRUD them entirely separately. But then I run into the problem of logging in, where I don’t know what table to look in to match their credentials (I’m thinking of the UserIdentity.php file).

So now I’m contemplating how much sense it makes to have basically twice as many methods in the User model class (eg. actionCreateStudent, actionCreateTeacher, actionListStudents, actionViewTeacher, etc). I am also questioning whether to create double the amount of view scripts (viewTeacher.php, viewStudent.php, _formStudent.php) or just pass some kind of IS_STUDENT variable to the view.php file and change labels and various other items within the same script file.

I’m sorry for just dumping all this out there, but I’m really hoping to learn something from this. I mention inheritance in the subject because I feel like if I was doing a traditional application, I would be definitely make use of this, with an abstract User class leading to a Student and a Teacher class. This would make perfect sense there, but within the context of a database-driven web app and Yii, I’m not sure what makes the most sense.

Like I said, I look forward to any ideas as to the best practices in this situation.

Thanks a lot guys,

Jaz

Unless you don’t have many different/additional columns for each user type, I think your first approach makes most sense. Of course you can use different views for each type (_formStudent.php). Also I would simply use one action for both types. Within the action you can check for $_GET[‘userType’].

What are your concerns about this? I think it would be overkill to completely seperate the user types.

IS_STUDENT looks better for me. You can probably create several AR models using a single table but different default AR scopes like IS_STUDENT = 1 or IS_STUDENT = 0.

Hey guys, thanks for the replies.

@Y!! I guess my idea for separating these more would be to do something like Teacher::model()->findAll() as opposed to User::model()->findAllByAttributes(array(‘is_student’=>0)). And, at some point, they might want some different fields in the records for Teachers versus Students. I’d end up with a _form.php that has to do all these checks to see whether or not to include a particular field. Or, in the action that receives the form post, I might process a field a certain way if it’s one type or the other. So, it would be kinda nice to see more of a clean separation between these elements, while at the same time maintaining a single class for processing logins and srbac.

@samdark How would multiple AR models work with regard to logging in (components/UserIndentity.php) and something like SRBAC? Are you suggesting that a sort of inheritance could be possible here with like three ARs? User extending AR, Student and Teacher extending User?

Create 3 classes: User, Teacher and Student. Two last extend User. Then take a look at this method: http://www.yiiframework.com/doc/api/CActiveRecord#instantiate-detail . You can override it in the User class to create instances depending on IS_STUDENT value so that you can call User::model()->findAll() to get a set of Student and Teacher objects.

As samdark said, you’ll need to add default scopes to Teacher and Students models (‘condition’=>‘is_student=…’).

You can use User (without defaultScope) for UserIndentity and Student + Teacher for views and managing these. Extending ARs with overriding defaultScope should work fine. Of course, it’s worth it if Student and Teacher are too different to be used as one entity.

andy_s’s variant with single table inheritance is even better.

Just wanted to update progress. Things are going well.

Starting with a typical "Class User extends CActiveRecord", I now have two other classes which extend User ("Class Student extends User"). I needed to override two methods to make this work properly.




//in Class Student

public function defaultScope(){

 return array(

  'condition'=>'is_student = 1',

 );

}

public static function model($className=__CLASS__){

 return parent::model($className);

}



When using CActiveDataProvider, all I needed was the proper defaultScope method to be defined. So I was confused when Student::model()->findAll() wasn’t making use of defaultScope. I realized I needed to override the static model function as well in order for Student::model()->findAll() to work. Before I added model(), it was using the method in the User class, returning ‘User’ and, hence, all results. Now that I’ve got these two items sorted out, things are falling into place.

One last note…




//By using the instantiate method in the User class as follows:

protected function instantiate($attributes){

 return $attributes['is_student'] ? new Student(null) : new Teacher(null);

}


//Then when I grab all the users, they exist as the correct type.. the following code would show this

foreach( User::model()->findAll() as $user ) echo ($user instanceof Student) ? 'Student' : 'Teacher';



I hope this is of as much use to someone else as it has been to me. Thanks for all the help.

I am currently working on an app for a school where I have to deal with a scenario similar to yours.

It’s a simple case of generalization/specialization so this is my approach.

This is my approach.

I have 3 tables for this g/s. The general table person has all fields that are common to students and teachers then i have a table for student that takes the person primary key as it’s primary key and the same for teacher.

both teacher and student have the fields that are only specific to them.

For my app, a teacher can also be a student.

Yeah, normally I would take an approach like this. I didn’t in this case for a couple reasons. 1) They just want a really simple app done really fast. 2) I just got curious as to how this sort of single-table inheritance would work, so I’m gonna go for it as a learning experience in case I ever have a more important occasion to make use of it.

Must have missed this post. Currently i’m facing a similar problem: Inheritance with shared attributes in the base model and additional different attributes per child model. Since the number of different child models can vary, i tried to come up with a very dynamic solution.

I’ve posted some idea here (which i’m not happy with) and would welcome any feedback:

http://www.yiiframework.com/forum/index.php?/topic/12775-unified-relational-model-into-one/page__view__findpost__p__63052

But maybe the approach with one table for each chilld’s additional attributes is much cleaner/easier to implement.