从本节开始,将学习搭建一个问答模块的站点,先开始我们的注册,登录,邮箱验证,信息提示。
一、邮件扩展包Sendcould
项目中,我们使用Sendcloud作为我们的邮件代理服务器。
安装sendcould
$ composer require naux/sendcloud
$ composer require guzzlehttp/guzzle
sendcloud详细用法请访问Github: naux/sendcloud
修改 config/app.php,添加服务提供者
'providers' => [
// 添加这行
Naux\Mail\SendCloudServiceProvider::class,
];
在 .env
中配置你的密钥, 并修改邮件驱动为 sendcloud
MAIL_DRIVER=sendcloud
SEND_CLOUD_USER= # 创建的 api_user
SEND_CLOUD_KEY= # 分配的 api_key
二、命令自动生成注册登录模块
vagrant@homestead:~/Code/my-app$ php artisan make:auth
laravel5.4
为我们带了开箱即用的用户注册系统,只需一个操作命令,即可完成登录模块的创建。
三、信息提示
在GitHub安装开源的信息提示包laracasts/flash
。
composer require laracasts/flash
And then, if using Laravel 5, include the service provider within config/app.php.
'providers' => [
Laracasts\Flash\FlashServiceProvider::class,
];
laracasts/flash详细用法请访问Github: laracasts/flash
四、多语言翻译包
我们可以看到,提示的信息为英文,我们想转为中文,应该怎样做呢?
GitHub 上有人专门为此写了一个扩展包 - Laravel-lang来对 Laravel 提供默认提示信息添加多语言版本翻译。
接下来让我们使用Composer
来安装 Laravel-lang
。
$ composer require "caouecs/laravel-lang:~3.0"
安装后的 laravel-lang
扩展包的所有核心文件都将被放置在 vendor/
文件夹下,其中包括我们需要的中文语言包,让我们将中文语言包提取到 Laravel 默认指定的语言包存放路径 resources/lang
中。
$ cp -a vendor/caouecs/laravel-lang/src/zh-CN resources/lang
完成之后你便可在 resources/lang/zh-CN
文件夹中看到我们新增的语言包文件。
最后,我们还需要将项目语言设置为中文。
config/app.php
<?php
return [
.
.
.
'locale' => 'zh-CN',
.
.
.
];
现在再次提交验证不通过的信息,能看到错误提示已变成中文。
Laravel-lang 详细用法请访问 Github:Laravel-lang
五、laravel-ueditor编辑器
项目中我们使用安正超同学开源的overtrue/laravel-ueditor编辑器进行开发。
使用方法:
安装
$ composer require "overtrue/laravel-ueditor:~1.0"
配置
添加下面一行到 config/app.php
中 providers
部分:
Overtrue\LaravelUEditor\UEditorServiceProvider::class,
发布配置文件与资源
$ php artisan vendor:publish
模板引入编辑器
这行的作用是引入编辑器需要的 css,js 等文件,所以你不需要再手动去引入它们。
@include('vendor.ueditor.assets')
编辑器的初始化
<!-- 实例化编辑器 -->
<script type="text/javascript">
var ue = UE.getEditor('container');
ue.ready(function() {
ue.execCommand('serverparam', '_token', '{{ csrf_token() }}'); // 设置 CSRF token.
});
</script>
<!-- 编辑器容器 -->
<script id="container" name="content" type="text/plain"></script>
laravel-ueditor详细用法请访问Github: overtrue/laravel-ueditor
六、select2选择框使用
很好用的一款选择框组件,详细用法请看Select2官网示例。
CDN:
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
用法:
<script type="text/javascript">
// 初始化
$('select').select2();
</script>
简单示例:
<html>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<select class="js-example-basic-multiple" multiple="multiple">
<option value="AL">Alabama</option>
<option value="WY">Wyoming</option>
</select>
<script type="text/javascript">
$(".js-example-basic-multiple").select2();
</script>
</html>
或者我们可以将上边的CDN资源文件通过curl -O
命令下载到本地项目目录中:
cd resources/assets/sass/
mk css
~/Code/zhihu-app/resources/assets/sass/css$ curl -O https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css
// 下载js文件
~/Code/zhihu-app/resources/assets/js$ curl -O https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js
实例应用
在 laravel5.4
中应用 select2
插件:
将 select2.min.css
和 select2.min.js
文件经过 gulp
编译后生成app.css
和 app.js
打包后的文件,然后在总视图布局文件/layouts/app.blade.php
中应用,
<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ elixir('css/app.css') }}" rel="stylesheet">
<!-- Scripts -->
<script>
window.Laravel = {!! json_encode([
'csrfToken' => csrf_token(),
]) !!};
</script>
</head>
<body>
<div id="app">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<!-- Collapsed Hamburger -->
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Branding Image -->
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
</div>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@if (Auth::guest())
<li><a href="{{ route('login') }}">Login</a></li>
<li><a href="{{ route('register') }}">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>
@include('shared.messages')
@yield('content')
</div>
<!-- Scripts -->
<script src="{{ elixir('js/app.js') }}"></script>
@yield('js')
<script>
$('#flash-overlay-modal').modal();
</script>
</body>
</html>
在底部引用app.js
代码,并添加区块布局 yield('js')
,在继承该总布局页面时,有关应用js代码的需要放在 @section('js') 如select2 @endsection
中,如下面的这个子页面 create.blade.php
。
@extends('layouts.app')
@section('content')
@include('vendor.ueditor.assets')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">发布问题</div>
<div class="panel-body">
@include("shared.errors")
<form action="/questions" method="post">
{{ csrf_field() }}
<div class="form-group">
<label for="title">标题</label>
<input type="text" name="title" value="{{ old('title') }}" class="form-control" placeholder="标题" id="title">
</div>
<div class="form-group">
<select class="js-example-basic-multiple form-control" multiple="multiple">
<option value="AL">Alabama</option>
<option value="WY">Wyoming</option>
</select>
</div>
<!-- 编辑器容器 -->
<label for="title">内容</label>
<script id="container" name="body" style="height:200px" type="text/plain">
{!! old('body') !!}
</script>
<button class="btn btn-success pull-right" type="submit">发布问题</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- 实例化编辑器 -->
@section('js')
<script type="text/javascript">
var ue = UE.getEditor('container', {
toolbars: [
['bold', 'italic', 'underline', 'strikethrough', 'blockquote', 'insertunorderedlist', 'insertorderedlist', 'justifyleft','justifycenter', 'justifyright', 'link', 'insertimage', 'fullscreen']
],
elementPathEnabled: false,
enableContextMenu: false,
autoClearEmptyNode:true,
wordCount:false,
imagePopup:false,
autotypeset:{ indent: true,imageBlockLine: 'center' }
});
ue.ready(function() {
ue.execCommand('serverparam', '_token', '{{ csrf_token() }}'); // 设置 CSRF token.
});
// select2,如果没有预加载ready,否则不会出现
$(document).ready(function () {
$(".js-example-basic-multiple").select2();
});
</script>
@endsection
@endsection
七、axios HTTP请求包
Promise based HTTP client for the browser and node.js
Github地址:mzabriskie/axios
安装:
$ npm install axios
laravel5.4开始使用 axios
做http请求,如果用旧的请求方法,会报这样的错误:
Uncaught TypeError: Cannot read property 'post' of undefined
具体的代码:
export default {
props:['question', 'user'],
mounted() {
/** 这种旧的写法会在Laravel5.4中报错
this.$http.post('/api/question/follower', {'question':this.question, 'user':this.user}).then(response => {
console.log(response.data);
})
*/
axios.post('/api/question/follower', {
'question':this.question,
'user':this.user
}).then(function(response){
console.log(response.data);
})
},
八、vue图片剪裁上传组件
vue图片剪裁上传组件: vue图片剪裁上传组件
1.npm 安装
npm install vue-image-crop-upload
// ES6 依赖
npm install babel-polyfill
2.用法
Example vue@2示例:
<div id="app">
<a class="btn" @click="toggleShow">set avatar</a>
<my-upload field="img"
@crop-success="cropSuccess"
@crop-upload-success="cropUploadSuccess"
@crop-upload-fail="cropUploadFail"
v-model="show"
:width="300"
:height="300"
url="/upload"
:params="params"
:headers="headers"
img-format="png"></my-upload>
<img :src="imgDataUrl">
</div>
<script>
import 'babel-polyfill'; // es6 shim
import Vue from 'vue';
import myUpload from 'vue-image-crop-upload/upload-2.vue';
new Vue({
el: '#app',
data: {
show: true,
params: {
token: '123456798',
name: 'avatar'
},
headers: {
smail: '*_~'
},
imgDataUrl: '' // the datebase64 url of created image
},
components: {
'my-upload': myUpload
},
methods: {
toggleShow() {
this.show = !this.show;
},
/**
* crop success
*
* [param] imgDataUrl
* [param] field
*/
cropSuccess(imgDataUrl, field){
console.log('-------- crop success --------');
this.imgDataUrl = imgDataUrl;
},
/**
* upload success
*
* [param] jsonData server api return data, already json encode
* [param] field
*/
cropUploadSuccess(jsonData, field){
console.log('-------- upload success --------');
console.log(jsonData);
console.log('field: ' + field);
},
/**
* upload fail
*
* [param] status server api return error status, like 500
* [param] field
*/
cropUploadFail(status, field){
console.log('-------- upload fail --------');
console.log(status);
console.log('field: ' + field);
}
}
});
</script>
将上边的代码应用到组件 Avatar.vue
中,需要对相关的方法按照组件的要求改一改,data
需要使用函数 return
进行返回。
<template>
<div style="text-align:center;">
<my-upload field="img"
@crop-success="cropSuccess"
@crop-upload-success="cropUploadSuccess"
@crop-upload-fail="cropUploadFail"
v-model="show"
:width="300"
:height="300"
url="/avatar"
:params="params"
:headers="headers"
img-format="png"></my-upload>
<img :src="imgDataUrl" style="width:80px;">
<div style="margin-top:20px;">
<button class="btn btn-default" @click="toggleShow">修改头像</button>
</div>
</div>
</template>
<script>
import 'babel-polyfill'; // es6 shim
import myUpload from 'vue-image-crop-upload/upload-2.vue';
export default {
props:['avatar'],
data() {
return {
show: false,
params: {
_token:Laravel.csrfToken,
name: 'img',
},
headers: {
smail: '*_~'
},
imgDataUrl: this.avatar // the datebase64 url of created image
}
},
components: {
'my-upload': myUpload
},
methods: {
toggleShow() {
this.show = !this.show;
},
/**
* crop success
*
* [param] imgDataUrl
* [param] field
*/
cropSuccess(imgDataUrl, field){
console.log('-------- crop success --------');
this.imgDataUrl = imgDataUrl;
},
/**
* upload success
*
* [param] jsonData server api return data, already json encode
* [param] field
*/
cropUploadSuccess(response, field){
console.log('-------- upload success --------');
this.imgDataUrl = response.url;
// 上传成功后,影藏掉
this.toggleShow();
},
/**
* upload fail
*
* [param] status server api return error status, like 500
* [param] field
*/
cropUploadFail(status, field){
console.log('-------- upload fail --------');
console.log(status);
console.log('field: ' + field);
}
}
}
</script>
将 Avatar.vue
引入app.js
文件中
// 设置头像
Vue.component('avatar', require('./components/Avatar.vue'));
const app = new Vue({
el: '#app'
});
在视图文件 avatar.blade.php
中使用 avatar
组件
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">更换头像</div>
<div class="panel-body">
<avatar avatar="{{ Auth::user()->avatar }}"></avatar>
</div>
</div>
</div>
</div>
</div>
@endsection
服务器的保存控制器UsersController.php
方法:
/**
* 头像上传保存到本地服务器
*/
public function avatarUpload(Request $request)
{
// 获取图片文件对象
$file = $request->file('img');
// 文件名
$filename = md5(time() . user()->id) . '.' . $file->getClientOriginalExtension();
$file->move(public_path('avatars'), $filename);
// 修改用户的头像
// user()->avatar = asset(public_path('avatars/'.$filename));
user()->avatar = '/avatars/'.$filename; // 相对路径
user()->save();
return ['url' => user()->avatar];
}
Github地址:https://github.com/dai-siki/v...
九、图片上传到七牛云
七牛云扩展包GitHub地址:laravel-filesystem-qiniu
1.安装
$ composer require "overtrue/laravel-filesystem-qiniu"
2.配置
在 config/app.php
文件中配置:
'providers' => [
// Other service providers...
Overtrue\LaravelFilesystem\Qiniu\QiniuStorageServiceProvider::class,
],
在 config/filesystems.php
中配置:
'qiniu' => [
'driver' => 'qiniu',
'access_key' => env('QINIU_ACCESS_KEY', 'xxxxxxxxxxxxxxxx'),
'secret_key' => env('QINIU_SECRET_KEY', 'xxxxxxxxxxxxxxxx'),
'bucket' => env('QINIU_BUCKET', 'test'),
'domain' => env('QINIU_DOMAIN', 'xxx.clouddn.com'), // or host: https://xxxx.clouddn.com
],
3.具体用法:
/**
* 头像上传保存到本地服务器
*/
public function avatarUpload(Request $request)
{
// 获取图片文件对象
$file = $request->file('img');
/*
* 本地存储图片
// 文件名
$filename = md5(time().user()->id) . '.' . $file->getClientOriginalExtension();
// 将图片保存到本地
// $file->move(public_path('avatars'), $filename);
// 修改用户的头像
// user()->avatar = asset(public_path('avatars/'.$filename));
user()->avatar = '/avatars/'.$filename; // 相对路径
*/
// 将图片保存到七牛[20170405]
$filename = 'avatars/' . md5(time().user()->id) . '.' . $file->getClientOriginalExtension();
Storage::disk('qiniu')->writeStream($filename, fopen($file->getRealPath(), 'r'));
user()->avatar = 'http://'.config('filesystems.disks.qiniu.domain') . '/' . $filename;
user()->save();
return ['url' => user()->avatar];
}
重构提交数据的验证
我们需要对客户端提交的数据进行验证,验证通过后才能入库,如下边的发表一篇文章为例,通常的写法是在store
方法中获取提交的数据,之后再通过规则验证valigate
,不过这里我们有一个更好的方法,那就是使用依赖注入
。
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
// $answer = $request->all();
// dd($answer);
// 验证提交的数据
$rules = [
'title' => 'required|min:6|max:150',
'body' => 'required|min:40'
];
// 自定义消息提示
$messages = [
'body.required' => "内容 不能为空。",
'body.min' => "内容 不能少于40个字符。",
];
$this->validate($request, $rules, $messages);
$data = [
'title' => $request->get('title'),
'body' => $request->get('body'),
'user_id' => Auth::id(),
];
$question = Question::create($data);
flash("恭喜你,发布成功!", "success");
return redirect()->route('questions.show', [$question->id]);
}
上边的方法,是将验证规则写在store方法里边的,我们可以通过依赖注入对上边的方法进行重构,先使用命令生成request。
php artisan make:request StoreQuestionRequest
将表单的验证写入用命令生成的Http\Requests\StoreQuestionRequest.php
文件
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreQuestionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Valigate messages
*
* @return array
*/
public function messages()
{
return [
'body.required' => "内容 不能为空。",
'body.min' => "内容 不能少于40个字符。",
];
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|min:6|max:150',
'body' => 'required|min:40'
];
}
}
将表单验证注入到接收的参数:
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(StoreQuestionRequest $request)
{
// $this->validate($request, $rules, $messages);
$data = [
'title' => $request->get('title'),
'body' => $request->get('body'),
'user_id' => Auth::id(),
];
$question = Question::create($data);
flash("恭喜你,发布成功!", "success");
return redirect()->route('questions.show', [$question->id]);
}
这个方法里边的参数store(StoreQuestionRequest $request)
即可对传递过来的参数进行验证,重构之后,我们的store
方法是不是简洁了许多^_^
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。