Gowhich

Durban's Blog

教程地址:http://www.golaravel.com/laravel/docs/5.1/quickstart/#validation

有兴趣的自己的可以去看下。

没事自己,也看了下,这个最近比较火的PHP框架,总体先不做评价,给那些入门后看了这个教程很困惑的人吧,包括我在内。

这个教程里面说的Validation,也不知道是哪个版本的,从链接地址上看出来是5.1这个版本的,但是我用的是5.2的版本,应该不是那种不兼容的问题。

只是在添加路由的过程中,只给除了如何添加路由但是没有说明白,如何去放置这些路由,由于这个是直接在routes.php中直接添加路由的,可能需要其他一切设置吧,毕竟这里的全局errors不应该就只是一个routes能解决的,我觉得。

先说下问题吧,说了这么多。

遇到的问题是:

Undefined variable: errors

不知道问题的,可以先试下对应链接里面的教程,走一遍就应该知道问题所在了,在提交错误的时候,会出现这个错误,当然还有另外一个错误,估计都是一个问题导致的。

解决办法是在stackoverflow找到的,唉,其实这个也是一个国外的框架,先不管看法的人是不是国内的,知道在国内还没有发现有人遇到这个问题,估计遇到的也不是很多。

解决问题的连接地址放在这里:

http://stackoverflow.com/questions/34420590/laravel-5-2-validation-errors

这里展示一下我修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Route::group(['middleware' => ['web']], function () {

Route::get('/', function () {
$tasks = Task::orderBy('created_at', 'asc')->get();
return view('tasks', [
'tasks' => $tasks
]);
});

Route::post('/task', function (Request $request) {

$validator = Validator::make($request->all(), [
'name' => 'required|max:255'
]);

if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}

$task = new Task;
$task->name = $request->name;
$task->save();

return redirect('/');
});

Route::delete('/task/{id}', function ($id) {
Task::findOrFail($id)->delete();

return redirect('/');
});
});

主要是把我们的Router放在

1
2
3
Route::group(['middleware' => ['web']], function () {

});

React-Native最近看了下他的ListView组件,记录下自己实现的小小功能:

一个简单的列表都没啥特别的了,这里添加一个导航。

首先使用NavigatorIOS组件,给我们的首页添加一个导航

1
2
3
4
5
6
7
8
9
10
11
12
var WalkerfreeProject = React.createClass({
render:function(){
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
'title':'Welcome',
'component': Movies,
}} />
);
}
});

如果只是就这么几行代码,还是有问题的,需要加一下样式

1
2
3
4
5
var styles = StyleSheet.create({
container: {
flex: 1,
}
});

最后组件注册

1
AppRegistry.registerComponent('WalkerfreeProject', () => WalkerfreeProject);

Movies这个就是一个ListView的组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
var React = require('react-native');

var {
AppRegistry,
Image,
ListView,
StyleSheet,
Text,
View,
NavigatorIOS,
ScrollView,
Navigator
} = React;

var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

var Movies = React.createClass({
getInitialState:function(){
return {
dataSource:new ListView.DataSource({
rowHasChanged:(row1, row2) => row1 != row2
}),
loaded:false
}
},
componentDidMount:function(){
this.fetchData();
},
fetchData:function(){
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource:this.state.dataSource.cloneWithRows(responseData.movies),
loaded:true
});
})
.done()
},
renderLoadingView:function(){
return (
<View style={styles.container}>
<Text>
Loading Views
</Text>
</View>
);
},
renderMovie:function(movie){
return (
<View style={styles.container} key={movie.id}>
<Image
source={{uri:movie.posters.thumbnail}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title} numberOfLines={1}>
{movie.title}
</Text>
<Text style={styles.year}>
{movie.year}
</Text>
</View>
<View style={styles.separator}></View>
</View>
);
},
render: function() {
if(!this.state.loaded){
return this.renderLoadingView();
}

return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView} />
);
}
});

var styles = StyleSheet.create({
separator: {
height: 1,
backgroundColor: '#CCCCCC',
},
listView:{
marginTop:65,
},
container: {
flex: 1,
},
thumbnail: {
width: 53,
height: 81,
},
rightContainer:{
flex:1,
position:'absolute',
top:0,
left:55
},
title:{
fontSize:20,
marginBottom:8,
textAlign:'left',
width:260
},
year:{
textAlign:'center'
}
});

module.exports = Movies;

整个代码就贴到这里了,没有啥需要说明的。这个官网也是有的,只是注意一下这里的样式就好了。当然在index.ios.js的里面要引入

1
var Movies = require('./App/Movies');

这个我是他Movies添加到了App这个目录里了,所以需要自己引入一下。其实也还是蛮简单的,因为就是一个简单的例子而已

在nodejs里面通过html的表单上传图片,跟php比较起来还是麻烦一些,特别是在react中使用表单提交含有文件的数据。

最近也在做这个操作,找了很多资料,一直都是有个co-busboy这个,其实它也是基于busboy去封装的,不过这个更适合在koa里面使用。

以往的表单提交,我们用浏览器去debug的时候,会看到,提交了一个post提交的数据,这个一般只是值的提交,不包括有文件,但是在react中,

好像就更加复杂了一些,对于表单提交要单独自己写一个提交的事件去处理,累死submit这样的jquey函数。

而且提交成功后,我们用chrome去debug的时候,会发现提交的是一个Request Payload这样的,跟Form Data不一样,而且我们在koa中,

用body也是获取不到对应的值的,一切都是繁琐加着繁琐,自己考虑一下,像php这样的,是人家都帮你做好了,用起来很顺手,最终需要解决的就是,

如果把提交的data从header中去解析出来,其实busboy做的还是很好的。下面我大概说下我是如何去操作的。

web使用的jquery,通过FormData

在componentDidMount方法中初始化一下submit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
let handleAction = this.props.handleAction;
$("#BackgroundForm").submit(function(e){
let formData = new FormData($(this)[0]);
$.ajax({
url:'/theapp/upload/background',
data:formData,
dataType:'json',
type:'post',
cache: false,
contentType: false,
processData: false,
beforeSend:function(){
$('#submitButton').attr('disabled',true);
},
success:function(data){
if(data.success == true){
handleAction(true);
$('#errorContainer').html('').hide();
}else if(data.success == false){
let message = '<ul><li>'+data.message+'!</li></ul>';
$('#errorContainer').html(message).show();
$('#submitButton').attr('disabled',false);
}
},error:function(err){
console.log(err);
}
});
return false;
});

在server中的操作是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const upload = function (ctx, key, streamName) {
return new Promise(function (resolve, reject) {
ctx.qiniuClient.upload(fs.createReadStream(streamName), key, function (err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
theapp.post('/upload/background', co.wrap(function *(ctx, next) {
let parts = parse(ctx.req);
let part;
//初始化数据
let title = '';
let theme = '';
let theorder = '';
let token = '';
let big_url;
let thumb_url;
//获取form-data提交的数据
while (part = yield parts) {
if (Array.isArray(part)) {
let name = part[0];
let value = part[1];
if (name == 'title') {
title = value;
}
if (name == 'theme') {
theme = value;
}
if (name == 'theorder') {
theorder = value;
}
if (name == 'access_token') {
token = value;
}
} else {
let streamName = '/tmp/' + Math.random();
let stream = fs.createWriteStream(streamName);
part.pipe(stream);
if (part.fieldname == 'big_url') {
big_url = streamName;
}
if (part.fieldname == 'thumb_url') {
thumb_url = streamName;
}
}
}
if (!big_url) {
throw new Error('请上传背景大图');
}
if (!thumb_url) {
throw new Error('请上传背景小图');
}
if (!title) {
fs.unlinkSync(big_url);
fs.unlinkSync(thumb_url);
throw new Error('名称不能为空');
}
if (!theme) {
fs.unlinkSync(big_url);
fs.unlinkSync(thumb_url);
throw new Error('主题不能为空');
}
if (!theorder) {
theorder = 0;
}
//名称判断存在
const url = ctx.config.hostDomain + '/admin/query';
const sql = `SELECT * FROM qeeniao.app_dft_background WHERE title = '${title}'`;
const options = {
timeout: ctx.config.httpTimeout,
method: 'POST',
data: {'sql': sql, 'access_token': token}
};
const requestData = yield urllib.request(url, options);
let data = requestData.data.toString();
data = JSON.parse(data);
if (data.length > 0) {
fs.unlinkSync(big_url);
fs.unlinkSync(thumb_url);
throw new Error('名称已经存在');
}
//上传图片
let big_res = yield upload(ctx, {
'key': 'image/background/hd/' + title + '.jpg'
}, big_url);
big_url = big_res['url'];
let thumb_res = yield upload(ctx, {
'key': 'image/background/thumbnail/' + title + '.jpg'
}, thumb_url);
thumb_url = thumb_res['url'];
//提交数据库
let postData = {
'title': title,
'theme': theme,
'theorder': theorder,
'big_url': big_url,
'thumb_url': thumb_url,
'access_token': token
};
let postUrl = ctx.config.hostDomain + '/admin/add/background';
let postoptions = {
timeout: ctx.config.httpTimeout,
method: 'POST',
data: postData
};
let postRes = yield urllib.request(postUrl, postoptions);
postRes = postRes.data.toString();
postRes = JSON.parse(postRes);
ctx.body = postRes;
}));

这里使用了一个qn的库

在启动的时候我把他加入了context中

1
2
3
4
5
6
app.context.qiniuClient = qn.create({
accessKey: "Your accessKey",
secretKey: "Your secretKey",
bucket: "Your bucket",
origin: 'Your origin'
});

这样处理起来就没问题了,其实要是如果koa把busboy自己封装起来是最好的。

在React中使用qiniu上传图片,实现方式有很多种,一种是在web端实现上传,一种是在server端实现上传

这里我说下我是如何在web端实现上传图片到七牛的

七牛的官方已经有了javascript的使用说明我这里就不重复了。

使用React首先是要把qiniu提供的js引入,由于我这里是使用的webpack,所以我就直接放在了引入文件中,

这样我就可以全局引用了。

1
require('./qiniu.js');

在组件中把qiniu的对象引入进来

1
const Qiniu = require('qiniu');

然后在componentDidMount这个方法中去初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//七牛上传大图
let uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles',
uptoken : this.props.uptoken,
domain: 'http://7u2r0u.com2.z0.glb.qiniucdn.com/',//这里换成自己的
container: 'app-background-container',
max_file_size: '100mb',
flash_swf_url: 'https://cdn.bootcss.com/plupload/2.1.7/Moxie.swf',
max_retries: 3,
dragdrop: true,
drop_element: 'app-background-container',
chunk_size: '4mb',
auto_start: true,
init: {
'FilesAdded': function(up, files) {
console.log('上传 FilesAdded');
plupload.each(files, function(file) {
// 文件添加进队列后,处理相关的事情
console.log(file);
});
},
'BeforeUpload': function(up, file) {
// 每个文件上传前,处理相关的事情
console.log('上传 Before Upload');
console.log(up);
console.log(file);
$('#app-background-container .showpick').hide();
$('#big_url').val('');
},
'UploadProgress': function(up, file) {
console.log('上传 UploadProgress');
console.log(up);
console.log(file);
},
'FileUploaded': function(up, file, info) {
console.log('====上传完成====');
let domain = up.getOption('domain');
let res = JSON.parse(info);
let sourceLink = domain + res.key;
console.log(sourceLink);
$('#app-background-container .showpick').attr('href',sourceLink);
$('#app-background-container .showpick img').attr('src',sourceLink);
$('#big_url').val(sourceLink);
$('#app-background-container .showpick').show();
},
'Error': function(up, err, errTip) {
//上传出错时,处理相关的事情
console.log('====上传失败====');
console.log(err);
console.log(errTip);
},
'UploadComplete': function() {
console.log('====上传完毕====');
}
}
});

在render中需要把

1
2
3
4
<a className="btn btn-default btn-primary" id="pickfiles" href="#">
<i className="glyphicon glyphicon-plus"> </i>
<span>上传背景大图</span>
</a>

放进去,一定要注意id的值

千万别忘记了plupload这个插件,自己去google下就可以找到的,因为我把这个plupload做为一个externals,

所以在webpack的config中加入了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
externals:{
'moment': true,
'jquery':'jQuery',
'plupload':true,//重点在这里
'format':true,
'bootstrap':true,
'fancybox':true,
'co':true,
'_':'lodash',
'async':true,
'datetimepicker':true,
'selectpicker':true,
'sweetalert':true,
'highcharts':'Highcharts',
'director':'Router'
},

执行如下代码:

1
git config core.ignorecase false

另外一个问题

当我们遇到文件大小写的时候,当在编辑器改完之后,提交代码,实际上仓库中并没有改变这个文件的大小写,可以通过下面的方式来修改

1
git mv A B

实例演示如下,如果我有个文件是

components/StatCNZZWidget.php

想要修改为

components/StatCnzzWidget.php

执行如下代码

1
$ git mv components/StatCNZZWidget.php components/StatCnzzWidget.php

当执行如下代码的时候

1
$ git status

会有如下类似的输出

1
renamed:    components/StatCNZZWidget.php -> components/StatCnzzWidget.php

最近打开gist.github.com就会出现问题,打不开,记录一个推荐的办法,很有效果

1
2
3
192.30.252.141 gist.github.com    
185.31.17.184 github.global.ssl.fastly.net
185.31.17.184 github-camo.global.ssl.fastly.net

前文:

别傻了,还通过源码去安装,很费时间的,而且安装完你还要去考虑本地的gcc版本问题,这里给大家提供一个很简答的方法.

第一部分:升级到4.7

1
2
3
cd /etc/yum.repos.d
wget http://people.centos.org/tru/devtools-1.1/devtools-1.1.repo
yum --enablerepo=testing-1.1-devtools-6 install devtoolset-1.1-gcc devtoolset-1.1-gcc-c++

这个将安装的文件放在了

1
/opt/centos/devtoolset-1.1

如果想要编辑器去处理的话,这样操作

1
2
3
export CC=/opt/centos/devtoolset-1.1/root/usr/bin/gcc  
export CPP=/opt/centos/devtoolset-1.1/root/usr/bin/cpp
export CXX=/opt/centos/devtoolset-1.1/root/usr/bin/c++

如果你想要gcc替换本地的,当然不是真的去替换,只要把他放在我们的/usrlocal/bin下面就好了,不必去管系统自带的【/usr/bin】。

1
2
3
ln -s /opt/rh/devtoolset-1.1/root/usr/bin/* /usr/local/bin/
hash -r
gcc --version

第二部分:升级到4.8【这个应该是目前最新的啦,不过网上查的话已经到5.2啦,感觉落后一点比较稳,当然还有就是这个版本是新的里面使用最多的】

1
wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo

1
2
cd /etc/yum.repos.d
wget http://people.centos.org/tru/devtools-2/devtools-2.repo

然后

1
yum install devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++

这个将安装的文件放在了

1
/opt/rh/devtoolset-2

如果想要编辑器去处理的话,这样操作

1
2
3
export CC=/opt/rh/devtoolset-2/root/usr/bin/gcc  
export CPP=/opt/rh/devtoolset-2/root/usr/bin/cpp
export CXX=/opt/rh/devtoolset-2/root/usr/bin/c++

如果你想要gcc替换本地的,当然不是真的去替换,只要把他放在我们的/usrlocal/bin下面就好了,不必去管系统自带的【/usr/bin】。

1
2
3
ln -s /opt/rh/devtoolset-2/root/usr/bin/* /usr/local/bin/
hash -r
gcc --version

这个两个部分的路径变了【请看这里】:http://people.centos.org/tru/devtools-2/readme

参考资料:http://superuser.com/questions/381160/how-to-install-gcc-4-7-x-4-8-x-on-centos

使用react的时候,总有一些变量是html的字符串,但是我们却想要实现innerHtml的类似方法,直接用html进行渲染。

这里react提供了一个dangerouslySetInnerHTML方法,可以实现此赋值操作。具体详情可以自己去google一个下,关键字:’react dangerouslySetInnerHTML’.

下面是我为记录的一个示例,可以作为一个简单的demo了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const Login = React.createClass({
getInitialState: function () {
return {
'error_state': false,
'error_message': ''
}
},
handleClick:function(){
let error_message = '';
error_message += '<li>错误信息一</li>';
error_message += '<li>错误信息二</li>';
error_message += '<li>错误信息三</li>';
this.setState({
'error_state': true,
'error_message': error_message
});
},
render:function(){
let alert_class = this.state.error_state ? 'alert alert-danger' : 'aler hidden';
return (
<div>
<button onClick={this.handleClick}>添加html</button>
<div className={alert_class} dangerouslySetInnerHTML={{__html:this.state.error_message}} ></div>
</div>
);
}
});

首先安装验证码插件

这里推荐使用ccap,这个插件是我在寻找过程中,觉得能跟koa搭配比较好的一个插件,其他的要不就是需要express,要不就是需要安装其他一系列比较大的类库。

1
npm install ccap --save

如何使用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const ccap = require('ccap')();
home.get('/home', (ctx, next) => {
return next().then(() => {
ctx.body = ctx.session.captcha;
});
});
home.get('/captcha', (ctx, next) => {
return next().then(() => {
let ary = ccap.get();
let txt = ary[0];
let buf = ary[1];
ctx.body = buf;
ctx.type = 'image/png';
ctx.session.captcha = txt;
});
});
0%