從本節開始,將學習搭建一個問答模塊的站點,先開始咱們的註冊,登陸,郵箱驗證,信息提示。javascript
項目中,咱們使用Sendcloud做爲咱們的郵件代理服務器。
安裝sendcouldphp
$ composer require naux/sendcloud $ composer require guzzlehttp/guzzle
sendcloud詳細用法請訪問Github: naux/sendcloudcss
修改 config/app.php,添加服務提供者html
'providers' => [ // 添加這行 Naux\Mail\SendCloudServiceProvider::class, ];
在 .env
中配置你的密鑰, 並修改郵件驅動爲 sendcloudvue
MAIL_DRIVER=sendcloud SEND_CLOUD_USER= # 建立的 api_user SEND_CLOUD_KEY= # 分配的 api_key
vagrant@homestead:~/Code/my-app$ php artisan make:auth
laravel5.4
爲咱們帶了開箱即用的用戶註冊系統,只需一個操做命令,便可完成登陸模塊的建立。java
在GitHub安裝開源的信息提示包laracasts/flash
。node
composer require laracasts/flash
And then, if using Laravel 5, include the service provider within config/app.php.jquery
'providers' => [ Laracasts\Flash\FlashServiceProvider::class, ];
laracasts/flash詳細用法請訪問Github: laracasts/flashios
咱們能夠看到,提示的信息爲英文,咱們想轉爲中文,應該怎樣作呢?
GitHub 上有人專門爲此寫了一個擴展包 - Laravel-lang來對 Laravel 提供默認提示信息添加多語言版本翻譯。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
項目中咱們使用安正超同窗開源的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官網示例。
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
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圖片剪裁上傳組件
npm install vue-image-crop-upload // ES6 依賴 npm install babel-polyfill
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
$ composer require "overtrue/laravel-filesystem-qiniu"
在 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 ],
/** * 頭像上傳保存到本地服務器 */ 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
方法是否是簡潔了許多^_^