added students to course

This commit is contained in:
Sergey Yarkov 2021-07-31 21:16:44 +03:00
parent a96ad1e7b5
commit a9715971e4
8 changed files with 143 additions and 8 deletions

View File

@ -2,7 +2,9 @@ import { Exception } from '@adonisjs/core/build/standalone';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'; import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import Course from 'App/Models/Course'; import Course from 'App/Models/Course';
import User from 'App/Models/User'; import User from 'App/Models/User';
import AddCourseToUserValidator from 'App/Validators/AddCourseToUserValidator';
import CreateCourseValidator from 'App/Validators/CreateCourseValidator'; import CreateCourseValidator from 'App/Validators/CreateCourseValidator';
import DelCourseFromUserValidator from 'App/Validators/DelCourseFromUserValidator';
import UpdateCourseValidator from 'App/Validators/UpdateCourseValidator'; import UpdateCourseValidator from 'App/Validators/UpdateCourseValidator';
export default class CoursesController { export default class CoursesController {
@ -95,8 +97,16 @@ export default class CoursesController {
/** /**
* Show Students of Course with "id" * Show Students of Course with "id"
* GET /courses/:id/students
*/ */
public async showStudents({ response, params }: HttpContextContract) {} public async showStudents({ response, params }: HttpContextContract) {
const course = await this.Course.query().preload('students').where('id', params.id).firstOrFail();
return response.ok({
message: `Fetched students from course with id: "${course.id}"`,
data: course.students,
});
}
/** /**
* Create new Course in a system * Create new Course in a system
@ -178,4 +188,58 @@ export default class CoursesController {
data: course, data: course,
}); });
} }
/**
* Attach student to course
* POST /courses/:id/attach-student
*/
async attachStudent({ response, request, params }: HttpContextContract) {
try {
const payload = await request.validate(AddCourseToUserValidator);
const student = await this.User.findOrFail(payload.student_id);
const course = await this.Course.findOrFail(params.id);
await course.load('students');
await student.load('roles');
const studentRoles = student.roles.map(r => r.slug);
if (!studentRoles.includes('student')) {
throw new Exception(`User cannot be attached to the course without "student" role.`, 400, 'E_USER_ROLE');
}
await course.related('students').attach([student.id]);
return response.ok({
message: 'Student attached',
data: 'SUCCESS',
});
} catch (error) {
if (error?.code === '23505')
throw new Exception('Student already attached to that course', 400, 'E_STUDENT_ATTACHED');
throw error;
}
}
/**
* Detach student from course
*/
async detachStudent({ response, request, params }: HttpContextContract) {
const payload = await request.validate(DelCourseFromUserValidator);
const course = await this.Course.query().preload('students').where('id', params.id).firstOrFail();
const candidate = course.students.find(student => student.id === payload.student_id);
if (!candidate) {
throw new Exception('Student not attached to that course', 400, 'E_STUDENT_NOT_ATTACHED');
}
const student = await this.User.findOrFail(payload.student_id);
await course.related('students').detach([student.id]);
return response.ok({
message: 'Student detached',
data: 'SUCCESS',
});
}
} }

View File

@ -122,7 +122,7 @@ export default class UsersController {
/** /**
* Attach array of roles to user * Attach array of roles to user
* POST /users/:id/attach_roles * POST /users/:id/attach-roles
*/ */
public async attachRoles({ request, response, params }: HttpContextContract) { public async attachRoles({ request, response, params }: HttpContextContract) {
await request.validate(AddRoleToUserValidator); await request.validate(AddRoleToUserValidator);
@ -141,7 +141,7 @@ export default class UsersController {
/** /**
* Detach array of roles from user * Detach array of roles from user
* DELETE /users/:id/detach_roles * DELETE /users/:id/detach-roles
*/ */
public async detachRoles({ request, response, params }: HttpContextContract) { public async detachRoles({ request, response, params }: HttpContextContract) {
await request.validate(DelRoleFromUserValidator); await request.validate(DelRoleFromUserValidator);

View File

@ -1,10 +1,20 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { BaseModel, beforeCreate, BelongsTo, belongsTo, column, HasMany, hasMany } from '@ioc:Adonis/Lucid/Orm'; import {
import Category from './Category'; BaseModel,
import Lesson from './Lesson'; beforeCreate,
import User from './User'; BelongsTo,
belongsTo,
column,
HasMany,
hasMany,
ManyToMany,
manyToMany,
} from '@ioc:Adonis/Lucid/Orm';
import Category from 'App/Models/Category';
import Lesson from 'App/Models/Lesson';
import User from 'App/Models/User';
// eslint-disable-next-line import/no-cycle // eslint-disable-next-line import/no-cycle
export default class Course extends BaseModel { export default class Course extends BaseModel {
@ -38,6 +48,11 @@ export default class Course extends BaseModel {
}) })
public lessons: HasMany<typeof Lesson>; public lessons: HasMany<typeof Lesson>;
@manyToMany(() => User, {
pivotTable: 'users_courses',
})
public students: ManyToMany<typeof User>;
@column.dateTime({ autoCreate: true }) @column.dateTime({ autoCreate: true })
public createdAt: DateTime; public createdAt: DateTime;

View File

@ -16,6 +16,7 @@ import Hash from '@ioc:Adonis/Core/Hash';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import Role from 'App/Models/Role'; import Role from 'App/Models/Role';
import Contact from 'App/Models/Contact'; import Contact from 'App/Models/Contact';
import Course from 'App/Models/Course';
export default class User extends BaseModel { export default class User extends BaseModel {
@column({ isPrimary: true }) @column({ isPrimary: true })
@ -43,6 +44,11 @@ export default class User extends BaseModel {
}) })
public roles: ManyToMany<typeof Role>; public roles: ManyToMany<typeof Role>;
@manyToMany(() => Course, {
pivotTable: 'users_courses',
})
public courses: ManyToMany<typeof Course>;
@column.dateTime({ autoCreate: true, serializeAs: null }) @column.dateTime({ autoCreate: true, serializeAs: null })
public createdAt: DateTime; public createdAt: DateTime;

View File

@ -0,0 +1,12 @@
import { schema, rules } from '@ioc:Adonis/Core/Validator';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
export default class AddCourseToUserValidator {
constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({
student_id: schema.string({}, [rules.exists({ table: 'users', column: 'id' })]),
});
public messages = {};
}

View File

@ -0,0 +1,12 @@
import { schema, rules } from '@ioc:Adonis/Core/Validator';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
export default class DelCourseFromUserValidator {
constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({
student_id: schema.string({}, [rules.exists({ table: 'users', column: 'id' })]),
});
public messages = {};
}

View File

@ -0,0 +1,18 @@
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
export default class UsersCourses extends BaseSchema {
protected tableName = 'users_courses';
public async up() {
this.schema.createTable(this.tableName, table => {
table.increments('id');
table.string('user_id', 21).unsigned().references('users.id').onDelete('CASCADE');
table.string('course_id', 21).unsigned().references('courses.id').onDelete('CASCADE');
table.unique(['user_id', 'course_id']);
});
}
public async down() {
this.schema.dropTable(this.tableName);
}
}

View File

@ -72,9 +72,17 @@ Route.group(() => {
.middleware('role:admin,teacher,student') .middleware('role:admin,teacher,student')
.as('showCourseLessons'); .as('showCourseLessons');
Route.get('courses/:id/students', 'CourseController.showStudents') Route.get('courses/:id/students', 'CoursesController.showStudents')
.middleware('role:admin,teacher,student') .middleware('role:admin,teacher,student')
.as('showCourseStudents'); .as('showCourseStudents');
Route.post('courses/:id/attach-student', 'CoursesController.attachStudent')
.middleware('role:admin,teacher')
.as('attachUserCourse');
Route.delete('courses/:id/detach-student', 'CoursesController.detachStudent')
.middleware('role:admin,teacher')
.as('detachUserCourse');
}).middleware('auth'); }).middleware('auth');
}) })
.prefix('/api/v1') .prefix('/api/v1')