Строим блог на Areto фреймворк

Контроллер статьи

Класс ArticleController отвечает за вывод, поиск, сортировку и фильтрацию статей.

controllers/ArticleController.js

const Base = require('../component/BaseController');
module.exports = class ArticleController extends Base {
  // place methods here
};
module.exports.init(module);
const ActiveDataProvider = require('areto/data/ActiveDataProvider');
const Article = require('../model/Article');

Метод actionIndex выводит основной список статей. Для сортировки и разбиения списка на страницы используется класс areto/data/ActiveDataProvider. В параметре query задается запрос, который находит список опубликованных статей.

Для вывода статей в различном порядке, используйте параметр sort. В свойстве attrs укажите атрибуты статьи, по которым доступна сортировка списка. Для сортировки по умолчанию используйте defaultOrder.

Для удобства представления и снижения нагрузки в ActiveDataProvider по умолчанию включено постраничное разбиение (10 элементов на страницу). Используйте параметр pageSize, чтобы задать другой размер страницы. Если требуется вывести весь список целиком, то установите pagination: null.

controllers/ArticleController.js

actionIndex () {
  let provider = this.createDataProvider({
    query: Article.findPublished()
  });
  provider.prepare(err => {
    err ? this.throwError(err)
        : this.render('index', {provider});
  });
}

Метод actionSearch выводит список статей отфильтрованных по поисковому тексту. Реализация аналогична методу actionIndex за исключением параметра query. Пользовательский текст из GET-параметра this.getQueryParam('text') передается аргументом в статический метод Article.findBySearch.

controllers/ArticleController.js

actionSearch () {
  let provider = this.createDataProvider({
    query: Article.findBySearch(this.getQueryParam('text'))
  });
  this.renderDataProvider(provider, 'index', {provider});
}

Метод actionView работает с отдельной статьей. Если запрос приходит в формате POST (isPost), то обрабатывается форма создания нового комментария.

Для создания нового комментария в объект класса Comment загрузите данные формы comment.load(this.getBodyParams()). Установите статью, к которой относится комментарий, и IP пользователя.

Если комментарий успешно сохранен, то пользователю выводится соответствующее сообщение (setFlash). Сообщение записывается в сессию и однократно отобразится при следующей загрузке страницы. Затем происходит перезагрузка страницы this.redirect, чтобы сбросить текущий POST запрос. В случае провала сохранения, ошибки будут отражены на форме комментария.

controllers/ArticleController.js

actionView () {
  this.getModel({
    with: ['category', 'mainPhoto', 'photos', 'tags']
  }, model => {
    let comment = new Comment;
    if (this.isGet()) {
      return this.renderView(model, comment);
    }
    comment.load(this.getBodyParams());
    comment.set('articleId', model.getId());
    comment.set('ip', this.req.ip);
    async.series([
      cb => comment.save(cb),
      cb => comment.hasError()
        ? this.renderView(model, comment)
        : cb(),
      cb => {
        this.setFlash('comment-done', this.translate('You message has been sent successfully!'));
        this.redirect(['view', model]);
      }
    ], err => this.throwError(err));
  });
}

Метод renderView отображает отдельную статью, указанную в аргументе model. Комментарии, относящихся к статье, выводятся через ActiveDataProvider, который формирует постраничное разбиение списка.

controllers/ArticleController.js

renderView (model, comment) {
  let comments = this.createDataProvider({
    query: model.relComments()
  });
  async.series([
    cb => comments.prepare(cb),
    cb => this.render('view', {model, comments, comment})
  ], err => this.throwError(err));
}

Метод actionTagged выводит список статей отфильтрованных по метке.

Сначала нужно найти модель метки по ее названию, которое передается в параметре tag GET-запроса. Создайте новую модель Tag, установите атрибут name. Проверьте полученные данные. В случае отсутствия ошибок, создайте запрос поиска метки по имени. Если метка найдена, то инициализируйте провайдер ActiveDataProvider, где в качестве источника данных query укажите отношение tag.relArticles(), возвращающее все статьи с данной меткой.

controllers/ArticleController.js

 actionTagged () {
  let tagName = this.getQueryParam('tag');
  async.waterfall([
    cb => this.getTag(tagName, cb),
    tag => {
      let provider = this.createDataProvider({
        query: tag.relArticles()
      });
      this.renderDataProvider(provider, 'tagged', {provider, tagName});
    }
  ], err => this.throwError(err));
}

getTag (tagName, cb) {
  let tag = new Tag;
  tag.set('name', tagName);
  async.waterfall([
    cb => tag.validate(cb),
    (tag, cb)=> tag.hasError()
      ? this.render('tagged', {tagName})
      : cb(),
    cb => Tag.findByName(tagName).one(cb),
      model => model
        ? cb(null, model)
        : this.render('tagged', {tagName})
  ], err => this.throwError(err));
}