Рубрики
Без рубрики

CGPA Calculator с Adonisjs: Crud и отношения

Если вы хотите узнать, как создавать API, используя Adonisjs в моде на основе проекта, ваше желание было выполнено. Учитесь создавать реальный API (CGPA Calculator) с использованием Adonisjs.

Автор оригинала: Osinachi Chukwujama.

Привет, добро пожаловать на третий учебник в серии. В Предыдущее руководство , мы добавили аутентификацию нашим API. Если вам понравится пропустить предыдущие шаги, клонировать репо и оформить заказ к Аутентификация Филиал, затем код вместе.

Модель курса

Модель курса имеет 8 полей с 2 метками времени. Чтобы добавить таблицу курса в нашу базу данных, мы

Курс-модель.png.png
  1. Создайте модель курса с миграциями
  2. Измените файл миграции, чтобы включить необходимые поля
  3. Запустить миграцию

Создание модели курса с миграциями

adonis make:model Course -m

# Output
# ✔ create  app/Models/Course.js
# ✔ create  database/migrations/1606416525019_course_schema.js

Флаг -m создает файл миграции.

Установка необходимых полей

Откройте вновь созданный курс aschema и добавьте этот код

      table.string("title").nullable();
      table.string("code").notNullable();
      table.enu("grade", ["A", "B", "C", "D", "F"]).notNullable();
      table.integer("credit_load").unsigned().notNullable();
      table.enu("semester", [1, 2, 3, 4]).notNullable();
      table
        .enu("level", [100, 200, 300, 400, 500, 600, 700, 800, 900])
        .notNullable();
      table
        .integer("user_id")
        .unsigned()
        .nullable()
        .references("id")
        .inTable("users")
        .onUpdate("CASCADE")
        .onDelete("CASCADE");

Обратите внимание, что класс и уровень реализованы с Table.enu Отказ Enum только позволяет определять значения. Обратитесь к Knex.js Документы для получения дополнительной информации о данных DataTypes. Последний столбец, user_id – это внешний ключ.

      table
        .integer("user_id")
        .unsigned()
        .nullable()
        .references("id")
        .inTable("users")
        .onUpdate("CASCADE")
        .onDelete("CASCADE");

Обратите внимание на Onupdate («каскад») и OnDelete («каскад») прикрепил? Это означает, что изменения в пользователю будут здесь отражены. Если пользователь удален, курсы с их идентификатором также удаляются.

Продолжай и запустите миграцию.

adonis migration:run

Курс Crud Marross

Если бы мы создадим индивидуальные маршруты для CRUD, мы получим> 4 маршрута.

Route.get('course', 'CourseController.index')
Route.post('course', 'CourseController.store')
Route.get('course/:id', 'CourseController.show')
Route.put('course/:id', 'CourseController.update')
Route.delete('course/:id', 'CourseController.destroy')

Adonis дает нам более чистый способ обрабатывать такую маршруту: Маршрутные ресурсы ! Добавьте эту строку на маршруты.

  Route.resource("courses", "CourseController").apiOnly().middleware(["auth"]);

Обратитесь к Документы Для получения дополнительной информации о избивообразных и маршрутных ресурсах в целом.

Контроллер курса

Мы добавим логику для пяти способов выше: индекс, хранить, шоу, обновление и уничтожение. Но сначала давайте импортировать необходимое.

const Course = use("App/Models/Course");
const { validateAll } = use("Validator");

Метод магазина

Добавьте метод магазина ниже в Coursecontroller.js.

  async store({ auth, request, response }) {
    try {
      const {
        title,
        grade,
        code,
        credit_load,
        semester,
        level,
      } = request.all();

      const rules = {
        title: "required",
        grade: "required|in:A,B,C,D,F",
        code: "required",
        credit_load: "required|integer",
        semester: "required|in:1,2",
        level: "required|in:100,200,300,400,500,600,700,800,900",
      };

      const validation = await validateAll(request.all(), rules);

      if (validation.fails()) {
        return response.status(400).send(validation.messages());
      }

      const course = await Course.create({
        title,
        grade,
        code,
        credit_load,
        semester,
        level
      });

      const user = await auth.getUser();
      await user.courses().save(course);

      return response.status(201).send(course);
    } catch (error) {
      console.log(error);
      return response.status(500).send(error);
    }
  }

Если вы сравните правила и даты данных, определенные в миграции, вы обнаружите сходство между ними. Если мы не используем Validator, база данных бросит ошибки, когда мы пробуем вставки или обновления с неправильными данными. Но лучше всего подтвердить данные на уровне приложений.

Очень интересная часть метода магазина заключается в том, как обращаются отношения. Поскольку пользователь и курсы разделяют отношения 1: N, нам нужно сообщить базу данных, чтобы установить внешний ключ для каждого курса. Мы делаем это, используя

await user.courses().save(course);

В нашем пользователе Lucid Model мы определим метод курсов, как так

  courses() {
    return this.hasMany("App/Models/Course");
  }

Этот метод возвращает связующее отношение, связанное с курсом. Поэтому, когда метод сохранения вызывается на этот метод, идентификатор пользователя будет вставлен в user_id поле курса в базе данных. Это может показаться запутанным вначале, но чтобы лучше понять его, нам нужно создать курсы.

Создание курсов

Откройте свое любимое приложение для тестирования API и вызовите этот маршрут Post /API/V1/Курсы Отказ Убедитесь, что у вас есть правый набор JWT. Вы можете использовать JSON ниже для вашего запроса

{
    "title": "Metals and Non metals",
    "grade": "A",
    "code": "MNM 304",
    "credit_load": "4",
    "semester": "2",
    "level": "100"
}

Вы должны получить ответ, похожий на

{
    "title": "Metals and Non metals",
    "grade": "A",
    "code": "MNM 304",
    "credit_load": "4",
    "semester": "2",
    "level": "100",
    "created_at": "2020-11-27 00:46:00",
    "updated_at": "2020-11-27 00:46:00",
    "id": 4,
    "user_id": 1
}

Вы можете идти вперед и предотвратить Create_at , updated_at и user_id Поля от отображения, добавляя это в модель курса.

  static get hidden() {
    return ["created_at", "updated_at", "user_id"];
  }

Возвращая созданные курсы

Единый курс

Чтобы вернуть один курс, мы будем использовать параметр запроса на идентификатор курса.

  async show({ auth, params, response }) {
    try {
      const course = await Course.find(params.id);

      if (!course) {
        return response.status(404).send("Course not found");
      }

      const courseOwner = await course.user().fetch();
      const requester = await auth.user;
      if (requester.id !== courseOwner.id) {
        return response
          .status(403)
          .send("You cannot view another user's course");
      }
      return response.status(200).send(course);
    } catch (error) {
      return response.status(500).send(error);
    }
  }

Во-первых, мы находим курс, который соответствует данному идентификатору. Если такой курс не существует, мы возвращаем 404. Мы получаем владелец курса, позвонив Пользователь () Способ на курсе, ясно. Помните, как мы использовали Hasmany Когда мы определили отношения между пользователем и курсом? Теперь мы будем использовать Приятно определить обратные отношения. Добавьте пользовательский метод ниже на свою модель курса.

user() {
    return this.belongsTo("App/Models/User");
}

Вернуться к Показать метод. Мы проверяем, что идентификатор владельца курса соответствует пользователю, позвонив на маршрут. Если это не так, мы вернем 403, иначе мы возвращаем курс. Ниже приведена попытка вернуть один курс.

Get-Single-roub.png

Несколько курсов

Чтобы вернуть несколько курсов, мы используем метод индекса.

  async index({ auth, request, response }) {
    try {
      const { semester, level } = request.all();

      const rules = {
        semester: "in:1,2",
        level: "in:100,200,300,400,500,600,700,800,900",
      };

      const validation = await validateAll(request.all(), rules);

      if (validation.fails()) {
        return response.status(400).send(validation.messages());
      }

      const user_id = await auth.user.id;

      let courses;

      if (level && !semester) {
        courses = await Course.query()
          .where({ user_id: user_id, level: level })
          .fetch();
      } else if (level && semester) {
        courses = await Course.query()
          .where({ user_id: user_id, level: level, semester: semester })
          .fetch();
      } else {
        courses = await Course.query().where("user_id", user_id).fetch();
      }

      return response.status(200).send(courses);
    } catch (error) {
      console.log(error);
      return response.status(500).send(error);
    }
  }

Пользователь, вызывающий этот API, имеет три варианта

  1. /Курсы возвращают все курсы пользователя
  2. /Курсы? Уровень = 300 Возвращает все курсы 300 уровней пользователя
  3. /Курсы? Уровень = 100 и семестр = 1 Возвращает все 300 уровня пользователя, 1-й семестрные курсы. Логика для определения правильного запроса для выполнения
      let courses;

      if (level && !semester) {
        courses = await Course.query()
          .where({ user_id: user_id, level: level })
          .fetch();
      } else if (level && semester) {
        courses = await Course.query()
          .where({ user_id: user_id, level: level, semester: semester })
          .fetch();
      } else {
        courses = await Course.query().where("user_id", user_id).fetch();
      }

Обратите внимание, что мы использовали привлечение при использовании где пункт абстракции. Вы можете узнать больше о запросах от Документы Отказ

Обновления курса с использованием метода обновления

Обновление курса очень похоже на создание одного. Мы делаем каждое поле требуемому и найти курс с параметров. Затем мы проверяем, принадлежит ли курс пользователю, запрашиваемую его. Если все пойдет хорошо, мы обновляем поля курса и сохраняем его. Ниже приведен неполный метод обновления. Ваша работа – завершить его. Не стесняйтесь модифицировать его, если вы думаете о лучшей реализации.

  async update({ auth, params, request, response }) {
    try {

      // your code above

      const courseOwner = await course.user().fetch();
      const requester = await auth.user;
      if (requester.id !== courseOwner.id) {
        return response
          .status(403)
          .send("You cannot view another user's course");
      }

      course.title = title;
      course.grade = grade;
      course.code = code;
      course.credit_load = credit_load;
      course.semester = semester;
      course.level = level;

      await course.save();
      return response.status(200).send(course);
    } catch (error) {
      console.log(error);
      return response.status(500).send(error);
    }
  }

Обратите внимание, что мы использовали ту же логику, чтобы убедиться, что пользователь, создающий запрос, является владельцем курса. Мы можем рефакторировать эту логику для этого метода.

  async isOwner({ auth, course, response }) {
    const courseOwner = await course.user().fetch();
    const requester = await auth.user;
    if (requester.id !== courseOwner.id) {
      throw new Error("You cannot view another user's course");
    }
  }

Теперь, чтобы называть это, мы делаем

      try {
        await this.isOwner({ auth, course, response });
      } catch (error) {
        return response
          .status(403)
          .send("You cannot  another user's course");
      }

Вы можете найти его иронично, что рефакторист добавил больше кода в кодовую базу. Хорошей частью этого является то, что мы можем легко изменить нашу логику в одном месте.

Удаление курса

Удаление курса, вероятно, самый простой здесь. Это только включает проверку и удаление.

  async destroy({ auth, params, response }) {
    try {
      const course = await Course.find(params.id);
      if (!course) {
        return response.status(404).send("Course not found");
      }
      try {
        await this.isOwner({ auth, course, response });
      } catch (error) {
        return response
          .status(403)
          .send("You cannot delete another user's course");
      }
      await course.delete();
      return response.send("course deleted successfully");
    } catch (error) {
      return response.status(500).send("An error occured");
    }
  }

Если вы заметите, учитывающие модели упрощают операции БД. Удаление просто курс. вместо Удалить из курсов, где ID = Отказ

Что вы узнали до сих пор

  1. Генерирующие модели и миграции
  2. Cascade обновление и удаление
  3. Маршрутные ресурсы
  4. Простой способ обрабатывать авторизацию

Следующие шаги

В следующем уроке мы рассмотрим 1: 1 отношения и как мы можем селить нашу базу данных. Спасибо за следующее вместе. Adios ✌🏾🧡.