function () use ($x, &$y){}
自從PHP5.3開始有了closure/匿名函數的概念,在這裏的use關鍵詞的做用是容許匿名函數capture到父函數scopejavascript
內存在的$x和$y變量。其中&&y爲引用方式capture,也就是說每次該匿名函數調用時,y的值若是php
被修改了也反映在這裏,而$x則是靜態引用。css
<?php $message = "hello\n"; $example = function () { echo $message; }; // Notice: Undefined variable: message $example(); $example = function () use ($message) { echo $message; }; // "hello" $example(); // Inherited variable's value is from when the function is defined, not when called $message = "world\n"; // "hello" $example(); // Inherit by-reference $message = "hello\n"; $example = function () use (&$message) { echo $message; }; // "hello" $example(); // The changed value in the parent scope is reflected inside the function call $message = "world\n"; // "world" $example(); // Closures can also accept regular arguments $example = function ($arg) use ($message) { echo $arg . ' ' . $message; }; // "hello world" $example("hello");
psr2是一種編碼規範,html
PSR0,PSR4是PHP的autoloading機制中的目錄安排規範前端
詳情: vue
https://github.com/php-fig/fig-standards/tree/master/acceptedjava
1.規劃設計routes: /tasks ; /alice/tasks node
Route::get('/tasks','TasksController@index');
2.建立上述controllermysql
php artisan make:controller TasksController Controller created successfully. app/Http/Controllers/TasksController.php created
3.建立Task modelreact
$ php artisan make:model Task
Model created successfully.
4. 建立tasks表
$ php artisan make:migration create_tasks_table --create --table="tasks" Created Migration: 2016_04_09_134106_create_tasks_table
$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_04_09_134106_create_tasks_table
注意:因爲laravel自帶了users和password_resets兩個migration,因此咱們執行php artisan migrate時有了3個表被建立
5. 建立view並在blade模版視圖resources.tasks.index中引用模型數據
@foreach($tasks as $task) <li> <a href="{{ url('/tasks',$task->id) }}">{{ $task->title }}</a> </li> @endforeach
6.在控制器中獲取model數據而且assign給blade模版視圖resources.tasks.index
class TasksController extends Controller { public function index(){ $tasks = Task::all(); return View::make('tasks.index',compact('tasks')); } }
至此,tasks index 頁面的功能已經設計完畢。
從第5步驟咱們看到,因爲咱們在視圖中對每個task都建立了對應的連接,而該連接的頁面url爲/tasks/{id},所以從這裏咱們會發現咱們接下來的工做是設計show頁面!@!
7.回到routes.php,咱們建立show頁面的路由:
Route::get('/tasks/{task}','TasksController@show');
8,這時咱們在index頁面點擊連接時會報錯「TasksController::show() does not exist」, 這也就告訴咱們須要建立show方法
public function show(Task $task){ return View::make('tasks.show',compact('task')); }
注意在這裏咱們使用了laravel5提供的route model binding特性,咱們在控制器中使用Task類typehinting了$task參數,而該$task參數和routes.php中定義的wildcast路由Route::get('tasks/{task}','xxx'}定義的task相匹配,所以laravel在調用咱們的控制器時自動注入Task模型(以id爲索引)。這個功能後續再作進一步的深刻。
9,這時咱們剩下來的工做就是設計show頁面模版了:
<div class="task"> <h1>{{$task->title}}</h1> <h2>{{$task->description}}</h2> </div>
至此,咱們就完成了一個簡單的task crud的一部分功能的完整開發了。
可是上述兩個頁面存在html重複的問題,咱們能夠抽象出一個layout模版,由tasks.index和tasks.show兩個頁面來共享
<?php $name = 'kitty'; $htmlstring = <<<Eof <table height="20"> <tr><td> {$name}<br/> <script> var p='hello world'; document.writeln(p); </script> </td></tr> </table> Eof;
能夠參考http://www.4wei.cn/archives/1001469詳情
有一點須要注意應該使用命令的全名稱(包含路徑),不然可能出問題: 執行sudo命令時command not found的解決辦法
編輯sudoers文件,註釋掉Defaults requiretty這行
不然會出現sudo: sorry, you must have a tty to run sudo的錯誤
再添加一行:
apache ALL=(ALL) NOPASSWD:ALL
這行中apache是laravel運行時的用戶名,若是你不清楚到底apache/ngix用戶名是什麼能夠用php的echo shell_exec("id -a")打印出來
這一行主要解決使用sudo命令時要求輸入密碼,而咱們在網站程序中不可能輸入密碼的
$php artisan make:controller --resource task/TasksController
resource controller中默認產生如下幾個路由項:
+--------+-----------+--------------------+---------------+----------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+--------------------+---------------+----------------------------------------------+------------+ | | GET|HEAD | tasks | tasks.index | App\Http\Controllers\TasksController@index | web | | | POST | tasks | tasks.store | App\Http\Controllers\TasksController@store | web | | | GET|HEAD | tasks/create | tasks.create | App\Http\Controllers\TasksController@create | web | | | DELETE | tasks/{tasks} | tasks.destroy | App\Http\Controllers\TasksController@destroy | web | | | PUT|PATCH | tasks/{tasks} | tasks.update | App\Http\Controllers\TasksController@update | web | | | GET|HEAD | tasks/{tasks} | tasks.show | App\Http\Controllers\TasksController@show | web | | | GET|HEAD | tasks/{tasks}/edit | tasks.edit | App\Http\Controllers\TasksController@edit | web | +--------+-----------+--------------------+---------------+----------------------------------------------+------------+
laravel中web middle會對Post操做作保護,必須有一個csrf的隱藏域放在form post data中,laravel才容許向下走。爲了讓laravel經過這個保護驗證,咱們須要在form中經過{{ csrf_field() }}來完成這個數據的存放:
{{ csrf_field() }} //會在form中產生如下field: <input type="hidden" name="_token" value="cbgekcEhbraIiU0ZaeNFgyQ5OIpg8xjIpBb7AXv9">
因爲全部瀏覽器對於form提交時只支持get和post兩種method,而咱們的REST API定義是由put,patch,delete,get,post完備的五種方法來實現的,一個workaround方案是和csrf相似,增長一個隱藏field , _method,
laravel也提供了對應的helper函數來完成這個工做:
{{ method_field('delete') }}
上面的blade模板編譯爲:
<input type="hidden" name="_method" value="delete">
這樣操做之後,即便咱們提交表單時使用的是post方法,可是laravel會檢查這個_method field,根據其對應的value來匹配到對應的路由,而且route到對應的控制器方法:好比update和destroy方法
有時候,咱們不但願在laravel的模版引擎中解析對應的{{}}內部的內容,好比vuejs,angularjs均可能使用{{}}做爲他們的前端模版的輸出函數,這時,咱們就但願blade不要畫蛇添足地在服務端渲染,這個{{}}留到前端去解析渲染吧。很簡單隻要加上@{{}},laravel就會忽略這裏的{{}}
@{{ data_to_web_page_for_javascript }}
在一個全新的laravel項目下載後,經過執行npm install來安裝構建工具,windows下可能在安裝過程當中會出現問題,一個可能的緣由是你的git bash/cmd運行於administrator用戶下,而gulp-sass在安裝時只能用普通用戶權限下安裝。
另一點是在安裝該工具後,有可能你有本身的gulpfile,這時但願二者均可用的話,能夠經過--gulpfile來指定你本身的或者laravel自帶的gulpfile
gulp --gulpfile yourgulpfile.js
若是在執行npm install時,因爲git默認狀況下對文件名的長度是有限制的,那麼就有可能node module嵌入深度過長致使git add出錯:
fatal: cannot create directory at 'laravel-elixir-vueify/node_modules/babel-preset-es2015/node_modules/babel-plugin-transform-es2015-block-scoping/ node_modules/babel-traverse/node_modules/babel-code-frame/node_modules/chalk/node_modules/ansi-styles': Filename too long
能夠經過設置git core的核心參數longpaths 爲true打開git不限制文件長度的核心功能
[core] autocrlf = true filemode = false longpaths = true
ErrorException in Filesystem.php line 109: file_put_contents(/assets/www/newkidsitl5229/bootstrap/cache/services.php): failed to open stream: Permission denied
解決辦法:
php artisan cache:clear
static::引用的是全局做用域,而self::引用的是對本類的靜態方法的引用。好比一個類中定義了static方法或者屬性,子類中又重寫了這個static屬性,則static::staticProperty就引用的是子類的重寫值,而selft::staticProperty則返回在本身類做用域中的static
參考:
https://segmentfault.com/q/1010000002468880
class A { public static function get_self() { return new self(); } public static function get_static() { return new static(); } } class B extends A {} echo get_class(B::get_self()); // A echo get_class(B::get_static()); // B echo get_class(A::get_static()); // A
http://www.jb51.net/article/54167.htm
self - 就是這個類,是代碼段裏面的這個類。
static - PHP 5.3加進來的只得是當前這個類,有點像$this的意思,從堆內存中提取出來,訪問的是當前實例化的那個類,那麼 static 表明的就是那個類。
static和$this很像,表明的是子類,可是又能夠應用於用靜態的方法
咱們可使用安正超的laravel-socialite package輕鬆實現第三方登陸,然而在使用過程當中,可能會因爲CA的配置出現如下錯誤:
「cURL error 60: SSL certificate problem: unable to get local issuer certificate」
解決方案:
到如下網址: http://curl.haxx.se/ca/cacert.pem 拷貝整個網頁內容,隨後paste爲一個文件 "cacert.pem", 更改php.ini文件中的
curl.cainfo = "[pathtothisfile]\cacert.pem"
最後從新啓動你的php,便可解決!
qq須要qq互聯開通應用; http://connect.qq.com/
微信須要 https://open.weixin.qq.com
在開發鑑權模塊時,當使用Auth::login($user),隨後dd(Auth::user())雖然能夠打印出登陸的用戶信息,可是從新到另一個page卻發現打印dd(Session::all())時,僅僅打印出_token的值,而且每次從新刷新都會變化。
這個困擾了我有好幾個小時,後來發現是dd()調用惹的禍,因爲dd會die(),而laravel使得session數據保存是在index.php文件的最後
$kernel->terminate($request, $response);來實現的,所以dd()調用時可能沒法執行到此,於是session數據每次執行時都會丟失!!更改成var_dump後就行了!
還有如下幾點值得注意:
1. 不要var_dump,若是在代碼中有var_dump,每次刷新頁面都會生成一個新的session文件!
2. 在routes.php中默認定義的都被包在'web' middleware中了(由RouteServiceProvider自動包含的!!),所以你不要在routes.php中再包一層'web' middleware了,或者你是一個偏執狂,你能夠這樣用:
Route::group(['middlewareGroups'=>'web'],function(){ Route::get('/', function () { return view('welcome'); }); });
對於api類型的route,你能夠這樣安排:
a).仍然在routes.php中,你使用'api'這個middle group;
b.)放到另一個文件中,好比叫作routes_api.php,而且在RouteServiceprovider中來引用!
具體請參考這個url: https://github.com/laravel/framework/issues/13000
因爲PHP是單繼承語言,不支持從多個基類中繼承,php爲了克服這個弱點,引入了trait的概念,使用trait咱們能夠不用使用傳統的繼承方式來繼承基類的方法,咱們可使用 use trait來直接引用到公共的方法。
一個PHP類能夠use多個trait,這些trait中有可能會有命名衝突,這時咱們能夠經過insteadof關鍵字來指明咱們使用哪一個:
trait AuthenticatesAndRegistersUsers { use AuthenticatesUsers, RegistersUsers { AuthenticatesUsers::redirectPath insteadof RegistersUsers; AuthenticatesUsers::getGuard insteadof RegistersUsers; } }
關於trait更加詳細的描述,能夠參考 http://www.cnblogs.com/CraryPrimitiveMan/p/4162738.html
Route group with namespace
有時咱們但願將一組路由做爲一個集合,這時能夠給這個組一個namespace,以避免每一個controller都輸入其namespace的麻煩:
Route::group(['prefix'=>'admin','namespace'=>'Admin'],function(){ Route::get('/', AdminController@index); })
在laravel中,若是咱們直接引用一個絕對url,這原本沒有什麼問題,可是若是後面該url作了更改,那麼就須要不少地方來作更改,一個可行的方案是使用named route:命名路由:
Route::get('session/create',['as'=>'register','uses'=>'SessionCotroller@index']);
這樣其餘地方使用link_to_route('register')則不管咱們怎麼改變上面的url,這個route都能連接到正確的url!
在laravel form操做中,常常須要對post數據格式化,其中get method下每每須要根據相關數據造成一個query string,這時能夠利用php自帶的http_build_query函數來實現:
<?php $data = array('foo'=>'bar', 'baz'=>'boom', 'cow'=>'milk', 'php'=>'hypertext processor'); echo http_build_query($data) . "\n"; echo http_build_query($data, '', '&'); ?> //輸出如下內容: foo=bar&baz=boom&cow=milk&php=hypertext+processor foo=bar&baz=boom&cow=milk&php=hypertext+processor
對於密碼字段咱們每每不但願明碼保存,在PHP中原生提供了對password加密及驗證的函數password_hash和password_verify.其中hash支持兩種默認的算法:PASSWORD_DEFAULT和
PASSWORD_BCRYPT
/** * 在這個案例裏,咱們爲 BCRYPT 增長 cost 到 12。 * 注意,咱們已經切換到了,將始終產生 60 個字符。 */ $options = [ 'cost' => 12, ]; echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
對應laravel中的facade及其函數是Hash::make(),Hash::check(),其底層使用了
github.com/ircmaxell/password_compat這個package
對於保護的路由,咱們有這樣的需求:一旦用戶登陸而且有權訪問這個路由的話,咱們但願直接redirect到先前的這個路由,這裏Redirect::intended()就是知足這個場景的。
if(Auth::attempt([ 'email' =>$input['email'], 'password' =>$input['password'] ]) return Redirect::intended('home');
上面的代碼中若是登陸成功,則重定向到先前的page,或者是home頁面
通常用於登陸未成功時返回前一頁面
當系統中發生了特定的事件,好比login成功時,咱們可能須要給用戶一個提示。實現方式:
1. 在controller中redirect的同時,調用with('flash_message', 'some information');
if(Auth::attempt([ 'email' =>$input['email'], 'password' =>$input['password'] ]) return Redirect::intended('home')->with('flash_message','You are logged in!~~'); 在模板中: @if (Session::get('flash_message')) <div class = "flash"> {{ Session::get('flash_message' }} </div> @endif
2. 在模板中經過@if Session::get('flash_message')來判斷是否有flash message,若是有則顯示出來
在laravel中,controller接收到form數據後,很是常見的一個使用方法就是
User::create(Input::all());這種模式雖然建立新user時很是方便,可是對於hacker來講,提供了一種很是便利地修改後臺數據的方法,好比在user create form中,除了username,password外,hacker可能會在客戶端增長一個hidden field: active,在用戶提交username,password的同時,將active也設置爲true,這時因爲咱們後臺代碼未做保護,則自動把該user就建立爲active的用戶了,而這,可能並非咱們想要的。解決方法: 1.要麼不要使用User::create(Input::all)調用模式,咱們使用$user = new User; $user->username = Input::get('username');$user->password=Input::get('password'),這樣雖然form中有active字段,可是咱們卻棄之不用這樣就達到了保護的目的;
2.上面的方法雖然可行,可是若是一個form表單數據很是多,一條一條地這樣調用也顯得麻煩,可是同時又要防止上面未經贊成的數據注入,可使用laravel model的$guarded和$fillable
class User extends Eloquent{ protected $guarded = ['active']; //指定哪些字段不能被mass assignment protected $fillable = ['username','password'] ; //指定哪些字段能夠mass assign }
對於密碼這類敏感信息,咱們不要使用明文保存在數據庫中,可是每次調用save時都顯式調用Hash::make($password)也是一個比較繁瑣的事情,好在laravel的eleoquent modle提供了一個這樣的feature: 若是在model類中有setPasswordAttribute($password)這樣的方法存在,則在save以前laravel自動調用這個方法來格式化$password,一樣地,若是有getPasswordAttribute()方法的話,則每次訪問一個字段時,都會調用getXXXXAtttribute()函數,其返回值做爲真正獲取到的值。
class User extends eloquent{ public setPasswordAttribute($password){ $this->attributes['password'] = Hash::make($password); } }
在定義路由時,咱們使用Route::resource('sessions','SessionsController')雖然是一個不錯的選擇,可是有時候咱們可能只對其中的兩三個方法感興趣,這時,咱們可使用only參數來明確指明只須要建立這幾個路由,其餘的都忽略掉:
Route::resource('sessions','SessonsController',['only'=>['create','store','destroy']);
參考Arrays on Steroids.mp4
User::all()將返回上述Collection對象,該對象又實現了多個interface: ArrayAccess, ArrayableInterface, Countable, IteratorAggregate, JsonableInterface,也就是說返回的對象支持多種操做方式
>>> $users = App\User::all(); //在這裏會執行數據庫操做 => Illuminate\Database\Eloquent\Collection {#690 all: [], } >>> count($users); => 0 >>> count($users); => 0 >>> count($users); => 0 >>> $users = App\User::all(); => Illuminate\Database\Eloquent\Collection {#701 all: [ App\User {#702 id: 6, name: "cnwedd", email: "dasd@ff.cc", created_at: null, updated_at: null, realname: "", imageurl: "", lastlogin: "2016-05-02 13:06:06", mobile: "", }, ], } >>> count($users); => 1 >>> $users->toJson(); => "[{"id":6,"name":"cnwedd","email":"dasd@ff.cc","created_at":null,"updated_at":null,"rea lname":"","imageurl":"","lastlogin":"2016-05-02 13:06:06","mobile":""}]" >>>
$users->first()->toArray(); = User::first()->toArray之間有什麼區別?
$users->last()->toArray(); = User::last()->toArray之間有什麼區別?
$users->all()則再也不訪問數據庫,由於User::all()已經返回了數據
App\User::first(['name']); => App\User {#704 name: "cnwedd", } //注意這個操做會執行數據庫查詢,而且只獲取name字段
咱們也可使用laravel的collection support helper類來使得任意數組變成一個方便操做的collection:
$myusers = new Illuminate\Support\Collection($myuserarray);
隨後$myusers->first()/last()/toArray()等等
注意FormBuilder從5.1開始已經再也不在框架core中出現,由https://laravelcollective.com/docs/5.2/html 來維護。
這其中包括: linke_to_route等Helpe函數
對於不常常變化而又頻繁訪問的數據,最好把這些數據cache起來,這樣每次訪問這些數據時,直接從cache中獲取而不用訪問數據庫,這會大大提升應用的性能。在laravel中起用cache很是簡單:
$users = User::remember(10,'users.all')->get(); //獲取users表中的全部數據,而且保存在cache中10分鐘,cache的key爲users.all //cache默認使用file方式,你也可使用memcache
若是你須要強制清除上面這個key的cache,則可使用:
Cache::forget('users.all'); //該代碼只清除'users.all'這個key的cache內容 Cache::flush();//該代碼將全部cache都清除掉=php artisan cache:clear
參考:6-Cache Tags and Memcached.mp4
Cookie保存在客戶的計算機上;Session保存在服務端;Cookie和Session都是對用戶的,而cache則是系統級別的;
Cookie::forever('key','value');
Cookie::get('key');
Session::put('foo','bar');
Session::get('foo');
Cache::put('favorites',[1,2], Carbon\Carbon::now()->addMinutes(60));
Cache::get('favorites');
在laravel中,upload文件時須要注意一點是在Form::open中須要指定'files'=>true
{{ Form::open(['route' =>'posts.store', 'files'=>true }}
<input type="file" name="thumbnail">
.... {{ Form::close() }}
注意上面的'files'=>true,FormBuilder會自動在form標籤中添加 enctype="multipart/form-data"屬性!
Input::file('thumbnail')將返回一個Symfony\Component\HttpFoundation\File\UploadedFile對象,你可使用該對象的move方法將上傳的文件轉存
public function store(){
$post = new Post;
$post->title = Input::get('title');
$post->body = Input::get('body');
if (Input::hasFile('thumbnail')){ $file = Input::file('thumbnail'); $file= $file->move(public_path().'/images/','myfile.img');//將上傳的文件重命名爲myfile.img而且存放到public/images目錄中
$post->thumbnail = $file->getRealPath();//返回絕對地址;或者使用getClientOriginalName返回文件名;
}
$post->save();
return "done";
}
https://laravel.com/docs/5.2/migrations#creating-columns
在項目開發中,不少時候須要建立一些dummy data,方便快速原型開發,這時可使用laravel的db seeder類,以及第三方faker庫
參考10-Seeds-and-Fakes.mp4.mp4
有時咱們不但願直接對數據庫執行永久刪除的操做,好比一個用戶咱們能夠deactivate,隨後當須要的時候咱們再把他找回來,這時候就須要使用softDelete的功能了。
很簡單隻要在對應的model中添加一個protected字段:
class User extends Eloquent{ protected $table = 'users'; portected $softDelete = true; //原理是:當執行User::delete(1)時,laravel只在數據庫的deleted_at字段更新了內容 //隨後在查詢數據庫User::all()時並不會返回任何內容, //除非使用User:withTrashed()->all()纔會返回 }
$user = User::withTrashed()->find(2);
$user->restore();
$user->posts()->restore();
在上面三行代碼例子中,咱們對$user和$user的posts都設置了softDelete
如何經過PHP代碼實現Cascade delete, update
雖然經過相似於下面的代碼,能夠借用mysql數據庫的聯動功能實現foreign key關聯的資源自動刪除,可是因爲不是每一種數據庫都有這種功能,故而咱們能夠經過php代碼來提供統一的方案:
Schema::table('class_student', function (Blueprint $table) { $table->foreign('class_id')->references('id')->on('classes') ->onUpdate('cascade')->onDelete('cascade'); $table->foreign('student_id')->references('id')->on('users') ->onUpdate('cascade')->onDelete('cascade'); $table->primary(['class_id','student_id']); });
參考: http://stackoverflow.com/questions/34363224/laravel-cascade-not-working
protected static function boot() { parent::boot(); static::deleting(function($ticket) { // delete related stuff ;) $reaction_ids = $ticket->reactions()->lists('id'); Reaction::whereIn($reaction_ids)->delete(); }); }
當咱們在edit一個資源時,咱們可能但願edit form自動Populate數據庫中的內容,這時只須要將
Form::open替換爲Form::model而且傳入對應的model便可,
好比{{ Form::model($user, ['method'=> 'PATCH', 'route'=> ['users.update', $user->id]]) }}
覆蓋默認relation所檢索的字段
laravel中存在着一套默認的命名規約,好比project和user之間的relation,project屬於一個用戶,
咱們在Project model中能夠建立一個owner方法,該方法返回該project所屬的用戶,默認狀況下,laravel會使用owner_id做爲外鍵,可是這確定不是咱們所要的,咱們能夠傳入第二個參數'user_id'便可解決這個問題:
class Project extends Eloquent{ protected $fillable = ['user_id','title','description'}; public function owner(){ return $this->belongsTo('User','user_id');//注意默認狀況下laravel會使用owner_id } }
laravel model很是強大易用,經過簡單的一兩行代碼咱們就能夠建立強大的關係結構,可是隨着應用複雜度增大,系統的性能可能快速降低,這時經過監察系統對數據庫查詢的頻率就能夠對優化有一些思路:
Event::listen('illuminate.query',function($sql){ var_dump($sql); //經過監聽illuminate.query事件,就能大概搞清楚系統的瓶頸,對於relation操做每每會有一個N+1 problem能夠優化 });
咱們經過with方法一次性地取出數據記錄同時取出對應的relation數據,則能夠大大優化數據庫查詢的次數:
$projects = Project::with('owner')->remember(10)->get();
上面的代碼只須要執行2次數據庫查詢,同時放到cache中10分鐘,這將大大提升系統的性能.
$query = Request::get('q'); $posts = Post::where('title','LIKE', "%$query%")->get(); //將返回title中包含$query字符串的post
使用laravel構建REST API是很是常見的應用,laravel也提供了一種構建這種應用路由框架的簡單方法: route:resource('resourcename','controllername');可是不少狀況下,咱們可能須要嵌入式資源,好比user, user.task,
具體使用方法以下:
Route::resource('users','Auth\AuthController'); Route::resource('users.tasks','TasksController');
上面兩行代碼將在laravel中造成如下標準的url路由:
| | POST | users | users.store | App\Http\Controllers\Auth\AuthController@store | web,guest | | | GET|HEAD | users | users.index | App\Http\Controllers\Auth\AuthController@index | web,guest | | | GET|HEAD | users/create | users.create | App\Http\Controllers\Auth\AuthController@create | web,guest | | | PUT|PATCH | users/{users} | users.update | App\Http\Controllers\Auth\AuthController@update | web,guest | | | GET|HEAD | users/{users} | users.show | App\Http\Controllers\Auth\AuthController@show | web,guest | | | DELETE | users/{users} | users.destroy | App\Http\Controllers\Auth\AuthController@destroy | web,guest | | | GET|HEAD | users/{users}/edit | users.edit | App\Http\Controllers\Auth\AuthController@edit | web,guest | | | POST | users/{users}/tasks | users.tasks.store | App\Http\Controllers\TasksController@store | web | | | GET|HEAD | users/{users}/tasks | users.tasks.index | App\Http\Controllers\TasksController@index | web | | | GET|HEAD | users/{users}/tasks/create | users.tasks.create | App\Http\Controllers\TasksController@create | web | | | DELETE | users/{users}/tasks/{tasks} | users.tasks.destroy | App\Http\Controllers\TasksController@destroy | web | | | PUT|PATCH | users/{users}/tasks/{tasks} | users.tasks.update | App\Http\Controllers\TasksController@update | web | | | GET|HEAD | users/{users}/tasks/{tasks} | users.tasks.show | App\Http\Controllers\TasksController@show | web | | | GET|HEAD | users/{users}/tasks/{tasks}/edit | users.tasks.edit | App\Http\Controllers\TasksController@edit | web |
除此以外,可能還不夠,好比咱們可能須要對結果過濾,這時能夠在對應url上添加query string來實現更加複雜的url結構GET /users/5/tasks?status=completed
高層代碼不要依賴於實體對象,而應依賴於abstractions;
高層代碼:不關心具體的細節;
底層代碼:關心具體的細節;
一個類不要被強制依賴於具體的底層實現細節;相反應該依賴於一個contract,或者說依賴於一個interface;
好比:你的房子有一個電源接口,全部能夠插電的設備,好比TV,電燈,空調等若是須要使用電源,就必須conform to(遵循)電源這個接口的規範,再看下面的代碼:
interface ConnectionInterface{ public function connect(); } class DbConnection implements ConnectionInterface{ } class PasswordReminder{ private $dbConnection; public function __construct(MySQLConnection $dbConnection){ $this->dbConnection = $dbConnection; //這裏應該更改成: public function __construct(ConnectionInterface $dbConnection){ $this->dbConnection = $dbConnection; } } } //須要問一下:爲何PasswordReminder須要關心是MySQLConnection,而不是SQLServer? //也就是說PasswordReminder不該該關心具體這個Connection是什麼
IoC container is a mini-framework for managing the composition of complex objects
當保存一個model時會有saving事件發生,對應的saving函數將被調用
Order::saving(function($model){ dd($modal); //Order model在保存時會調用這個函數 return false; // 若是返回false,則通知laravel不要保存這個model return $model->validate(); // 利用上面特性,咱們在model中定義一個validate方法,只有檢查經過才能保存! }
可是上面的代碼組織顯然不是很好,咱們能夠放到model的類定義中:
class Order extends Eloquent{ public static function boot(){ parent::boot(); //boot函數在Order建立時會被調用 static::saving(function($model){ return $model->validate(); }); } }
思路:使用before/after filter,以url爲key來作緩存,若是不存在則保存$response->getContent() (返回渲染好的html文件),如已存在則直接返回。 Caching Essentials.mp4
function do_something(){ //do some function throw new Exception('I take exception to that.'); } Route::get('/', function(){ try{ do_something(); } catch(Exception $e){ return $e->getMessage(); //獲取上面拋出異常時的消息 } });
在blade模版中,默認狀況下{{ $html }}將會把$html變量純文本展現,若是你但願 $html變量中的html代碼做爲頁面html的一部分,則須要使用
{!! $html !!} //將$html的內容原封不動(不作escape操做)地輸出到view中
php artisan make:model Models\\RBAC\\Role //上述命令將在app/Models/RBAC目錄下建立Role模型,牛的是:若是目錄不存在會自動建立!
Redis是一款提供in-memory快速緩存服務的軟件組件,另外一方面她也提供將內存中的數據永久化的手段:即寫到磁盤中,每次從新啓動後直接從磁盤中恢復。要用她:
1. 首先須要安裝這款軟件,windows上從https://github.com/MSOpenTech/redis/releases下載安裝,linux下直接從源代碼build安裝;
參考http://www.cnblogs.com/linjiqin/archive/2013/05/27/3101694.html windows下的安裝測試
2. 在config/cache.php中選擇默認的cache driver爲redis
'default' => env('CACHE_DRIVER', 'redis'),
3.你還須要經過composer安裝一個php package predis/predis
package (~1.0)(該package實際上就是redis client的php實現,至關於redis-cli中直接執行對應的redis 協議命令)
composer require predis/predis:~1.0
laravel關於redis的配置信息放在config/database.php中的redis section中,主要就是host,port,password參數,這些參數對於Redis::put/get操做時在初始化tcp鏈接時會使用;
同時laravel又統一作了封裝放在Cache這個facade中(從而能夠提供更高級的功能,好比指定cache timeout的時間爲10分鐘,Cache::put('foo','bar',10)),只要config/cache.php中的driver選擇爲redis(最好咱們仍是在.env文件中來定義):
CACHE_DRIVER=redis SESSION_DRIVER=redis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379
在linux下安裝 :
1. wget http://download.redis.io/releases/redis-3.0.7.tar.gz;
2. tar xvfz redis-3.0.7.tar.gz;
3. cd redis-3.0.7
4; make && make install;
5. cp redis.conf /etc/redis
6. 更改redis.conf中的daemonize yes
7. redis-server /etc/redis/redis.conf
8. 在/etc/init.d/目錄建立redis啓動腳本,代碼以下:
#!/bin/sh # chkconfig: 2345 90 10 # description: Redis is a persistent key-value database # redis Startup script for redis processes # processname: redis redis_path="/usr/local/bin/redis-server" redis_conf="/etc/redis/redis.conf" redis_pid="/var/run/redis.pid" # Source function library. . /etc/rc.d/init.d/functions [ -x $redis_path ] || exit 0 RETVAL=0 prog="redis" # Start daemons. start() { if [ -e $redis_pid -a ! -z $redis_pid ];then echo $prog" already running...." exit 1 fi echo -n $"Starting $prog " # Single instance for all caches $redis_path $redis_conf RETVAL=$? [ $RETVAL -eq 0 ] && { touch /var/lock/subsys/$prog success $"$prog" } echo return $RETVAL } # Stop daemons. stop() { echo -n $"Stopping $prog " killproc -d 10 $redis_path echo [ $RETVAL = 0 ] && rm -f $redis_pid /var/lock/subsys/$prog RETVAL=$? return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $prog RETVAL=$? ;; restart) stop start ;; condrestart) if test "x`pidof redis`" != x; then stop start fi ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart}" exit 1 esac exit $RETVAL
9.設置爲開機啓動:
chkconfig --add redis
chkconfig --level 2345 redis on
service restart redis
思路:laravel中在一個channel中publish一條消息,在Nodejs中subscribe這個channel,一旦laravel publish了這條消息,nodejs對應的subscribe會被執行,而且broadcast到全部的socket.io.js創建的connection
通常地,對於user資源,咱們都是使用id來訪問的,可是這種url是很是不友好的,咱們能夠簡單作一下修改,在laravel中就能實現以用戶名做爲url的參數,這樣看起來就方便多了,主要有幾個步驟:
1. 在路由中作wildcast , 好比Route::get(users/{username},'usersController@show'}
2. 在RouteServiceProvider的boot方法中註冊路由模型綁定:
$router->bind( 'username', function ( $username ) { return \App\User::where( 'name', $username )->firstOrFail( ); } );
經過上面第2.步,則laravel在接收到/users/yourname這條路由時,就會自動查找name爲yourname的User,而且返回這個model,在你的控制器中注入使用!
在項目開發中,有一個場景我須要使用動態變量名稱給一個data object增長屬性,隨後post到服務器端,這時務必使用obj[dynamicvariablename]這種格式纔會起做用:
postdata._token = token; postdata._method = method; postdata[this.fieldname] = inputdata; this.$http.post(this.url,postdata).then( 。。。 )
1. php aritisan make:request userLoginRequest;
2. 在該類中,定義$rules;
3. 在controller中type hint,laravel自動會在收到這個post請求時作validation;
思路:在父元素中嵌入一個專門用於mask的元素,絕對定位
http://demo.doyoe.com/css/auto-height/
elixir雖然使用方便,可是有一個很大的問題是:若是你但願分開多個task,分別經過gulp/gulp watch來執行的話,是沒有簡單的方法的。
我如今使用的workaround是:另外建立一個gulpfile,在這個gulpfile中定義構建任務對應要作的工做,隨後經過gulp --gulpfile yournewtaskfile來執行
在實際使用中發現,browserify打包只能處理簡單的語法詞法問題,好比缺乏一個' , '號,import不存在的文件這樣的問題,對於好比在components中定義了一個不存在的component類這類"runtime"的錯誤,它並不檢查,甚至好比咱們import了一個類,可是components數組引用中使用另一個不存在的類名,這類問題在browserify打包過程當中並不會發現,只有運行時在瀏覽器console終端中顯示出來。
laravel提供了將數據表row映射爲model的簡單強大的機制,咱們可使用model來訪問對應的數據集,一樣地,laravel eloquent也支持模型之間的relation.這些relation包含如下幾種:
one-to-one
one-to-many
在一對多這種模式中(好比user和task的關係:user hasMany(Task): tasksCreatedByMe, tasksOwnedByMe; Task belongsTo(User): createdBy, ownedBy
上述幾對關係中:
User model: tasksCreatedByMe .vs. Task model: createdBy
User model: tasksOwnedByMe .vs. Task model: ownedBy
都依賴於如下表結構: tasks表中必須分別有兩個外鍵owner和creator分別引用users表中的id字段。
表格的結構須要在Task表中有兩個字段: owner/creator
many-to-many
假設下面的場景: 須要查找返回一個user及其全部tasks,而且只能使用一次數據庫查詢,user和task之間有hasMany, belongsTo的relation:
$userinfo = User::with('tasks')->where('id','=',$user->id)->first(); //注意:first()返回的是User model, get()返回的是一個集合
有時,你但願一次性地獲取資源對應的relation,而且可能但願嵌套的relation也能獲取,好比,一本書Book屬於一個author,一個author又有對應的contacts信息,你但願一次性獲取一本書對應的做者以及做者對應的聯繫信息.能夠經過如下方法一次性搞定(使用dot語法!!!):
$books = App\Book::with('author.contacts')->get();
有時,你但願一次性地獲取資源對應的relation,而且可能但願嵌套的relation也能獲取,而且限制兩層relation對應須要選擇的字段(減小網絡傳輸,提升性能)好比,一本書Book屬於一個author,一個author又有對應的contacts信息,你但願一次性獲取一本書對應的做者(name,id字段)以及做者對應的聯繫信息(address,user_id字段).能夠經過如下方法一次性搞定(使用dot語法!!!):
$books = App\Book::with(['author'=>function($q){ $q->select(['id','name']); }, 'author.contacts'=>function($q){ $q->select(['address','user_id']; // 注意user_id字段是必選的哦,由於這是user和address表的外鍵! })->get();
再來一個例子:
$query->with([ 'child' => function ($q) { $q->with(['grandchild' => function ($q) { $q->where(‘someOtherCol’, ‘someOtherVal’); //constraint on grandchild }]) ->where(’someCol', ’someVal’)->select(['childcol1','childcol2']); //constraint on child } ]);
對於form表單的驗證時後端開發常見的技術要求,咱們能夠經過N種方案來實現這個要求,可是到底哪一種能夠做爲最佳實踐呢?隨着laravel5.0引入了custom form request的feature,這個功能專門就是完全解決這個問題的,能夠說是laravel後端表單認證的最佳實踐,而且你能夠方便地指定誰有相關權限執行這個操做,以及對應的validation rule。
同時你也能夠經過overwrite messages方法來自定義你但願展現的錯誤信息。
使用流程:
1. php artisan make:request YourCustomizedRequest;
2. 在該YourCustomizedRequest類中,定義authorize來指定對該request訪問的受權規則
public function authorize() { $commentId = $this->route('comment'); return Comment::where('id', $commentId) ->where('user_id', Auth::id())->exists(); } public function messages() { return [ 'title.required' => 'A title is required', 'body.required' => 'A message is required', ]; }
3.經過overwirte messages方法來自定義驗證錯誤信息;
4.經過TypeHinting YourCustomizedRequest這個Request來調用這個request中定義的authorize策略
public function store(Request $request) { $validator = Validator::make($request->all(), [ 'title' => 'bail|required|unique:posts|max:255', //注意:bail關鍵字表示只要第一個驗證失敗後面就不驗了,提升效率,具體rules能夠參考:
//https://www.laravel.com/docs/5.2/validation#available-validation-rules 'body' => 'required', ]); //在這裏咱們能夠經過after方法引入一個callback,作咱們本身的額外處理 $validator->after(function($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); if ($validator->fails()) { return redirect('post/create') ->withErrors($validator, 'login') //注意這裏login是可選的namespace參數, //這時,使用{{ $errors->login->first('email') }}的命名空間方式引用error bag ->withInput(); } // Store the blog post... }
使用Laravel自帶的控制器trait方法實現簡單表單數據驗證:
$this->validate($request, [ 'title' => 'required|unique:posts|max:255', 'author.name' => 'required', 'author.description' => 'required', ]);
authenticate: 就是告訴系統你是誰,這個每每是經過你登陸系統,laravel在session中保存User來實現的;
authorize: 就是系統告訴你你有作什麼事情的權限,好比你能夠查看敏感數據,能夠刪除修改部分資源
middleware: 就是一個http request到達你的業務邏輯(控制器方法)前必須作的必要檢查,只有這些middleware層層經過後,才能最後調用你的控制器方法邏輯;
實現一個應用受權謹慎讓應該有對應權限的人作對應權限的事是laravel應用的訴求。如何具體實現不是一個簡單的事情,須要認真思考。我梳理總結如下原則和策略:
1. 凡是通用的適用於大片entry point的監察邏輯都經過應用對應的middleware到路由集(Route::group)上去來實現:
好比,咱們全部/admin/xx的路由都必須有管理員權限的角色纔可以訪問,再好比只有vip登陸用戶才能訪問受保護的資源,最好的方法就是在受保護的路由集中使用middleware來控制
Route::group(['prefix'=>'admin','middleware'=> ['role:admin']],function(){ // 應用一個role:admin這個middleware到/admin/xxx的路由集上 /* admin/roles */ Route::get('/roles',['as'=>'adminroles','uses'=>'Admin\AdminRolesController@index']); 。。。 });
2.對於在一個控制器中的部分方法須要保護,好比若是你但願建立修改刪除一個task,那麼可能涉及到幾個action和對應的頁面: create, store, update,delete,可以訪問這些頁面的前提是你必須登陸後才能執行。那麼這時比較合適的方式多是在控制器構造函數中引入middleware:
public function __construct() { // 咱們在Controller構造函數中應用定製的mustloggedin middleware,指定只對create/store/update/delete作保護,其餘的action所有放行 $this->middleware('mustloggedin',['only'=>['create','store','update','delete']]); } // mustloggedin middleware定義: class MustLoggedIn { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->guest()) { if ($request->ajax() || $request->wantsJson()) { return response('Unauthorized.', 401); } else { return redirect()->guest('/')->withErrors('您必須登陸後才能訪問頁面: '.$request->fullUrl()); } } return $next($request); } }
3. 對於要求更具體的一些action,好比update一個task的內容,你可能要求必須是task owner纔能有權限編輯更改task的內容,這時,比較合適的方法就是使用custom request來實現。具體能夠參考上面custom form request的部分
middleware很好用,可是不少人可能不清楚如何向middleware傳入參數及使用方法:
1. 在路由或者控制器中引入帶參數的middleware(使用);
public function __construct() { $this->middleware('role:admin',['only'=>['create','store','update']]); }
2. 在kernel.php的對應routeMiddleware section中添加role middleware的聲明
protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, // entrust middleware 'role' => \Zizaco\Entrust\Middleware\EntrustRole::class, 'permission' => \Zizaco\Entrust\Middleware\EntrustPermission::class, 'ability' => \Zizaco\Entrust\Middleware\EntrustAbility::class, // my middleware 'mustloggedin' => \App\Http\Middleware\MustLoggedIn::class, ];
3.在role middleware的handle函數中,你傳入的參數就以第三個參數來訪問了(定義)
public function handle($request, Closure $next, $roles) {//這裏$roles就是在middle引用的時候傳入的參數 if ($this->auth->guest() || !$request->user()->hasRole(explode('|', $roles))) { abort(403,"您不具備 ".$roles." 的角色!"." 無權訪問 ".$request->fullUrl()); } return $next($request); }
policy提供了一種定義對一個action訪問控制的機制,policy class中的全部方法都對應着controller的對應action.
好比PostPolicy的一個update方法可能定義了誰有權限update一個post,而在PostController的update方法中,咱們使用if(Gate::denies('update',$post)){abort('403');}
Plain PHP views (.php) | Blade views (.blade.php) |
---|---|
Outputting data in PHP | Outputting data using Blade |
<?php echo(date('Y')); ?> |
{{ date('Y') }} |
Looping through data with PHP | Looping through data with Blade |
<?php foreach($users as $user){ ?> |
@foreach($users as $user) |
<p> |
<p> |
<?php echo($userelname); ?><br> |
{{ $userelname }}<br> |
<?php echo($usereladdress); ?> |
{{ $usereladdress }} |
</p> |
</p> |
<?php } ?> |
@endforeach |
Using conditional statements in PHP | Using conditional statements in Blade |
<?php if($category == 'blog') { ?> |
@if($category == 'blog') |
... |
... |
<?php } else { ?> |
@else |
... |
... |
<?php } ?> |
@endif |
對於受控資源的訪問控制,好比必須登錄以後才容許發表評論,這樣的場景咱們應該會司空見慣。可是若是已經登陸了系統,若是再次訪問/login路由的話,系統自動重定向到主頁,這個功能究竟是怎麼工做的呢?
laravel自帶了幾個middleware,其中一個就是guest middleware,它就是實現這個功能的。經過php artisan route:list咱們能夠列出對應的路由中包含了guest middleware的保護引用。
GET|HEAD | login | | App\Http\Controllers\Auth\AuthController@showLoginForm | web,guest //該guest middleware的定義爲: public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return redirect('/'); } return $next($request); }
咱們可能會用auth
這個多態關係是個什麼鬼?簡單說就是一個model能夠belongsTo多個其餘的model ,貌似比較傻,能夠有不少其餘的解決方案的
http://www.richardbagshaw.co.uk/laravel-user-types-and-polymorphic-relationships/
elixir是laravel前端構建工具集,它包裝了gulp,全部gulp task都由elixir來包裝管理。其配置文件在config.js中,能夠在gulpfile.js文件中經過elixir.config.*來overwrite,
默認的配置項及值有:
generic: assetsPath: 'resources/assets', publicPath: 'public', appPath: 'app', viewPath: 'resources/views', css: folder: 'css', outputFolder: 'css', autoprefix: { enabled: true, // https://www.npmjs.com/package/gulp-autoprefixer#api options: { browsers: ['last 2 versions'], cascade: false } } less: { folder: 'less'} js: folder: 'js', outputFolder: 'js', babel: { // https://www.npmjs.com/package/gulp-babel#babel-options options: { presets: ['es2015', 'react'] } }
使用laravel, 分頁pagination實在再簡單不過了,可是有時咱們須要同時支持search查詢的功能,這時分頁點擊時,咱們也仍是但願看到的是查詢內容裏面的分頁。很簡單:
public function apidata(Request $request) { $search = $request->query('title'); if($search){ // appends方法自動將query string加到pagination的url中 // search方法是scoped query,自動返回like $search string的結果集 // paginate則以分頁方式返回結果集 return Skill::whereLevel(1)->with(['cates.parents','children'])->search($search)->paginate(5)->appends($request->input()); }; }
當eager loading一個relation時,能夠方便地對結果集排序,好比Node model其children(是遞歸的)就能夠用updated_at來排序
class Node extend eloquent{ public function children($recursive = false){ return $this->child()->with('children')->orderBy('updated_at','DESC'); } }
return Classroom::with(['teachers'=>function($query){ $query->select('teacher_id', 'name','realname'); //teacher_id字段必選選中,不然不work }])->paginate(10);
若是一張表格很是大,你可能但願將表格根據時間分爲小表,好比orders_201501,orders_201506等等,可是但願使用一個Order model,底層的table則須要根據時間來選擇。
http://stackoverflow.com/questions/26757452/laravel-eloquent-accessing-properties-and-dynamic-table-names
$namelength = iconv_strlen($prefix)+4; $existusers = User::where('name','like','%' . $prefix . '%')->whereRaw('LENGTH(name) = ?', array($namelength))->orderBy('name','desc')->pluck('name')->toArray();
上面的例子檢索全部name字段長度爲$prefix+4,而且name包含$prefix字符串的記錄
參考如下: https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/
定製custom relation vs hydrate
laravel eloquent雖然很是強大基本上都可以知足全部數據庫關係,可是仍然有不少時候沒法簡單地經過一個belongsTo, belongsToMany, hasOne這種簡單的關係來實現,有兩個可能的思路:
1. 定製你本身的custom relationship方法,爲所欲爲知足你的定製化要求,可是須要對laravel model relation類有比較清晰的理解,典型引用案例就是eager loading with limits(由於你沒法對eager loading的relation限制條目!)
"
I needed a complex sql to resolve the relationship. What I ended up doing is extending the base class for relations and creating my own, right now in laravel 5.2 that is Illuminate\Database\Eloquent\Relations\Relation. It's quite self-explanatory implementing the abstract methods, but if you need any examples you can see the implementations of HasMany, BelongsTo, etc. all the relations that come with laravel out of the box.
"
https://laravel.io/forum/06-15-2015-custom-relationship-queries
https://github.com/johnnyfreeman/laravel-custom-relation
https://laracasts.com/discuss/channels/eloquent/create-a-custom-relationship-method
2. 直接使用raw sql來獲取到你的model,隨後hydrate到你的model中去,這個方法不支持eager loading
"
In another note, you can always create your custom queries or getting information wherever you want and create a collection of laravel models using the static ::hydrate() and ::hydrateRaw() methods in your model classes.
"
詳細的hydrate調用案例:
http://stackoverflow.com/questions/22312586/laravel-select-from-derived-table#answer-25069239
咱們在開發本身的受權系統時,每每會用到強大的middleware功能,在middleware中作相應的檢查看看用戶是否有對資源的訪問權限,這個資源自己每每由route+parameter來共同決定。
這些個信息能夠經過如下代碼來訪問:
public function handle($request, Closure $next) { $parameters = $request->route()->parameters(); $routename = $request->route()->getName(); return $next($request); }
https://scotch.io/tutorials/get-laravel-route-parameters-in-middleware
$title = Input::get('title'); // 這段代碼等同於下面 try { $title = $_GET['title']; // get query string title value xxxx ?title=xxxx }catch (\Exception $e){}
$posts = Post::leftJoin('comments', 'comments.post_id', '=', 'posts.id') ->select('posts.*', 'count(*) as commentsCount') ->groupBy('posts.id') ->get(); $posts = Post::leftJoin('comments', 'comments.post_id', '=', 'posts.id') ->select('posts.*', 'count(*) as commentsCount') ->groupBy('posts.id') ->get();
https://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/
構造函數,每個類在初始化時自動調用,你能夠執行依賴的注入,參數初始化等
當試圖給一個不能訪問的屬性來賦值時被自動調用,好比若是試圖執行$obj->privatePropery = $value這個代碼時,因爲privateProperty是私有的,所以沒法經過這種$obj->property來訪問,這時這個__set函數就將被執行,而且將$name的property賦值爲$valuclass xx {
private $privatepropery = ''; private $username; // 能夠被讀以及寫(經過__set) private $password; // 能夠被寫,可是不可讀(經過__get) private $age; private $accessibles = ['username']; private $fillables = ['username','password']; public function __get($name){ if(!in_array($name,$this->accesibles)){ // 若是訪問的屬性不在$accessibles數組定義的白名單中,則不容許訪問 return Null; } if(isset($name)){ return $this->$name; // 首先判斷該類中是否有這個屬性,有則賦值,沒有則忽略 } }
public function __set($name,$value){
if(!in_array($name,$this->fillables)){ // 若是訪問的屬性不在$fillable數組定義的白名單中,則不容許賦值 return false; } if(isset($name)){ $this->$name=$value; // 首先判斷該類中是否有這個屬性,有則賦值,沒有則忽略 } }
} $obj = new xx; $obj->username //能夠訪問 $obj->password // 返回null $obj->nonexist //返回null
當試圖訪問一個私有屬性時,則該magic方法被調用
當對象被cast to string時自動調用,好比echo $obj這時因爲$obj不是string,那麼就會自動調用$obj的__toString方法
http://php.net/manual/en/language.oop5.magic.php
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(),__sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() and __debugInfo()
is triggered when invoking inaccessible methods in an object context.
若是在對象調用的context中,被調用的方法不存在,則自動調用這個__call方法。 $obj->nonexist();
is triggered when invoking inaccessible methods in an object context.
若是在static環境中調用不存在的方法,則自動調用這個__call方法。 ClassName::nonexist();
<?php class MethodTest { public function __call($name, $arguments) { // Note: value of $name is case sensitive. echo "Calling object method '$name' " . implode(', ', $arguments). "\n"; } /** As of PHP 5.3.0 */ public static function __callStatic($name, $arguments) { // Note: value of $name is case sensitive. echo "Calling static method '$name' " . implode(', ', $arguments). "\n"; } } $obj = new MethodTest; $obj->runTest('in object context'); MethodTest::runTest('in static context'); // As of PHP 5.3.0 ?>
上面的__call和__callStatic在laravel中很是經常使用,好比facade都定義了__callStatic,以靜態方式寫代碼,可是實際上最終用這個__callStatic首先解析出obj而後調用到真正的實體對象的方法
class User{ public function login(){ var_dump('user logged in...'); } } class Administrator extends User{ public function login(){ parent::login(); //調用子類的login方法 var_dump('user logged in as administrator!'); } } (new Administrator)->login(); // "user logged in..." // "user logged in as administrator!"
在laravel應用中,session的默認爲120,一旦超過這個時間,服務端的session就將timeout被刪除,而這時若是有ajax請求就會莫名其妙地出錯,可是用戶卻可能不知情。最好是可以主動在後端判斷session是否timeout,若是已通過期則回覆對應的消息,提示用戶從新登陸:
http://stackoverflow.com/questions/14688853/check-for-session-timeout-in-laravel
if ((time() - Session::activity()) > (Config::get('session.lifetime') * 60)) { // Session expired }
能夠作成一個middleware,在每一個request處理時觸發
經過配置.env文件中的
SESSION_DRIVER=redis
咱們知道url中的#後面的內容是爲瀏覽器客戶端來使用的,永遠不會送往server端,那麼若是服務器端但願獲得這個信息,又該如何處理呢?一個可行的方案是在url中將#後面的內容轉換爲querystring,這樣後端就可以獲得這個信息加上fullurl()函數就能拼湊出整個url
在使用laravel env()函數來讀取.env文件中的配置信息過程當中,偶爾你會發現獲取不到。後來再經過執行php artisan config:cache命令來模擬問題出現場景,發現該命令執行後,在tinker中執行env就永遠沒法獲取。看看下面的laravel代碼Illuminate/Foundation/Bootstrap/DetectEnvironment.php line 18
public function bootstrap(Application $app) { if (! $app->configurationIsCached()) { $this->checkForSpecificEnvironmentFile($app); try { (new Dotenv($app->environmentPath(), $app->environmentFile()))->load(); } catch (InvalidPathException $e) { // } } }
上述這段代碼說明了若是存在緩存配置文件,就不會去設置環境變量了,配置都讀緩存配置文件,而不會再讀環境變量了。
所以,在配置文件即 app/config 目錄下的其餘地方,讀取配置不要使用 env 函數去讀環境變量,這樣你一旦執行 php artisan config:cache 以後,env 函數就不起做用了。全部要用到的環境變量,在 app/config 目錄的配置文件中經過 env 讀取,其餘地方要用到環境變量的都統一讀配置文件而不是使用 env 函數讀取。
不少時候,咱們會經過whereIn([1,5,8])這種方式來查找對應數據庫記錄,可是mysql會自動按照天然升序排列結果集,而這可能不是咱們想要的。
解決辦法是mysql的order by field來實現,對應laravel就是orderByRaw()
select * from `persons` where `id` in (1437, 1436, 1435, 1434, 1429) order by FIELD(id, 1437,1436,1435,1434,1429)
$implode(',',$idsarray); Person::whereIn('id',$idsarray)->orderByRaw(DB::raw("FIELD(id, ${idsorderstr})"))->get();
$arraydata = [1,5]; $idquery = join("','",$arraydata); $ids = \DB::select("select cf.id as pid from cates cf join cates cs on cf.id = cs.root where cs.id in ('$idquery') order by cf.id ");
return \DB::table(\DB::raw(" (select * from (select *,SUM(CASE isright WHEN 1 THEN 1 ELSE 0 END) AS right_count from useranswers where class_id = '$classid%' and user_id='$uid%' group by user_id) as mine union select * from (select *,SUM(CASE isright WHEN 1 THEN 1 ELSE 0 END) AS right_count from useranswers where class_id = '$classid%' group by user_id order by right_count desc limit 2) as top2) as answerrights "))->get();
有時候須要訪問post body的內容,雖然$request->get('keyname')絕大多數時間是能夠知足需求的,可是仍是有不少時候咱們但願可以徹底獲得post body的內容而且按照本身的方式來處理。方法是:
$bodyContent = $request->getContent(); $decodedBody = json_decode($bodyContent); // 下面就能夠直接對decodedBody對象操做了
建議不要使用nullable修飾,由於會影響性能而且佔用空間,最好使用laravel的默認NOT NULL
http://blog.csdn.net/dsislander/article/details/8573666
咱們知道在laravel中返回結果集時,只能對最外層的model作orderby操做,如何在eager loading一個關係relation時同時以該對該relation作排序呢?
答案是:
在定義關係函數時,直接在query builder中添加order by 字段
public function comments() { return $this->hasMany('Comment')->orderBy('column'); }
在某些場景下,當咱們返回model時但願自動加上一個額外的屬性,好比返回post model時但願自動加上其user信息,返回班級classroom時,自動返回其已經佈置的做業數目等,這時就須要使用laravel model的appends功能了:
class classroom extends Model{ public $table = 'classrooms'; protected $appends = ['homework_count']; /** * @return mixed accessor for homeworkCount attribute */ public function getHomeworkCountAttribute() { $homeworkscount = $this->homework()->count('homeworks.id'); return $homeworkscount; } }
在DB::table('xxx')->get()返回的對象數組中,若是數據量很大,字段數也比較多的話,通常狀況下字段長度都比較長,字段的value自己卻很短,所以,有效載荷是很小的,浪費傳輸帶寬下降網站響應速度。一個有效的優化方法就是直接去除這些對象數組中的key值傳輸,只傳value。具體方法以下:
$draftdata = DB::table(DB::raw("xxxSQL"))->get(['id','title','someotherfield']); $temp = array_map(function($v){ return array_values(get_object_vars($v)); },$draftdata); // 通過上面的array_values(只返回array的value而忽略對應的key)和get_object_vars操做(將對象修改成array)變換後就返回了已經簡化的數據 // [ [1,'這是title','第三個字段'],[2,'這是第二個title','第三個字段的value']] return ($temp);
下面是validator的擴展代碼,這樣就能夠在controller中使用captcha這個rule了,其底層邏輯由captcha_check來實現
// Validator extensions $this->app['validator']->extend('captcha', function($attribute, $value, $parameters) { return captcha_check($value); });
因爲laravel startsession middleware自己默認狀況下只有在順利經過了route檢查後才能啓動,這樣就會致使在Http not found exception中沒法訪問到session,若是你確實須要在任何狀況下都能訪問session,應該怎麼辦?方法很簡單,將Session\Middleware\StartSession::class,這個middleware放到kernel.php的$middleware定義中
protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Session\Middleware\StartSession::class, ];
有的時候咱們只但願更新composer.json中的某一個vendor的package,而其餘的package都保持不變,有什麼辦法呢?
1. 首先在composer.json中增長你但願安裝的package以及版本要求;
2. 執行composer update vendor/package的命令就行了!
$notification->setHidden(['segmentednotification']);
carbon自己是一個很是好用的PHP date package,可是有的時候咱們還但願用到php原生的函數功能,好比format成微信開發須要的20180120112305的字符串格式
$n = new \Carbon\Carbon(); $ts = $n->getTimestamp(); return date('YmdHis',$ts); // 20180104161547 $u = User::orderBy('created_at','desc')->first(); $ts = $u->created_at; // 被laravel自動cast爲carbon object return date('YmdHis',$ts->getTimestamp()); // 20180104161547
public function MakeSign() { //簽名步驟一:按字典序排序參數 ksort($this->values); $string = $this->ToUrlParams(); //簽名步驟二:在string後加入KEY $string = $string . "&key=".WxPayConfig::KEY; //簽名步驟三:MD5加密 $string = md5($string); //簽名步驟四:全部字符轉爲大寫 $result = strtoupper($string); return $result; } public function SetSign() { $sign = $this->MakeSign(); $this->values['sign'] = $sign; return $sign; } public static function getNonceStr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str =""; for ( $i = 0; $i < $length; $i++ ) { $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } public function ToXml() { if(!is_array($this->values) || count($this->values) <= 0) { throw new WxPayException("數組數據異常!"); } $xml = "<xml>"; foreach ($this->values as $key=>$val) { if (is_numeric($val)){ $xml.="<".$key.">".$val."</".$key.">"; }else{ $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * 將xml轉爲array * @param string $xml * @throws WxPayException */ public function FromXml($xml) { if(!$xml){ throw new WxPayException("xml數據異常!"); } //將XML轉爲array //禁止引用外部xml實體 libxml_disable_entity_loader(true); $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $this->values; }
<?php function foobar($arg, $arg2) { echo __FUNCTION__, " got $arg and $arg2\n"; } class foo { function bar($arg, $arg2) { echo __METHOD__, " got $arg and $arg2\n"; } } // Call the foobar() function with 2 arguments call_user_func_array("foobar", array("one", "two")); // Call the $foo->bar() method with 2 arguments $foo = new foo; call_user_func_array(array($foo, "bar"), array("three", "four")); ?>
http://www.cnblogs.com/lyzg/p/6159617.html
<script> var user = {!! json_encode(userobj()) !!} // userobj是laravel拉取數據庫造成的PHP對象或數組 </script>
上面的代碼將使得PHP的輸出不通過htmlentities這個php函數來過濾轉換html相關的內容,直接原封不動的輸出給js
有時候存在這樣的場景:咱們在前一個request處理時使用withErrors('error information')在laravel session store中flash了一個errors,原本在下一個request中咱們是能夠訪問到這個error的,可是可能在middleware中又強制作了一個redirect,而這時是再也不帶errors的,這時因爲laravel的session store機制會將flash.old清空,所以在最終的request處理函數中就沒法獲取到該error,怎麼辦?一個可行的策略是:
$request->session()->reflash(); // 將flash data保留到下一次訪問
$ composer create-project --prefer-dist laravel/laravel lara56 Installing laravel/laravel (v5.4.30) - Installing laravel/laravel (v5.4.30): Downloading (connecting...) Could not fetch https://api.github.com/repos/laravel/laravel/zipball/098b8a48830c0e4e6ba6540979bf2459c8a6a49e, please create a GitHub OAuth token to go over the API rate limit Head to https://github.com/settings/tokens/new?scopes=repo&description=Composer+on+USER-20151001BU+2018-03-01+1407 to retrieve a token. It will be stored in "C:/Users/Administrator/AppData/Roaming/Composer/auth.json" for future use by Composer. Token (hidden): No token given, aborting. You can also add it manually later by using "composer config --global --auth github-oauth.github.com <token>" Failed to download laravel/laravel from dist: Could not authenticate against github.com Now trying to download from source - Installing laravel/laravel (v5.4.30):
解決辦法:
1. 經過:https://github.com/settings/tokens建立一個token
2. composer config --global github-oauth.github.com 23bc35a888235f66032ef68c7a8023b7b28e0f6
1. 在packagist中查找對應framework的版本: https://packagist.org/packages/laravel/framework
2. composer create-project laravel/laravel lara560 --prefer-dist 5.6.0
可是要注意的是laravel/laravel可能和laravel/framework的版本並不會很快同步的哦, 好比laravel/framekwork在v5.6.7版本時,而laravel/laravel卻可能只在v5.6.0上
解決辦法, 在webpack.mix.js中,使用如下代碼,以後再調用你本身的mix.js.sass等任務
mix.webpackConfig({ devtool: "inline-source-map" });
php artisan config:cache
1. 在config目錄下增長一個yourcfg.php
2.該php文件中
return [ 'myconfigkey1' => env('MYCONFIGKEY1',DEFAULTVALUE ]
3. 執行
php artisan config:cache
上面這條命令將使得全部config cache起來,這時你若是修改你的env文件你會發現並不會當即生效,那麼要麼再次執行上述config:cache命令,要麼執行如下命令:
php artisan config:clear以便清除掉config的cache內容,可是這樣作會有個性能的損失"
public function category() { return $this->belongsToMany(Category::class)->wherePivotIn('id', [1]); }
https://heera.it/laravel-model-relationship
和npm相似,國外網站從國內訪問不少不穩定,速度慢。感謝又拍雲,感謝活雷鋒。免費爲中國開發者搭建並運營着PHP composer的國內鏡像,使用方法以下:
composer config -g repo.packagist composer https://packagist.phpcomposer.com
今後網絡速度會大大提升。