mirror of
https://github.com/sergeyyarkov/educt-server.git
synced 2026-10-11 10:19:20 +03:00
feat: likes system in courses
This commit is contained in:
parent
d96a54351e
commit
4c1572a9d8
@ -170,6 +170,7 @@ export default class CoursesController extends BaseController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Detach student from course
|
* Detach student from course
|
||||||
|
* DELETE /courses/:id/detach-student
|
||||||
*/
|
*/
|
||||||
public async detachStudent(ctx: HttpContextContract) {
|
public async detachStudent(ctx: HttpContextContract) {
|
||||||
const payload = await ctx.request.validate(DelCourseFromUserValidator);
|
const payload = await ctx.request.validate(DelCourseFromUserValidator);
|
||||||
@ -182,6 +183,10 @@ export default class CoursesController extends BaseController {
|
|||||||
return this.sendResponse(ctx, result.data, result.message, result.status);
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch count of students of course
|
||||||
|
* GET /courses/:id/students/count
|
||||||
|
*/
|
||||||
public async studentsCount(ctx: HttpContextContract) {
|
public async studentsCount(ctx: HttpContextContract) {
|
||||||
const result = await this.courseService.fetchStudentsCount(ctx.params.id);
|
const result = await this.courseService.fetchStudentsCount(ctx.params.id);
|
||||||
|
|
||||||
@ -191,6 +196,34 @@ export default class CoursesController extends BaseController {
|
|||||||
|
|
||||||
return this.sendResponse(ctx, result.data, result.message, result.status);
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set like on course by authorized user
|
||||||
|
* PUT /courses/:id/likes
|
||||||
|
*/
|
||||||
|
public async setLike(ctx: HttpContextContract) {
|
||||||
|
const result = await this.courseService.attachUserLike(ctx.params.id, ctx.auth);
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset like on course by authorized user
|
||||||
|
* DELETE /courses/:id/likes
|
||||||
|
*/
|
||||||
|
public async unsetLike(ctx: HttpContextContract) {
|
||||||
|
const result = await this.courseService.detachUserLike(ctx.params.id, ctx.auth);
|
||||||
|
|
||||||
|
if (!result.success && result.error) {
|
||||||
|
throw new Exception(result.message, result.status, result.error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendResponse(ctx, result.data, result.message, result.status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new Ioc().make(CoursesController);
|
new Ioc().make(CoursesController);
|
||||||
|
|||||||
@ -53,6 +53,16 @@ export default class Course extends BaseModel {
|
|||||||
})
|
})
|
||||||
public students: ManyToMany<typeof User>;
|
public students: ManyToMany<typeof User>;
|
||||||
|
|
||||||
|
@manyToMany(() => User, {
|
||||||
|
pivotTable: 'courses_likes',
|
||||||
|
pivotTimestamps: {
|
||||||
|
createdAt: 'liked_on',
|
||||||
|
updatedAt: false,
|
||||||
|
},
|
||||||
|
pivotColumns: ['liked_on'],
|
||||||
|
})
|
||||||
|
public likes: ManyToMany<typeof User>;
|
||||||
|
|
||||||
@column.dateTime({ autoCreate: true })
|
@column.dateTime({ autoCreate: true })
|
||||||
public createdAt: DateTime;
|
public createdAt: DateTime;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
import { inject, Ioc } from '@adonisjs/core/build/standalone';
|
||||||
|
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
|
||||||
import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
|
import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,13 +8,17 @@ import RoleEnum from 'App/Datatypes/Enums/RoleEnum';
|
|||||||
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
import StatusCodeEnum from 'App/Datatypes/Enums/StatusCodeEnum';
|
||||||
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
import IResponse from 'App/Datatypes/Interfaces/IResponse';
|
||||||
import RoleHelper from 'App/Helpers/RoleHelper';
|
import RoleHelper from 'App/Helpers/RoleHelper';
|
||||||
import CategoryRepository from 'App/Repositories/CategoryRepository';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repositories
|
* Repositories
|
||||||
*/
|
*/
|
||||||
import CourseRepository from 'App/Repositories/CourseRepository';
|
import CourseRepository from 'App/Repositories/CourseRepository';
|
||||||
import UserRepository from 'App/Repositories/UserRepository';
|
import UserRepository from 'App/Repositories/UserRepository';
|
||||||
|
import CategoryRepository from 'App/Repositories/CategoryRepository';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validators
|
||||||
|
*/
|
||||||
import CreateCourseValidator from 'App/Validators/Course/CreateCourseValidator';
|
import CreateCourseValidator from 'App/Validators/Course/CreateCourseValidator';
|
||||||
import UpdateCourseValidator from 'App/Validators/Course/UpdateCourseValidator';
|
import UpdateCourseValidator from 'App/Validators/Course/UpdateCourseValidator';
|
||||||
|
|
||||||
@ -479,6 +484,77 @@ export default class CourseService {
|
|||||||
data: {},
|
data: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async attachUserLike(id: string | number, auth: AuthContract): Promise<any> {
|
||||||
|
const user = await auth.use('api').authenticate();
|
||||||
|
const course = await this.courseRepository.getById(id);
|
||||||
|
|
||||||
|
if (!course) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Course not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await course.related('likes').attach([user.id]);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === '23505') {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.BAD_REQUEST,
|
||||||
|
message: 'You are already liked this course.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_BAD_REQUEST',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Like attached.',
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async detachUserLike(id: string | number, auth: AuthContract): Promise<any> {
|
||||||
|
const user = await auth.use('api').authenticate();
|
||||||
|
const course = await this.courseRepository.getById(id);
|
||||||
|
|
||||||
|
if (!course) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: StatusCodeEnum.NOT_FOUND,
|
||||||
|
message: 'Course not found.',
|
||||||
|
data: {},
|
||||||
|
error: {
|
||||||
|
code: 'E_NOT_FOUND',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await course.related('likes').detach([user.id]);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
status: StatusCodeEnum.OK,
|
||||||
|
message: 'Like detached.',
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new Ioc().make(CourseService);
|
new Ioc().make(CourseService);
|
||||||
|
|||||||
19
database/migrations/1628622517837_courses_likes.ts
Normal file
19
database/migrations/1628622517837_courses_likes.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import BaseSchema from '@ioc:Adonis/Lucid/Schema';
|
||||||
|
|
||||||
|
export default class CoursesLikes extends BaseSchema {
|
||||||
|
protected tableName = 'courses_likes';
|
||||||
|
|
||||||
|
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.timestamp('liked_on', { useTz: true });
|
||||||
|
table.unique(['user_id', 'course_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down() {
|
||||||
|
this.schema.dropTable(this.tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,4 +33,12 @@ Route.group(() => {
|
|||||||
Route.delete('/:id/detach-student', 'Api/v1/CoursesController.detachStudent')
|
Route.delete('/:id/detach-student', 'Api/v1/CoursesController.detachStudent')
|
||||||
.middleware('role:admin,teacher')
|
.middleware('role:admin,teacher')
|
||||||
.as('courses.detach-student');
|
.as('courses.detach-student');
|
||||||
|
|
||||||
|
Route.put('/:id/likes', 'Api/v1/CoursesController.setLike')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('courses.set-like');
|
||||||
|
|
||||||
|
Route.delete('/:id/likes', 'Api/v1/CoursesController.unsetLike')
|
||||||
|
.middleware('role:admin,teacher,student')
|
||||||
|
.as('courses.unset-like');
|
||||||
}).prefix('courses');
|
}).prefix('courses');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user