REST服务准备
1 Laravel
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
- RESTful 路由: 通过简单的闭包就能响应HTTP请求。帮你快速开始构建非凡的应用。
- 强大的数据操纵能力: Laravel自带了强大的Eloquent ORM 和迁移工具。能够完美的与MySQL、Postgres、SQL Server 和 SQLite协同工作。
- 优雅的模版引擎: PHP代码或轻量级的Blade模版引擎都可无缝融合。Blade模版可以继承,并且拥有极快的解析速度。相信你会喜欢它的。
- 为明天做准备: 构建大型的企业级应用或者只是提供简单的JSON API;书写强大的控制器或轻巧的RESTful路由,Laravel适应所有级别的开发工作。
- 可靠的基石: Laravel 的基石是数个Symfony组件,这些经过千锤百炼、可靠的组件为你的应用提供坚实的基础。
- 基于Composer管理器: Composer 是一套帮你管理第三方扩展包的工具。能够让你迅速在 Packagist 中找到需要的扩展包。
- 强大的社区支持: 无论你是一个PHP新手还是经验丰富的架构师,都能在社区中找到需要的知识。你可以在IRC中讨论Idea,或者在论坛中发布问题。
- 测试、重构: Laravel 从开始就将测试作为重点功能。我们提供了灵活的IoC容器,集成了PHPUnit 测试工具。不用担心,这些都很容易上手。
1.1 为什么是 Laravel
- 因为个人喜爱,你也可以用 Ruby On Rails来搭建这样一个功能,或者是Java。
- PHP在我的服务器上运行得挺不错的,而且我又不需要重新去写配置那些配置。
- Laravel 可以简单的开发我们所需要的功能,换句话说他是 PHP 世界的 Ruby On Rails。
这里不会再重述之前的问题,这里只是将需要的步骤一个个写下来,然后丢到这里好好说一下。至于RESTful是什么,前面已经介绍了,就不再重复了。那么下面,我们就用Laravel来搭建一个平台给物联网用的。
2 安装 Laravel
2.1 GNU/Linux安装Composer
GNU/Linux Ubuntu/OpenSUSE下可以执行
$ curl -sS https://getcomposer.org/installer | php
2.1.1 Windows安装Composer
请直接下载
2.1.2 Mac OS
1.安装Composer
brew install homebrew/php/composer
2.安装Laravel
composer global require "laravel/installer=~1.1"
3.创建Laravel工程
composer create-project laravel/laravel your-project-name --prefer-dist
2.1.3 Mac OS
1.下载laravel.phar
wget http://laravel.com/laravel.phar
2.重命名
mv laravel.phar laravel
3.移动到bin中
sudo mv laravel /usr/local/bin
4.创建项目
laravel new blog
3 MySQL
3.1 安装MySQL
出于某些原因,我建议用MariaDB替换MySQL,如果你"真正"需要mysql,将mariadb替换为mysql
ps: 在下文中我会继续用MySQL,而不是MariaDB,MairaDB是MySQL的一个分支,真正的开源分支。
Ubuntu/Debian/Mint
$ sudo apt-get install mariadb-server
Fedora/Centos
$ sudo yum install mariadb-server
openSUSE
$ sudo zypper install mariadb-server
Mac OS
$ brew install mariadb
3.2 配置MySQL
修改database.php
app/config/database.php
要修改的就是这个
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'iot',
'username' => 'root',
'password' => '940217',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
如果你已经有phpmyadmin,似乎对你来说已经很简单了,如果没有的话,就直接用
$ mysql -uroot -p
来创建一个新的
CREATE DATABASE IF NOT EXISTS iot default charset utf8 COLLATE utf8_general_ci;
数据库的目的在于存储数据等等的闲话这里就不多说了,创建一个RESTful的目的在于产生下面的JSON格式数据,以便于我们在Android、Java、Python、jQuery等语言框架或者平台上可以调用,最主要的是可以直接用Ajax来产生更炫目的效果。
{
"id": 1,
"temperature": 14,
"sensors1": 12,
"sensors2": 12,
"led1": 0
}
创建REST服务
1 数据库迁移
这个名字是源自于Ruby On Rails在那时候的印象,不直接使用MySQL的目的在于让我们可以专注于过程。
1.1 创建表
表的概念,类似于在Excel中的表,如果你真实不懂数据库。 让我们创建一个athomes的表,为什么是athomes,因为以前在写android程序的时候就叫的是athome,忽略掉这些次要的因素吧。
$ php artisan migrate:make create_athomes_table
打开 app/database/migrations/***create_athomes_table.php 这里的***是由日期和某些随机变量组成的,修改生成的PHP代码,如下:
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAthomesTable extends Migration { public function up() { Schema::create('athomes', function(Blueprint $table) { $table--->increments('id'); $table->float('temperature'); $table->float('sensors1'); $table->float('sensors2'); $table->boolean('led1'); $table->timestamps(); }); } public function down() { Schema::drop('athomes'); } }
id值是自加的,也就是我们在localhost/athome/{id},当我们创建一个新的数据的时候,会自动加上去。最后一个timestamps批的是时间,会包含创建时间和修改时间。 剩下的temperature,sensors1,sensors2是小数,以及只有true和false的led1。
1.2 数据库迁移
我们只是写了我们需要的数据的格式而并没有丢到数据库里,
$ php artisan migrate
这个就是我们执行迁移的命令,如果你用phpmyadmin可以直接打开查看,没有的话,可以。
$ mysql -uroot -p
use iot; select * from athomes;
就可以看到我们写的东西,那么接下来就是创建RESTful服务了
2 创建RESTful
用下面的代码实现我们称之为Athomes控制器的创建
$ php artisan controller:make AthomesController
就会在app/controllers下面生成下面的代码
class AthomesController extends \BaseController { public function index() {} public function create() {} public function store() {} public function show($id) {} public function edit($id) {} public function update($id) {} public function destroy($id) {} }
3 Laravel Resources
上面的代码过于沉重,请让我用 Ctrl+C 来带来点知识吧。
Verb | Path | Action | Route Name |
---|---|---|---|
GET | /resource | index | resource.index |
GET | /resource/create | create | resource.create |
POST | /resource | store | resource.store |
GET | /resource/{resource} | show | resource.show |
GET | /resource/{resource}/edit | edit | resource.edit |
PUT/PATCH | /resource/{resource} | update | resource.update |
DELETE | /resource/{resource} | destroy | resource.destroy |
所以我们只需要专注于创建 create, edit, show, destory 等等。好吧,你可能没有耐心了,但是在修改这个之前我们需要先在 app/model 加个 class
class Athomes extends Eloquent {
protected $table = 'athomes';
}
如果你想要的只是控制器Athomes的代码的话。。
class AthomesController extends \BaseController {
public $restful=true;
protected $athome;
public function __construct(Athomes $athome)
{
$this--->athome = $athome ;
}
public function index()
{
$maxid=Athomes::all();
return Response::json($maxid);
}
public function create()
{
$maxid=Athomes::max('id');
return View::make('athome.create')->with('maxid',$maxid);
}
public function store()
{
$rules = array(
'led1'=>'required',
'sensors1' => 'required|numeric|Min:-50|Max:80',
'sensors2' => 'required|numeric|Min:-50|Max:80',
'temperature' => 'required|numeric|Min:-50|Max:80'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('athome/create')
->withErrors($validator)
->withInput(Input::except('password'));
} else {
$nerd = new Athomes;
$nerd->sensors1 = Input::get('sensors1');
$nerd->sensors2 = Input::get('sensors2');
$nerd->temperature = Input::get('temperature');
$nerd->led1 = Input::get('led1');
$nerd->save();
Session::flash('message', 'Successfully created athome!');
return Redirect::to('athome');
}
}
public function show($id)
{
$myid=Athomes::find($id);
$maxid=Athomes::where('id','=',$id)
->select('id','temperature','sensors1','sensors2','led1')
->get();
return Response::json($maxid);
}
public function edit($id)
{
$athome = Athomes::find($id);
return View::make('athome.edit')
->with('athome', $athome);
}
public function update($id)
{
$rules = array(
'led1'=>'required|',
'sensors1' => 'required|numeric|Min:-50|Max:80',
'sensors2' => 'required|numeric|Min:-50|Max:80',
'temperature' => 'required|numeric|Min:-50|Max:80'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('athome/' . $id . '/edit')
->withErrors($validator);
} else {
$nerd = Athomes::find($id);
$nerd->sensors1 = Input::get('sensors1');
$nerd->sensors2 = Input::get('sensors2');
$nerd->temperature = Input::get('temperature');
$nerd->led1 = Input::get('led1');
$nerd->save();
Session::flash('message', 'Successfully created athome!');
return Redirect::to('athome');
}
}
public function destroy($id)
{
$athome = Athomes::find($id);
$athome->delete();
if(is_null($athome))
{
return Response::json('Todo not found', 404);
}
Session::flash('message', 'Successfully deleted the nerd!');
return Redirect::to('athome');
}
}
希望你能读懂,没有的话,继续。
下面这部分来自于之前的博客,这里就不多加论述了。 这个也就是我们要的模板,
3.1 修改Create()
public function create()
{
$maxid=Athomes::max('id');
return View::make('athome.create')->with('maxid',$maxid);
}
这里需要在app/views/创建一个athome里面创建一个create.blade.php,至于maxid,暂时还不需要,后面会用到show。如果只需要模板,可以简化为
public function create()
{
return View::make('athome.create');
}
这里只是对其中代码的进行一下说明。
3.2 创建前台页面
3.2.1 开始之前
由于使用到了bootstrap以及bootstrap-select,记得添加css。
<link rel="stylesheet" type="text/css" href="<?= url('css/bootstrap.min.css') ?>" />
<link rel="stylesheet" type="text/css" href="<?= url('css/bootstrap-select.min.css') ?>" />
以及javascript
<script type="text/javascript" src="<?= url('js/jquery.min.js')?>"></script>
<script type="text/javascript" src="<?= url('js/bootstrap.min.js') ?>"></script>
<script type="text/javascript" src="<?= url('js/bootstrap-select.min.js') ?>"></script>
<script>
$('.selectpicker').selectpicker();
</script>
3.2.2 创建资源页面
这里用到的是之前提到的那个作者写下的,稍微修改了一下。
<div class="row-fluid">
{{ HTML::ul($errors->all()) }}
{{ Form::open(array('url' => 'athome')) }}
<div class="form-group">
{{ Form::label('led1', '开关1') }}
{{ Form::select('led1',array('关','开'),$selected=NULL,array('class'=>'selectpicker')) }}
</div>
<div class="form-group">
{{ Form::label('sensors1', 'sensors1') }}
{{ Form::text('sensors1', Input::old('sensors1'), array('class' => 'form-control')) }}
</div>
<div class="form-group">
{{ Form::label('sensors2', 'sensors2') }}
{{ Form::text('sensors2', Input::old('sensors2'), array('class' => 'form-control')) }}
</div>
<div class="form-group">
{{ Form::label('temperature', 'temperature') }}
{{ Form::text('temperature', Input::old('temperature'), array('class' => 'form-control')) }}
</div>
{{ Form::submit('Create!', array('class' => 'btn btn-primary')) }}
{{ Form::close() }}
</div>
开关一开始打算用 checkbox,加上 bootstrap-switch 实现
ON OFF
弱弱地觉得还是没掌握好的节奏,所以最后用 select 来实现。
还需要修改一下之前的 create(),添加一行
return Redirect::to('athome');
也就是添加完后,重定向到首页查看,最后例子给出的 create 如下
public function store()
{
$rules = array(
'led1'=>'required',
'sensors1' => 'required|numeric|Min:-50|Max:80',
'sensors2' => 'required|numeric|Min:-50|Max:80',
'temperature' => 'required|numeric|Min:-50|Max:80'
);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails()) {
return Redirect::to('athome/create')
->withErrors($validator);
} else {
// store
$nerd = new Athomes;
$nerd->sensors1 = Input::get('sensors1');
$nerd->sensors2 = Input::get('sensors2');
$nerd->temperature = Input::get('temperature');
$nerd->led1 = Input::get('led1');
$nerd->save();
Session::flash('message', 'Successfully created athome!');
return Redirect::to('athome');
}
}
效果图:
3.4 更新资源页面
完整的 blade 模板文件
<!DOCTYPE html lang="zh-cn">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="">
<meta name="viewport" content="width=device-width">
<meta name="description" content="">
<title>@yield('title')</title>
<link rel="stylesheet" type="text/css" href="<?= url('css/bootstrap.min.css') ?>" />
<link rel="stylesheet" type="text/css" href="<?= url('css/bootstrap-select.min.css') ?>" />
<link rel="stylesheet" href="<?= url('css/justified-nav.css') ?>" type="text/css" media="screen" />
</head>
<body>
<div class="container">
<div class="container">
<div class="row-fluid">
<h1>Edit {{ $athome->id }}</h1>
<!-- if there are creation errors, they will show here -->
{{ HTML::ul($errors->all()) }}
{{ Form::model($athome, array('route' => array('athome.update', $athome->id), 'method' => 'PUT')) }}
<div class="form-group">
{{ Form::label('led1', '开关1') }}
{{ Form::select('led1',array('关','开'),$selected=NULL,array('class'=>'selectpicker')) }}
</div>
<div class="form-group">
{{ Form::label('sensors1', '传感器1') }}
{{ Form::text('sensors1', Input::old('sensors1'), array('class' => 'form-control')) }}
</div>
<div class="form-group">
{{ Form::label('sensors2', '传感器2') }}
{{ Form::text('sensors2', Input::old('sensors2'), array('class' => 'form-control')) }}
</div>
<div class="form-group">
{{ Form::label('temperature', '温度传感器') }}
{{ Form::text('temperature', Input::old('temperature'), array('class' => 'form-control')) }}
</div>
{{ Form::submit('Edit the Nerd!', array('class' => 'btn btn-primary')) }}
{{ Form::close() }}
</div>
</div>
<div class="footer">
<p>© Company 2013</p>
</div>
</div>
</div>
<script type="text/javascript" src="<?= url('js/jquery.min.js')?>"></script>
<script type="text/javascript" src="<?= url('js/bootstrap.min.js') ?>"></script>
<script type="text/javascript" src="<?= url('js/bootstrap-select.min.js') ?>"></script>
<script>
$('.selectpicker').selectpicker();
</script>
<script type="text/javascript" src="<?= url('js/log.js') ?>"></script>
</body>
</html>
效果图:
最后效果见:http://b.phodal.com/
RESTful Evertywhere
1 Javascript与ajax
AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。AJAX = 异步 JavaScript和XML(标准通用标记语言的子集)。
1.1 jQuery
Jquery是继prototype之后又一个优秀的Javascript框架。它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+),jQuery2.0及后续版本将不再支持IE6/7/8浏览器。jQuery使用户能更方便地处理HTML(标准通用标记语言下的一个应用)、events、实现动画效果,并且方便地为网站提供AJAX交互。jQuery还有一个比较大的优势是,它的文档说明很全,而且各种应用也说得很详细,同时还有许多成熟的插件可供选择。jQuery能够使用户的html页面保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需定义id即可。
2 Java与json
Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为串行化Java对象为JSON字符串,或反串行化JSON字符串成Java对象。
2.1 Android
3 Python与json
使用pySerial可以使Python很好地在Windows/Linux/BSD上执行串口操作。
使用urllib2库时
#!/usr/bin/env python import json import urllib2 url="http://b.phodal.com/athome/1" date=urllib2.urlopen(url) result=json.load(date) print result
3.1 requests
Python 标准库中的 urllib2 模块提供了你所需要的大多数 HTTP 功能,但是它的 API 烂出翔来了。它是为另一个时代、另一个互联网所创建的。它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务。
GET请求
#!/usr/bin/env python import requests url = "http://b.phodal.com/athome/1" r = requests.get(url) print r.text
POST请求
#!/usr/bin/env python import requests url = "http://b.phodal.com/athome/1" data = {"temperature": 19, "sensors1": 32, "sensors2": 7.5, "led1": 0, "method": "PUT"} r = requests.put(url, data) print r.text
前端显示
1 库与车轮子
在多数的情况下我们都没有理由也没有必要去重新发明我们的车轮,在这时使用库会是一个比较好的做法。
2 库
2.1 jQuery Mobile
jQuery Mobile是jQuery 在手机上和平板设备上的版本。jQuery Mobile不仅会给主流移动平台带来jQuery核心库,而且会发布一个完整统一的jQuery移动UI框架。支持全球主流的移动平台。jQuery Mobile开发团队说:能开发这个项目,我们非常兴奋。移动Web太需要一个跨浏览器的框架,让开发人员开发出真正的移动Web网站。
3 网站前台显示
3.1 Highcharts
Highcharts有以下的特点
- 兼容性:兼容当今所有的浏览器,包括 iPhone、IE 和火狐等等;
- 对个人用户完全免费;
- 纯JS,无BS;
- 支持大部分的图表类型:直线图,曲线图、区域图、区域曲线图、柱状图、饼装图、散布图;
- 跨语言:不管是 PHP、Asp.net 还是 Java 都可以使用,它只需要三个文件:一个是Highcharts 的核心文件 highcharts.js,还有 a canvas emulator for IE 和 Jquery类库或者 MooTools 类库;
- 提示功能:鼠标移动到图表的某一点上有提示信息;
- 放大功能:选中图表部分放大,近距离观察图表;
- 易用性:无需要特殊的开发技能,只需要设置一下选项就可以制作适合自己的图表;
- 时间轴:可以精确到毫秒;
在这里只需将需要处理的数据存储到数组中,便可以将其渲染成为图形,下面的温度走势图便是基于Highcharts的结果:
var dataLength = [];
function drawTemp() {
var zero = [];
$.getJSON('/athome/', function(json) {
var items = [];
dataLength.push(json.length);
$.each(json, function(key, val) {
zero.push(val.temperature);
});
chart = new Highcharts.Chart({
color: {
linearGradient: {
x1: 0,
x2: 0,
y1: 0,
y1: 1
},
stops: [
[0, '#003399'],
[1, '#3366AA']
]
},
chart: {
renderTo: 'Tchart',
type: 'spline'
},
title: {
text: '本月温度情况'
},
subtitle: {
text: ''
},
xAxis: {
categories: [],
title: {
text: ''
}
},
yAxis: {
title: {
text: '温度 (°C)'
}
},
tooltip: {
backgroundColor: '#FCFFC5',
borderColor: 'black',
borderRadius: 10,
borderWidth: 1,
enabled: true,
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y + '°C';
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
x: -10,
y: 100,
borderWidth: 0
},
plotOptions: {
line: {
dataLabels: {
enabled: true
},
enableMouseTracking: false
}
},
series: [{
name: '本月',
data: zero
}, {
name: '对比',
data: [26.0]
}]
});
});
};
function showTemper() {
var length = dataLength[0];
$.ajax({
url: '/athome/' + length,
type: 'GET',
dataType: 'json',
async: true,
timeout: 1000,
error: function() {},
success: function(sdata) {
$('.temperStatus').empty();
$('.temperStatus').append(sdata.temperature);
}
});
};
$(document).ready(function() {
setInterval("drawTemp();", 5000);
setInterval("showTemper();", 800);
drawTemp();
showTemper();
});