Build a blog with Areto Node.js framework

Article model

The Article class is the model of the article. Also it integrates the other entities of the blog.

Article view

The STORED_ATTRS property is not declared in the model of the public section. because all article's changes will be made in the administration module. Add the name of the table and string status IDs to the constants of the class:

  • draft - article is in edit mode and is not available in the public section of the blog.
  • published - article is available for viewing in the public section.
  • archived - article withdrawn from publication.

models/Article.js

'use strict';
const Base = require('areto/db/ActiveRecord');
module.exports = class Article extends Base {

  static getConstants () {
    return {
      TABLE: 'article',
      STATUS_DRAFT: 'draft',
      STATUS_PUBLISHED: 'published',
      STATUS_ARCHIVED: 'archived'
    };
  }
  // place methods here
};
module.exports.init(module);
const Comment = require('./Comment');
const Photo = require('./Photo');
const User = require('./User');
const Tag = require('./Tag');

Model title corresponds to the article title.

models/Article.js

...
getTitle () {
  return this.get('title');
}
...

The following methods are required to check the status.

models/Article.js

...
isDraft () {
  return this.get('status') === this.STATUS_DRAFT;
}

isPublished () {
  return this.get('status') === this.STATUS_PUBLISHED;
}

isArchived () {
  return this.get('status') === this.STATUS_ARCHIVED;
}
...

Data query

The findPublished method returns a query that looks for all published articles. Also data for the mainPhoto, tags relations (see below) will be requested for each article.

models/Article.js

...
static findPublished () {
  return this.find().where({status: this.STATUS_PUBLISHED}).with('mainPhoto','tags');
}
...

The findBySearch method extends the findPublished query. Used filter searches the specified text in the article's title.

models/Article.js

...
static findBySearch (text) {
  let query = this.findPublished();
  if (typeof text === 'string' && /[a-z0-9\-\s]{1,32}/i.test(text)) {
    query.andWhere(['LIKE', 'title', `%${text}%`]);
  }
  return query;
}
...

Model's relations

Methods prefixed by rel define the relations of the target model to others. Each relation is set via a link that is described in the return areto/db/ActiveQuery query.

The relAuthor method finds the author of the article. The hasOne, hasMany functions reflect the expected number of results. In this case, one article can have one author only.

The first argument (User) defines the model class associated with the article. The second argument [User.PK, 'authorId'] contains the attributes that form links. A primary key (User.PK) is used by the user model. authorId attribute that stores the identity of the author is used by the article.

models/Article.js

...
relAuthor () {
  return this.hasOne(User, [User.PK, 'authorId']);
}
...

The relPhotos method returns a query for all photos associated with the article. The article may have a lot of photos, so hasMany method is used.

models/Article.js

...
relPhotos () {
  return this.hasMany(Photo, ['articleId', this.PK]);
}
...

The main photo displayed in the list of articles defined in the relMainPhoto relation. The Article class contains the mainPhotoId attribute that keeps the model ID of the Photo class.

models/Article.js

...
relMainPhoto () {
  return this.hasOne(Photo, [Photo.PK, 'mainPhotoId']);
}
...

The relComments method defines the comments, related to the article. Additional filter by status allows only approved ones for the public section of the blog.

models/Article.js

...
relComments () {
  return this.hasMany(Comment, ['articleId', this.PK]).where({status: Comment.STATUS_APPROVED});
}
...

The relTags method selects tags relating to the article. Each tag can be associated with multiple articles and each article can contain multiple tags. This type of relations is called a "many to many". It uses the junction rel_article_tag table.

Link the Tag model with the junction table tagId field. The first argument of viaTable contains the name of the junction table. The second argument binds the table articleId field to the Article model.

models/Article.js

...
relTags () {
  return this.hasMany(Tag, [Tag.PK, 'tagId']).viaTable('rel_article_tag', ['articleId', this.PK]);
}
...