Node + MongoDB 建站 2期 -3

實(shí)現(xiàn)分類功能

1 設(shè)計(jì)分類數(shù)據(jù)模型

創(chuàng)建category schema及model

var mongoose = require('mongoose')
var Schema = mongoose.Schema
var ObjectId = Schema.Types.ObjectId

var CategorySchema = new Schema({
  name: String,
  movies: [{type: ObjectId, ref: 'Movie'}],
  meta: {
    createAt: {
      type: Date,
      default: Date.now()
    },
    updateAt: {
      type: Date,
      default: Date.now()
    }
  }
})

// var ObjectId = mongoose.Schema.Types.ObjectId
CategorySchema.pre('save', function(next) {
  if (this.isNew) {
    this.meta.createAt = this.meta.updateAt = Date.now()
  }
  else {
    this.meta.updateAt = Date.now()
  }

  next()
})

CategorySchema.statics = {
  fetch: function(cb) {
    return this
      .find({})
      .sort('meta.updateAt')
      .exec(cb)
  },
  findById: function(id, cb) {
    return this
      .findOne({_id: id})
      .exec(cb)
  }
}

module.exports = CategorySchema

2 分類后臺錄入及分類存儲

修改index jade如下涉馁,添加分類panel

extends ../layout
block content
  .container
    .row
      each cat in categories
        .panel.panel-default
          .panel-heading
            h3
              a(href='/results?cat=#{cat._id}&p=0') #{cat.name}
          .panel-body
            if cat.movies && cat.movies.length > 0
              each item in movies
                .col-md-2
                    .thumbnail
                        a(href="/movie/#{item._id}")
                          if item.poster.indexOf('http:') > -1
                            img(src="#{item.poster}", alt="#{item.title}")
                          else
                            img(src="/upload/#{item.poster}", alt="#{item.title}")
                        .caption
                              h4 #{item.title}
                              p: a.btn.btn-primary(href="/movie/#{item._id}", role="button") 觀看預(yù)告片

修改index controller如下,查詢categories

var mongoose = require('mongoose')
var Movie = require('../models/movie')
var Category = require('../models/Category')

exports.index = function(req, res) {
    Category
    .find({})
    .populate({
      path: 'movies',
      select: 'title poster',
      options: { limit: 6 }
    })
    .exec(function(err, categories) {
      if (err) {
        console.log(err)
      }
      res.render('index', {
        title: 'imooc 首頁',
        categories: categories
      })
    })
}

admin jade中添加分類label

        .form-group
          label.col-sm-2.control-label(for="inputCategory") 電影分類
          .col-sm-10
            input#inputCategory.form-control(type="text", name="movie[category]", value=movie.category)

movie schema中添加category字段

var ObjectId = Schema.Types.ObjectId
......
  category: {
    type: ObjectId,
    ref: 'Category'
  },

創(chuàng)建分類錄入頁category_admin jade

extends ../layout

block content
  .container
    .row
      form.form-horizontal(method="post", action="/admin/category")
        .form-group
          label.col-sm-2.control-label(for="inputCategory") 電影分類
          .col-sm-10
            input#inputCategory.form-control(type="text", name="category[name]", value=category.name)
        .form-group
          .col-sm-offset-2.col-sm-10
          button.btn.btn-default(type="submit") 錄入

添加category controller被芳,保存后到category list頁面

var mongoose = require('mongoose')
var Category = mongoose.model('Category')

// admin new page
exports.new = function(req, res) {
  res.render('category_admin', {
    title: 'imooc 后臺分類錄入頁',
    category: {}
  })
}

// admin post movie
exports.save = function(req, res) {
  var _category = req.body.category
  var category = new Category(_category)

  category.save(function(err, category) {
    if (err) {
      console.log(err)
    }

    res.redirect('/admin/category/list')
  })
}
// catelist page
exports.list = function(req, res) {
  Category.fetch(function(err, catetories) {
    if (err) {
      console.log(err)
    }

    res.render('categorylist', {
      title: 'imooc 分類列表頁',
      catetories: catetories
    })
  })
}

創(chuàng)建category list頁面

extends ../layout

block content
  .container
    .row
      table.table.table-hover.table-bordered
        thead
          tr
            th 名字
            th 時間
            th 查看
            th 修改
            th 刪除
        tbody
          each item in catetories
            tr(class="item-id-#{item._id}")
              td #{item.name}
              td #{moment(item.meta.updateAt).format('MM/DD/YYYY')}
              td: a(target="_blank", href="../movie/#{item._id}") 查看
              td: a(target="_blank", href="../admin/update/#{item._id}") 修改
              td
                button.btn.btn-danger.del(type="button", data-id="#{item._id}") 刪除

在routes中添加路由

var Category = require('../app/controllers/category')
  // Category
app.get('/admin/category/new', User.signinRequired, User.adminRequired, Category.new)
app.post('/admin/category', User.signinRequired, User.adminRequired, Category.save)
app.get('/admin/category/list', User.signinRequired, User.adminRequired, Category.list)

3 電影錄入增加分類

添加分類選擇

        .form-group
          label.col-sm-2.control-label(for="inputCategory") 電影分類
          .col-sm-10
            input#inputCategory.form-control(type="text", name="movie[categoryName]", value=movie.categoryName)
        .form-group
          label.col-sm-2.control-label 分類選擇
          each cat in categories
            label.radio-inline
              if movie.category
                input(type="radio", name="movie[category]", value=cat._id, checked=cat._id.toString()==movie.category.toString())
              else
                input(type="radio", name="movie[category]", value=cat._id)
              | #{cat.name}

修改movie controller

var Category = require('../models/category')
......
exports.new =  function(req, res) {
    Category.find({},function(err,categories){
        res.render('admin', {
            title: 'imooc 后臺錄入頁',
            categories: categories,
            movie: {}
        })
    })
}

通過/admin/category/new新建分類巨柒,查看后臺錄入頁


修改movie controller的save方法樱拴,同時保存category

    } else {
        _movie = new Movie(movieObj)
        var categoryId = movieObj.category
        _movie.save(function(err,movie) {
            if (err){
                console.log(err)
            }
            if (categoryId) {
                Category.findById(categoryId, function(err, category) {
                category.movies.push(movie._id)

                category.save(function(err, category) {
                    res.redirect('/movie/' + movie._id)
                })
                })
            }         
        })

修改update方法柠衍,獲取categories

    if (id) {
        Movie.findById(id,function(err,movie){
            Category.find({}, function(err, categories) {
                res.render('admin', {
                title: 'imooc 后臺更新頁',
                movie: movie,
                categories: categories
                })
            })
        })
    }

4 獲取豆瓣API數(shù)據(jù)

豆瓣電影的API地址格式為 https://api.douban.com/v2/movie/subject/+id
在admin.js中添加

    $('#douban').blur(function() {
      var douban = $(this)
      var id = douban.val()
  
      if (id) {
        $.ajax({
          url: 'https://api.douban.com/v2/movie/subject/' + id,
          cache: true,
          type: 'get',
          dataType: 'jsonp',
          crossDomain: true,
          jsonp: 'callback',
          success: function(data) {
            $('#inputTitle').val(data.title)
            $('#inputDoctor').val(data.directors[0].name)
            $('#inputCountry').val(data.countries[0])
            $('#inputPoster').val(data.images.large)
            $('#inputYear').val(data.year)
            $('#inputSummary').val(data.summary)
          }
        })
      }
    })

在admin jade中引入js晶乔,添加輸入框

        .form-group
          label.col-sm-2.control-label 豆瓣同步
          .col-sm-10
            input#douban.form-control(type="text")
......
  script(src='/js/admin.js')

啟動服務(wù)珍坊,輸入豆瓣id,點(diǎn)擊空白處正罢,將調(diào)用ajax請求自動填寫數(shù)據(jù)


5 電影錄入增加分類自定義

修改movie controller的save方法阵漏,當(dāng)輸入分類名稱時,新建category并save翻具,save成功同時更新movie對象的category

    } else {
        _movie = new Movie(movieObj)
        var categoryId = movieObj.category
        var categoryName = movieObj.categoryName
        _movie.save(function(err,movie) {
            if (err){
                console.log(err)
            }
            if (categoryId) {
                Category.findById(categoryId, function(err, category) {
                category.movies.push(movie._id)

                category.save(function(err, category) {
                    res.redirect('/movie/' + movie._id)
                })
                })
            }
            else if (categoryName) {
                var category = new Category({
                name: categoryName,
                movies: [movie._id]
                })

                category.save(function(err, category) {
                movie.category = category._id
                movie.save(function(err, movie) {
                    res.redirect('/movie/' + movie._id)
                })
                })
            }

6 添加分類列表及分頁

添加results.jade

extends ../layout

block content
  .container
    .row
      .panel.panel-default
        .panel-heading
          h3 #{keyword}
        .panel-body
          if movies && movies.length > 0
            each item in movies
              .col-md-2
                .thumbnail
                  a(href="/movie/#{item._id}")
                    img(src="#{item.poster}", alt="#{item.title}")
                  .caption
                    h4 #{item.title}
                    p: a.btn.btn-primary(href="/movie/#{item._id}", role="button") 觀看預(yù)告片
      ul.pagination
        - for (var i = 0; i < totalPage; i++) {
          - if (currentPage == (i + 1)) {
              li.active
                span #{currentPage}
          - }
          - else {
              li
                a(href='/results?#{query}&p=#{i}') #{i + 1}
          - }
        - }

在route中添加路由app.get('/results', Index.search)
在index controller中添加search方法

// search page
exports.search = function(req, res) {
  var catId = req.query.cat
  var q = req.query.q
  var page = req.query.p
  var count = 2
  var index = page * count

  if (catId) {
    Category
      .find({_id: catId})
      .populate({
        path: 'movies',
        select: 'title poster'
      })
      .exec(function(err, categories) {
        if (err) {
          console.log(err)
        }
        var category = categories[0] || {}
        var movies = category.movies || []
        var results = movies.slice(index, index + count)

        res.render('results', {
          title: 'imooc 結(jié)果列表頁面',
          keyword: category.name,
          currentPage: (page + 1),
          query: 'cat=' + catId,
          totalPage: Math.ceil(movies.length / count),
          movies: results
        })
      })
  }

查看結(jié)果


7 添加搜索功能

修改header jade履怯,添加搜索框

  .row
    .page-header.clearfix
      h1= title
      .col-md-4
        small 重度科幻迷
      .col-md-8
        form(method='GET', action='/results')
          .input-group.col-sm-4.pull-right
            input.form-control(type='text', name='q')
            span.input-group-btn
              button.btn.btn-default(type='submit') 搜索

在search方法中根據(jù)catId判斷,通過搜索框是通過Movie來找裆泳,通過new RegExp(q + '.*', 'i')來模糊匹配

// search page
exports.search = function(req, res) {
  var catId = req.query.cat
  var q = req.query.q
  // var page = parseInt(req.query.p, 10) || 0
  var page = req.query.p
  var count = 3
  var index = page * count

  if (catId) {
    Category
      .find({_id: catId})
      .populate({
        path: 'movies',
        select: 'title poster'
      })
      .exec(function(err, categories) {
        if (err) {
          console.log(err)
        }
        var category = categories[0] || {}
        var movies = category.movies || []
        var results = movies.slice(index, index + count)

        res.render('results', {
          title: 'imooc 結(jié)果列表頁面',
          keyword: category.name,
          currentPage: (page + 1),
          query: 'cat=' + catId,
          totalPage: Math.ceil(movies.length / count),
          movies: results
        })
      })
  }
  else {
    Movie
      .find({title: new RegExp(q + '.*', 'i')})
      .exec(function(err, movies) {
        if (err) {
          console.log(err)
        }
        var results = movies.slice(index, index + count)

        res.render('results', {
          title: 'imooc 結(jié)果列表頁面',
          keyword: q,
          currentPage: (page + 1),
          query: 'q=' + q,
          totalPage: Math.ceil(movies.length / count),
          movies: results
        })
      })
  }
}

8 海報(bào)上傳功能

添加文件上傳組件叹洲,form添加enctype屬性

form.form-horizontal(method="post", action="/admin/movie/new"  enctype="multipart/form-data")
        .form-group
          label.col-sm-2.control-label(for="uploadPoster") 海報(bào)上傳
          .col-sm-10
            input#uploadPoster(type="file", name="uploadPoster")

movie controller中添加savePoster,save方法中更新poster

// admin poster
exports.savePoster = function(req, res, next) {
  var posterData = req.files.uploadPoster
  var filePath = posterData.path
  var originalFilename = posterData.originalFilename

  if (originalFilename) {
    fs.readFile(filePath, function(err, data) {
      var timestamp = Date.now()
      var type = posterData.type.split('/')[1]
      var poster = timestamp + '.' + type
      var newPath = path.join(__dirname, '../../', '/public/upload/' + poster)

      fs.writeFile(newPath, data, function(err) {
        req.poster = poster
        next()
      })
    })
  }
  else {
    next()
  }
}
exports.save =  function(req,res){
    var id = req.body.movie._id
    var movieObj = req.body.movie
    var _movie

    if (req.poster) {
        movieObj.poster = req.poster
      }

安裝connect-multiparty npm i connect-multiparty --save-dev工禾,并在app js中添加

var multipart = require('connect-multiparty');
app.use(multipart());

8 添加PV統(tǒng)計(jì)

在list jade中添加PV列疹味,movie schema中添加PV

  pv: {
    type: Number,
    default: 0
  },

在movie controller中,detail中更新movie帜篇,每次進(jìn)入detail頁面PV將增加1

  Movie.update({_id: id}, {$inc: {pv: 1}}, function(err) {
    if (err) {
      console.log(err)
    }
  })
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市诫咱,隨后出現(xiàn)的幾起案子笙隙,更是在濱河造成了極大的恐慌,老刑警劉巖坎缭,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竟痰,死亡現(xiàn)場離奇詭異,居然都是意外死亡掏呼,警方通過查閱死者的電腦和手機(jī)坏快,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來憎夷,“玉大人莽鸿,你說我怎么就攤上這事∈案” “怎么了祥得?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蒋得。 經(jīng)常有香客問我级及,道長,這世上最難降的妖魔是什么额衙? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任饮焦,我火速辦了婚禮怕吴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘县踢。我一直安慰自己转绷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布殿雪。 她就那樣靜靜地躺著暇咆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丙曙。 梳的紋絲不亂的頭發(fā)上爸业,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音亏镰,去河邊找鬼扯旷。 笑死,一個胖子當(dāng)著我的面吹牛索抓,可吹牛的內(nèi)容都是我干的钧忽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼逼肯,長吁一口氣:“原來是場噩夢啊……” “哼耸黑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起篮幢,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤大刊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后三椿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缺菌,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年搜锰,在試婚紗的時候發(fā)現(xiàn)自己被綠了伴郁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛋叼,死狀恐怖焊傅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸦列,我是刑警寧澤租冠,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站薯嗤,受9級特大地震影響顽爹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骆姐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一镜粤、第九天 我趴在偏房一處隱蔽的房頂上張望捏题。 院中可真熱鬧,春花似錦肉渴、人聲如沸公荧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽循狰。三九已至,卻和暖如春券勺,著一層夾襖步出監(jiān)牢的瞬間绪钥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工关炼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留程腹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓儒拂,卻偏偏與公主長得像寸潦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子社痛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容