Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Tangaye's avatar

Login authentication for user, guardian and teacher.

My system requires login for user, teacher, and guardian. I did a little bit of research on the topic and lot of folks talk about utilizing the user type feature. But I don't think that will work for me or maybe I don't understand how to make it work that way.

One thing I'm concern about when I'm creating a teacher(something only the admin should do) is that a teacher can teach multiple subjects and classes. So, in short a many to many relationship exists between teacher and subject. Whenever I inserting a teacher I have to specify the class(es) he/she teaches. Also a guardian is related to many students, and whenever I'm inserting a guardian I have to specify the student(s) the guardian is related to.

I could create separate migrations, models controllers, and views for all those users(admins, guardians, staffs and teachers) but something tells me that wouldn't be an efficient solution to the problem. What are others alternatives to addressing the issue.

What are others alternatives to addressing this issue, wherein I can have a single login that is use to authenticate different kinds of users and returns views that are specific to user kind.

Is there a way that when the administrator is inserting teacher and guardians a user account be generated for them? Thanks for the attention!

0 likes
14 replies
IgorBabko's avatar

Hello -

As I understand user, teacher, guardian models will have different relationships with other models, such as teachers will have many subjects and classes, students will belong to guardian, etc... and all those three types of users should have different fields in DB tables.

To avoid multi auth you still can have only one User auth model with some kind of a proxy relationship ->data() that will return particular user - weather teacher, guardian or student.

Here's how you can accomplish this:

This will be your user migration ( added column called type that will store fully qualified path to the particular user model class ).

$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->string('type')->default(\App\Teacher::class);
$table->rememberToken();
$table->timestamps();

All involved models with set relationships:

class User extends Model
{
    public function data()
    {
        return $this->hasOne($this->type);
    }
}

class Teacher extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function subjects()
    {
        return $this->belongsToMany(Subject::class);
    }
}

class Student extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function guardian()
    {
        return $this->belongsTo(Guardian::class);
    }
}

class Guardian extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function students()
    {
        return $this->hasMany(Student::class);
    }
}

And an example of how you'll use that kind of setup:

$teacher = new User(['type' => Teacher::class]);

$teacher->data()->create([
    'teacher_name' => 'john doe',
    // ...
    // some other properties related specifically to teacher
]);

$teacher->data()->subjects()->createMany([
    new Subject(),
    new Subject(),
    new Subject(),
]);

$guardian = new User(['type' => Guardian::class]);

$guardian->data()->create([
    'guardian_name' => 'jane doe',
    // ...
    // some onther properties related specifically to guardians
]);

$guardian->data()->students()->createMany([
    new User(['type' => Student::class]),
    new User(['type' => Student::class]),
    new User(['type' => Student::class]),
]);
1 like
Snapey's avatar

For simplicity I would definately recommend that anyone that needs to login is classed as a User

You can then still have tables related to Student guardian and teacher which are linked to a User with a one to one relationship

Just watch out for the fact that a teacher could also be a guardian and if your database is around long enough, in time a student can become a guardian or teacher

In terms of adding users don't get confused thinking you need to 'register' them... The User model is no different to any other model you can create new entries by just using User::create() for instance

Tangaye's avatar

@IgorBabko assuming that I adopt your suggestion. You saying that "the type column will store the fully qualified path to the particular user model class. " I'm a bit confuse because I have no experience on how this is going to work. Take for instance in my form will the type be a drop down list of all the user classes? If so, how do one know that a specific guardian user account is being created given that the type class selected is guardian.

Also is it require that I add a default class path to the type column? I know my question my look a bit dumb, but the only part of your answer I'm understanding is the model and the migration. I know you're doing your best to help me but I'm kind of a novice to the knowledge you're sharing now. Will appreciate it if you can patiently walk me through the solution. Thanks!

Tangaye's avatar

@Snapey for now I don't fully understand where you're driving at, but please keep in mind that the system requires that I render different views for teachers and guardians.

IgorBabko's avatar
Level 36

@Tangaye -

Also is it require that I add a default class path to the type column? - Not required.

Take for instance in my form will the type be a drop down list of all the user classes? - correct

Let's say your select element will be as follows:

<select name="type">
    <option value="\App\Teacher">Teacher</option>
    <option value="\App\Student">Student</option>
    <option value="\App\Guardian">Guardian</option>
</select>

When you're gonna create a user after sending the form, you'll fetch the value of the type request variable:

$user = new User(['type' => request('type')]);

After this $user->type will be equal to whether \App\Teacher, \App\Student or \App\Guardian ( whatever value is selected by the user ).

Let's say the user is selected \App\Teacher and $user->type is equal to \App\Teacher.

Next here comes this part -

class User extends Model
{
    public function data()
    {
        return $this->hasOne($this->type);
    }
}

When you refer to data relationship, it's gonna dynamically determine which relationship to use based off $this->type value ( in our case it's \App\Teacher ), so after substitution it will look like this:

class User extends Model
{
    public function data()
    {
        return $this->hasOne('\App\Teacher');
    }
}

and after when we'll reference $user->data()->create(['name' => 'John']);

the system will know that we're trying to create new Teacher model and associate it with current User model.

After this if we will ever refer to $user->data it will give us back an eloquent model of a Teacher.

That's it, does it make sense to you?

Tangaye's avatar

@IgorBabko are you saying that creating a user will also create a teacher/guardian? If so how does this handles assigning multiple subjects or classes to a teacher and multiple students to a guardian. Please take a look on how I know to insert records into a pivot table in this case subject_teacher. Before I ask this question I had in my to have a form for teacher details and at the bottom there are check-boxes of subjects a teacher can teach. for each checkbox that is checked they are send in an array format which makes it possible for me to loop through the array of subject sent and assigned them to the specify teacher.

$teacher = Teacher::create(request(['name', 'gender','address','etc']));

foreach (request(['subject_id']) as $subject){
    //assigned  subject(s) to teacher
   // this will insert records in the pivot table
    $teacher->subject()->attach($subject);
}               

I know this might not fall inline with yours, but how do I adopt what I'm trying to do with yours?

IgorBabko's avatar

@Tangaye -

are you saying that creating a user will also create a teacher/guardian? - yes.

$user = User::create(['type' => '\App\Teacher']);

$user->data()->create(request('name', 'gender','address','etc'));

$user->data()->subjects()->attach(request('subject_ids')); // if request('subject_id') - is an array of IDs.

Case with guardians:

$user = User::create(['type' => '\App\Guardian']);

$user->data()->create(request('name', 'gender','address','etc'));

If relationship between guardians and students is many-to-many then:

$user->data()->students()->attach([1,2,3])); // [1,2,3] - student ids

If relationship between guardians and students is one-to-many then:

$user1 = User::create(['type' => '\App\Student']);
$student1 = $user1->data()->create([ /* student1 properties */ ]);

$user2 = User::create(['type' => '\App\Student']);
$student2 = $user2->data()->create([ /* student2 properties */ ]);

$user3 = User::create(['type' => '\App\Student']);
$student3 = $user3->data()->create([ /* student3 properties */ ]);

$user->data()->students()->saveMany([
    $student1,
    $student2,
    $student3,
]));

But keep in mind that to make all relationships between different user types work properly you should have:

  • user_id column on Teacher, Guardian and Student table;
  • if relationship between guardians and students is many-to-many then create pivot table called guardian_student, but if relationship is one-to-many then add guardian_id column to students table.
1 like
Tangaye's avatar

@IgorBabko things are getting a bit clearer. From what you're suggesting I feel that all table connected to the user table should have the same type of column. Correct me if I'm wrong. Ex:

teacher table

id
firstName
lastName
gender
email
userId

guardian table

id
firstName
lastName
gender
email
userId

But now in the user table there area some columns that just won't be in the tables connected to it, which I see as password and username column.

user table

id
firstName
lastName
gender
email
password

How would you handle such situation?

IgorBabko's avatar

@Tangaye It's not necessary to have same columns on all user type tables. You can just create columns on the User table that will be common for all three user types (teacher, guardian, student), and create specific columns on each of the user types tables which will be unique to the particular user type

Tangaye's avatar

@IgorBabko so you're saying my migrations can look like this:

teacher table

id
userId

guardian table

id
relationship
userId

user table

id
firstName
lastName
gender
username
email
password

If my migration is inline with what you're suggesting then that means whenever I'm creating a user I'll have to check if the admin creating the user specify the type of user. If the type of user for example is guardian I'll now use jquery to append the guardian relationship column to the form.

If the type the admin selects is teacher I`ll append append with jquery also a checkbox list of all subjects and classes.

Now, if what I'm thinking of is the right approach then in my user controller I'll use a lot of if statements like this:

if(request('type') === '\App\Teacher'){
    $user = User::create(['type' => '\App\Teacher']);

    $user->data()->create(request('name', 'gender','address','etc'));

    $user->data()->subjects()->attach(request('subject_ids')); // if 
    request('subject_id') - is an array of IDs.
}

else if (request('type') === '\App\Guardian'){
     $user = User::create(['type' => '\App\Guardian']);

     $user->data()->create(request('name', 'gender','address','relationship'));
}

from what you've been teaching me this is how I envision things to work. Am I getting things wrong? Are there better ways to do it?

ONE LAST THING My user model has these fillable fields:

protected $fillable = [
    'name', 'email', 'password',
];

will I need to update them to this:

protected $fillable = [
    'firstname','lastname','name', 'email', 'password','type',
];

Or should I stick with only fields unique to the user migration:

protected $fillable = [
    'name', 'email', 'password','type',
];
IgorBabko's avatar

@Tangaye

Yeah, you can make all the fields from users table fillable

protected $fillable = [
    'firstname','lastname','name', 'email', 'password','type',
];

I'll correct you a bit in the code below:

    $user = User::create(request('type', 'name', 'gender','address')); // request('type', 'name', 'gender', 'address', 'etc...') - fields that exist on users table

    $user->data()->create(request(/* ... */)); // request(/* ... */) - fields that are specific to a concret user type ( teacher, guardian or student ) 

    $user->data()->subjects()->attach(request('subject_ids'));

I think you got me right in everything else.

1 like
Tangaye's avatar

@IgorBabko I appreciate your help and patience so far. It has been tremendous. But there is something that @snappy share light on that is bothering me. He stated:

Just watch out for the fact that a teacher could also be a guardian and if your database is around long enough, in time a student can become a guardian or teacher

From what he's suggesting it is possible and likely that a teacher can be a guardian also. How do I handle that based on what you've shown me.

I'm thinking I can have something sort of a switch/if statement to check if the user being created is both a teacher and a guardian. If both holds true I perceive doing something like this:

User::create(['type' => '\App\Teacher']);
User::create(['type' => '\App\Guardian']);

But been that a one-to-one relationship attribute has been set up in the user model I'm sure what I'm trying isn't going to work. How would you go about doing this? What are your advises?

IgorBabko's avatar

Hello @Tangaye -

Sorry, I had kinda difficult past month, didn't check the forum at all.

just in case it's still relevant to you, I'll write my implementation down below:

To allow users to have many roles, you could create roles table and leverage one-to-one-polymorphic relationship.

Structure of the roles table:

$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('roleable_id');
$table->string('roleable_type');
$table->timestamps();

You Role model would look like the following:

class Role extends Model
{
    public function roleable()
    {
        return $this->morphTo();
    }
}

And the rest of models are as follows:

class User extends Model
{
    public function roles()
    {
        return $this->hasMany(Role::class);
    }
}

class Teacher extends Model
{
    public function role()
    {
        return $this->morphOne(Role::class, 'roleable');
    }

    public function subjects()
    {
        return $this->belongsToMany(Subject::class);
    }
}

class Student extends Model
{
    public function role()
    {
        return $this->morphOne(Role::class, 'roleable');
    }

    public function guardian()
    {
        return $this->belongsTo(Guardian::class);
    }
}

class Guardian extends Model
{
    public function role()
    {
        return $this->morphOne(Role::class, 'roleable');
    }

    public function students()
    {
        return $this->hasMany(Student::class);
    }
}

You can then get rid of type column on users table as it doesn't serve any purpose anymore.

$user = User::create([ /* users data */ ]);

if ($request->type === '\App\Guardian') {
    $roleable = Guardian::create([ /* guardian data */ ]);
} elseif ($request->type === '\App\Student') {
    $roleable = Student::create([ /* student data */ ]);
} else {
    $roleable = Teacher::create([ /* teacher data */ ]);
}

$role = Role::create([
   'user_id' => $user->id
]);

$role->roleable()->associate($roleable); // many roles also possible
// you just have to create new role and assign it to roleable in the same way

// $anotherRole = Role::create([
//     'user_id' => $user->id
//  ]);

// $teacherRoleable = Teacher::create([ /* teacher data */ ]);

// $anotherRole->roleable()->associate($teacherRoleable);

And then you can work with that implementation like so:

$user = User::first();

// get all user roles
$user->roles;

// get associated roleable ( e.g. Guardian, Student, Teacher models )
foreach ($user->roles as $role) {
     $userRoleable = $role->roleable; // $userRoleable can be Guardian, Student or Teacher
}

// if you have roleable model and want to get the associated user
// it can be accomplished in this way
$student = Student::first();

$student->role->user;

Let me know if you have any questions.

Tangaye's avatar

Thanks for your help. Will appreciate it if you can get me a means(email or social media profile) to get to you. Want you to take a look at the demo when I'm through. Thanks again.

Please or to participate in this conversation.