Skip to content

web mvc todo chinese

fantasyni edited this page May 3, 2014 · 1 revision

概述

Node 非常擅长进行web应用开发, 通过使用Bearcat Node web 开发会变得更加容易, 快速和可维护.
让我们开始Bearcat实战系列教程 -- web mvc todo

预热

你可以先可以参照todo的说明尝试着把todo应用简单的跑起来
跑起来后大致是这样的
directory structure 你可以添加, 更新, 完成, 删除你的todo列表

SQL 表结构

这个todo应用数据库用的是mysql, 因此, 首先我们要确定好sql表结构

DROP TABLE IF EXISTS `bearcat_todo`;
CREATE TABLE `bearcat_todo` (
  `id` bigint(20) NOT NULL,
  `title` varchar(300) default NULL,
  `finished` int(11) default 0,
  `post_date` bigint(20) default 0,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

为了使用 Bearcat-dao 提供的 O/R mapping, 我们需要添加如下的IDGenerator表用于生成表bearcat_todo唯一的主键id

create table IDGenerator(
    name varchar(50) NOT NULL,
    id bigint(20) unsigned NOT NULL DEFAULT 0,
    
    PRIMARY KEY (name)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into IDGenerator (name, id) values ('bearcat_todo', 0);

配置好 package.json 和 context.json

添加 bearcat 和 bearcat-dao 依赖到 package.json

"bearcat": "~0.1.0",
"bearcat-dao": "~0.1.0"

添加context.json文件, 使用占位符来根据不同的环境来配置不同的值

{
	"name": "bearcat-todo",
	"dependencies": {
		"bearcat-dao": "*"
	},
	"scan": "app",
	"beans": [{
		"id": "mysqlConnectionManager",
		"func": "node_modules.bearcat-dao.lib.connection.sql.mysqlConnectionManager",
		"props": [{
			"name": "port",
			"value": "${mysql.port}"
		}, {
			"name": "host",
			"value": "${mysql.host}"
		}, {
			"name": "user",
			"value": "${mysql.user}"
		}, {
			"name": "password",
			"value": "${mysql.password}"
		}, {
			"name": "database",
			"value": "${mysql.database}"
		}]
	}]
}

O/R mapping domain

我们现在要编写我们的O/R mapping domain 对象, 它是一个简单的POJO, 没什么特别的

var TodoDomain = function() {
	this.id = null;
	this.title = null;
	this.finished = 0;
	this.post_date = 0;
}

module.exports = {
	func: TodoDomain,
	primary: [{
		name: "id",
		type: "Long"
	}],
	fields: ["title", "finished", "post_date"],
	tableName: "bearcat_todo"
}

编写 todoDao

之后我们编写todoDao来添加对表bearcat_todo的CRUD增删改查操作
todoDao 也是一个简单的由Bearcat IoC 容器所管理的POJO

var TodoDomain = require('../domain/todoDomain');

var TodoDao = function() {
	this.domainDaoSupport = null;
}

TodoDao.prototype.setDomainDaoSupport = function(domainDaoSupport) {
	this.domainDaoSupport = domainDaoSupport;
}

TodoDao.prototype.getDomainDaoSupport = function() {
	return this.domainDaoSupport;
}

TodoDao.prototype.init = function() {
	// init todoDao with todoDomain to specify the O/R mapping domain for current todoDao
	this.domainDaoSupport.initConfig(TodoDomain);
}

TodoDao.prototype.getList = function(params, cb) {
	var sql = ' 1=1 order by finished asc, id asc limit ?,?';
	return this.domainDaoSupport.getListByWhere(sql, params, null, cb);
}

TodoDao.prototype.addTodo = function(params, cb) {
	var sql = 'insert into ' + this.domainDaoSupport.getTableConfig().getTableName() + ' set title = ?, post_date = ?';
	return this.domainDaoSupport.add(sql, params, cb);
}

TodoDao.prototype.getTodoById = function(id, cb) {
	return this.domainDaoSupport.getById(id, cb);
}

TodoDao.prototype.updateTodo = function(title, id, cb) {
	var sql = 'update ' + this.domainDaoSupport.getTableConfig().getTableName() + ' set title = ? where id = ?';
	return this.domainDaoSupport.update(sql, [title, id], cb);
}

TodoDao.prototype.updateTodoFinished = function(finished, id, cb) {
	var sql = 'update ' + this.domainDaoSupport.getTableConfig().getTableName() + ' set finished = ? where id = ?';
	return this.domainDaoSupport.update(sql, [finished, id], cb);
}

TodoDao.prototype.deleteById = function(id, cb) {
	return this.domainDaoSupport.deleteById(id, cb);
}

module.exports = {
	id: "todoDao",
	func: TodoDao,
	props: [{
		name: "domainDaoSupport",
		ref: "domainDaoSupport"
	}],
	"init": "init"
}

编写 todoService

Service 层基于 Dao 层, 并且被Controller层所调用

var TodoService = function() {
	this.todoDao = null;
}

TodoService.prototype.getList = function(params, cb) {
	return this.todoDao.getList(params, cb);
}

TodoService.prototype.addTodo = function(params, cb) {
	return this.todoDao.addTodo(params, cb);
}

TodoService.prototype.getTodoById = function(id, cb) {
	return this.todoDao.getTodoById(id, cb);
}

TodoService.prototype.updateTodo = function(title, id, cb) {
	return this.todoDao.updateTodo(title, id, cb);
}

TodoService.prototype.updateTodoFinished = function(finished, id, cb) {
	return this.todoDao.updateTodoFinished(finished, id, cb);
}

TodoService.prototype.deleteById = function(id, cb) {
	return this.todoDao.deleteById(id, cb);
}

module.exports = {
	id: "todoService",
	func: TodoService,
	props: [{
		name: "todoDao",
		ref: "todoDao"
	}]
}

编写 todoController

Controller 在web中就是处理request, response请求的那层

var TodoController = function() {
	this.todoService = null;
}

// get index list
TodoController.prototype.index = function(req, res, next) {
	this.todoService.getList([0, 50], function(err, results) {
		if (err) {
			console.log(err);
			return;
		}
		res.render('index.html', {
			todos: results
		});
	});
}

// create a new todo list
TodoController.prototype.new = function(req, res, next) {
	var title = req.body.title || '';
	title = title.trim();
	if (!title) {
		return res.render('error.html', {
			message: '标题是必须的'
		});
	}
	this.todoService.addTodo([title, Date.now()], function(err, result) {
		if (err) {
			return next(err);
		}
		res.redirect('/');
	})
}

TodoController.prototype.view = function(req, res, next) {
	res.redirect('/');
}

// edit a todo list
TodoController.prototype.edit = function(req, res, next) {
	var id = req.params.id;
	this.todoService.getTodoById(id, function(err, result) {
		if (err) {
			return next(err);
		}

		if (!result) {
			return next();
		}

		result = result[0];
		res.render('todo/edit.html', {
			todo: result
		});
	});
}

// save a todo list
TodoController.prototype.save = function(req, res, next) {
	var id = req.params.id;
	var title = req.body.title || '';
	title = title.trim();
	if (!title) {
		return res.render('error.html', {
			message: '标题是必须的'
		});
	}

	this.todoService.updateTodo(title, id, function(err, result) {
		if (err) {
			return next(err);
		}
		res.redirect('/');
	});
}

// delete a todo list
TodoController.prototype.delete = function(req, res, next) {
	var id = req.params.id;

	this.todoService.deleteById(id, function(err, result) {
		if (err) {
			return next(err);
		}
		res.redirect('/');
	});
}

// finish a todo list
TodoController.prototype.finish = function(req, res, next) {
	var finished = req.query.status === 'yes' ? 1 : 0;
	var id = req.params.id;

	this.todoService.updateTodoFinished(finished, id, function(err, result) {
		if (err) {
			return next(err);
		}
		res.redirect('/');
	});
}

module.exports = {
	id: "todoController",
	func: TodoController,
	props: [{
		name: "todoService",
		ref: "todoService"
	}]
}

编写 server.js

启动Bearcat, 添加route配置给connect, express之类的web框架

var contextPath = require.resolve('./context.json');

var bearcat = Bearcat.createApp([contextPath]);

bearcat.start(function() {
  /**
   * Routing
   */
  var router = urlrouter(function(app) {
    app.get('/', bearcat.getRoute("todoController", "index"));
    app.post('/todo/new', bearcat.getRoute("todoController", "new"));
    app.get('/todo/:id', bearcat.getRoute("todoController", "view"));
    app.get('/todo/:id/edit', bearcat.getRoute("todoController", "edit"));
    app.post('/todo/:id/edit', bearcat.getRoute("todoController", "save"));
    app.get('/todo/:id/delete', bearcat.getRoute("todoController", "delete"));
    app.get('/todo/:id/finish', bearcat.getRoute("todoController", "finish"));
  });
  app.use(router);

  // start app
  app.listen(config.port);
  console.log('Server start on ' + config.port);
});

跑起来

node server.js

项目完整的代码在 todo