#6 Модели и контроллеры в Express

poster
В этом уроке мы познакомимся с моделями и контроллерами. Зарефакторим весь наш проект, разбив код по логическим частям.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

Сегодня мы с вами разберем модели и контроллеры в nodejs.

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

В моделях мы будет хранить все методы, которые взаимодействуют с базой. Например мы можем описать модель Track, Artist, Playlist. В нашем случае мы создадим модель Artist у которой будут методы all, findById, add, update, delete.

Давайте добавим папку models и создадим в ней artists.

Добавим метод all, который будет принимать callback, находить в базе записи и возвращать в callback в виде err и docs. Этот метод не знает ничего про сервер, роутинг и API. Он работает только с базой и в коллбек отдает данные. Что это за callback ему без разницы.

var db = require('../db');

exports.all = function (cb) {
  db.get().collection('artists').find().toArray(function (err, docs) {
    cb(err, docs);
  })
}

Теперь давайте добавим папку controllers и файл artists. Давайте добавим метод all, который будет получать функцию с req, res и отдавать данные.

var Artists = require('../models/artists');

exports.all = function (req, res) {
  Artists.all(function (err, docs) {
    if (err) {
      console.log(err);
      return res.sendStatus(500);
    }
    res.send(docs);
  })
}

Мы импортируем в контроллер нашу модель и описываем метод all, который принимает аргументы req, res и вызывает метод модели all. Как вы видите контроллер отвечает за работу с реквестом и респонзом, но он ничего не знает о базе данных и откуда берутся данные. Это очень хорошо, так как позволяет в модели брать данные откуда угодно, не меняя при этом все приложение.

Теперь нам осталось импортировать наш контроллер в server.js

var artistsController = require('./controllers/artists');
app.get('/artists', artistsController.all)

Теперь мы можем упростить наш роутинг до вызова метода контроллера.

Давайте точно так же порефакторим оставшиеся методы.

Добавим метод findById и импортируем ObjectID так он нам нужен здесь.

var ObjectID = require('mongodb').ObjectID

exports.findById = function (id, cb) {
  db.get().collection('artists').findOne({ _id: ObjectID(id) }, function (err, doc) {
    cb(err, doc);
  })
}

На вход мы принимаем id и коллбек. Точно так же как и в предыдущем случае мы работаем только с данными о request, response модель ничего не знает.

Добавляем метод к контроллеру

exports.findById = function (req, res) {
  Artists.findById(req.params.id, function (err, doc) {
    if (err) {
      console.log(err);
      return res.sendStatus(500);
    }
    res.send(doc);
  })
}

Вызываем метод из модели и передаем id и коллбек на вход.

В server.js сокращаем вызов до

app.get('/artists/:id', artistsController.findById)

Давайте проверим, что get метод работает.

Теперь рефакторим создание исполнителей

Добавим метод create в модель

exports.create = function (artist, cb) {
  db.get().collection('artists').insert(artist, function (err, result) {
    cb(err, result);
  })
}

и метод create в контроллер

exports.create = function (req, res) {
  var artist = {
    name: req.body.name
  };
  Artists.create(artist, function (err) {
    if (err) {
      console.log(err);
      return res.sendStatus(500);
    }
    res.send(artist);
  })
}

Вызовем в server.js artistsController.create

app.post('/artists', artistsController.create)

Давайте проверим, что post по прежнему работает.

Теперь рефакторим put

exports.update = function (id, newData, cb) {
  db.get().collection('artists').updateOne(
    { _id: ObjectID(id) },
    newData,
    function (err, result) {
      cb(err, result);
    }
  )
}

Создаем метод update, который принимает id и новые данные

exports.update = function (req, res) {
  var newData = {
    name: req.body.name
  }
  Artists.update(req.params.id, newData, function (err) {
    if (err) {
      console.log(err);
      return res.sendStatus(500);
    }
    res.sendStatus(200);
  })
}

Описываем метод в контроллере и вызываем в роуте update

app.put('/artists/:id', artistsController.update)

И нам осталось поменять только delete метод

exports.delete = function (req, res) {
  Artists.delete(req.params.id, function (err) {
    if (err) {
      console.log(err);
      return res.sendStatus(500);
    }
    res.sendStatus(200);
  })
}
app.delete('/artists/:id', artistsController.delete)

Давайте проверим, что все работает.

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

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Rustam Apaev
1год назад назад
Здравствуйте, большое спасибо за уроки, посмотрел с удовольствием и всё понял) Как и остальные, что смотрел, кроме редакса, до него видимо не созрел) Мне бы очень хотелось посмотреть как авторизация происходит. С Passport и токенами знаком , но не до конца понимаю как в итоге прикручивать к формам, и всякие "зайти через фейсбук, гугл" и прочее. Везде обрывистые данные даются, нигде целой картины нет( Для меня это вообще большая проблема, потому что вроде как мелкая деталька на сайте, сама собой разумеющаяся, а с ней столько гемора, чтобы её прикрутить. Либо я что-то не так представляю (
monsterlessons
1год назад назад
Добрый день. Спасибо за идею. Добавил в список будущих видео.
Иван Древаль
2 лет назад
Как по мне "все очень плохо". "Всю логику из сервера мы вынесли" - да где же? Т.е. 4 роута это не логика? Я в серверном JS плох, но у меня есть достаточно крупный сайт в поддержке на PHP и ... Что будет с server.js когда количество моделей/контроллеров перевалит за сотню? 400 роутов, Карл! Вот смотрю я уроки по Ноде и не вижу ни одного хоть сколько нибудь приближенного к реально рабочему проекту, одни хелоуворлды разной сложности :( Опять же когда модулей станет много, то в папках с моделями их будет тоже куча, причем все скопом... Хотя да, тут эстетизм и на PHP тоже так делают. Но лично мне нравится идея HMVC ну или хотя бы хранить MVC модули отдельно друг от друга ... Правда при реализации я столкнулся с тем, что тот же handlebars не в курсе моих желаний :( Я ни в коем случае не критикую урок, но все же это утопия, а хотелось бы приближенности к реальности. Спасибо.
monsterlessons
2 лет назад
Спасибо за отзыв, но сложно показать огромный проект за несколько уроков для начинающих. 1. В server.js у меня остались только описания роутов. Логики там нет. Можно все роуты вынести в отдельный файл. Если их там будет 400 можно разбить на несколько файлов. Например apiRoutes, projectRoutes, adminRoutes. 2.Если будет сотня моделей на проекте явно вы будете их как то группировать. Вне зависимости от языка это обычно делают одинаково. Например папка modules и кладем в нее по фичам файлы. Basket, Checkout, ProductList, BasketFlyout и т.п. и модели и контроллеры, которые не sharable раскидываются по этим модулям. Которые sharable в папку shared/models и shared/controllers. 3. Я не вижу проблемы с handlebars. Вы кладете вьюхи так же по модулям и используете
Иван Древаль
2 лет назад
1) как по мне, было бы логично держать папку с роутами и каким-то образом автоматически подгружать оттуда все роуты через index.js ... Где-то видел подобную реализацию, но сейчас наверное уже не найду. 2) Вот нечтно подобное было бы и интересно глянуть. Надеюсь дойдут у вас до этого руки. 3) Как тогда мне указать где лежит общий layout? Ну или возможно я что-то не то делаю ... Спасибо :)
monsterlessons
2 лет назад
1. Делайте как вам удобно, тут нет единого подхода 2. Да скорее всего в будущем сделаю 3. Погуглите просто как вставлять шаблон в layout в node, так как каждая библиотека реализует это по своему
Ordo Malleus
2 лет назад
код для Models delete не верный, такой как для контроллера
monsterlessons
2 лет назад
Какое именно место вы имеете ввиду?
yugle7
2 лет назад
В тексте под видео artistsController написано с ошибкой artistsConroller.
monsterlessons
2 лет назад
Спасибо поправил.
imp
2 лет назад
Спасибо Вам за уроки, очень доступно объясняете! Есть один вопрос. Если, к примеру, проект немного увеличится и роутов станет много. Наверное не правильно их будет хранить в одном файле. Думаю что правильно будет их разделять. Как Вы разделяете роуты? Возможно есть несколько оптимальных подходов для разных размеров приложений, которые Вы используете? Спасибо
monsterlessons
2 лет назад
Спасибо. В больших проектах роуты можно разделять по модулям. Т.е. модуль это отдельная сущность - например плейлисты или треки. И под каждую сущность можно создавать свою папку с роутами, контроллерами и моделями. Это как один из подходов.