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
今后网络速度会大大提升。